// Client request: Accept: application/xml
[HttpGet("{id}")]
public ActionResult<Product> GetProduct(int id) => Ok(_service.GetById(id));
// Returns XML instead of the default JSON if an XML formatter is registered and requested
Topics
36
API Documentation with Swagger/OpenAPI
API Versioning
Authentication
Authorization
Background Tasks & Hosted Services
Blazor Integration with ASP.NET Core
Caching
Configuration & Options Pattern
Content Negotiation & Output Formatters
Controllers vs Minimal APIs
CORS (Cross-Origin Resource Sharing)
Dependency Injection
Endpoint Metadata, Route Constraints & Templates
Error Handling
File Uploads & Streaming Large Files
Filters
gRPC Services
Health Checks
Hosting Models: Kestrel, IIS & Reverse Proxies
HTTPS, Certificates & Transport Security
Localization & Globalization
Logging
Model Binding & Validation
MVC Views, Razor Syntax & Tag Helpers
Output Caching & Response Caching
Rate Limiting
Razor Pages
Request Pipeline & Middleware
Response Compression & Caching Headers
Routing
Security Headers, Antiforgery & CSRF Protection
Sessions, Cookies & TempData
SignalR & Real-Time Communication
Static Files, wwwroot & Content Delivery
Testing ASP.NET Core Applications
WebSockets
Content Negotiation & Output Formatters
15 questions found
Content negotiation is the process by which the server determines the format (JSON, XML, plain text) to return a response in, based on the client's Accept header expressing its preferences -- ASP.NET Core's [ApiController] defaults to JSON output but can serve alternate formats if the corresponding output formatter is registered and the client requests it via Accept.
Real-world example
A legacy enterprise client expecting XML responses and a modern SPA expecting JSON both call the exact same endpoint, each receiving their preferred format automatically based on their respective Accept headers.
RESTful Web APIs & Controllers;API Documentation with Swagger/OpenAPI
How do you add XML output formatting support to an ASP.NET Core API alongside the default JSON formatter?
IntermediateAddXmlSerializerFormatters() (or AddXmlDataContractSerializerFormatters() for an alternative serialization approach) registers XML input/output formatters alongside the default JSON ones, letting the same controllers serve both formats automatically based on content negotiation without any per-action code changes.
builder.Services.AddControllers()
.AddXmlSerializerFormatters();
// Now both of these work automatically:
// GET /api/products/1 Accept: application/json -> JSON response
// GET /api/products/1 Accept: application/xml -> XML response
Real-world example
A B2B integration platform serving both modern partners (JSON) and legacy enterprise systems (XML) adds XML formatters once at startup, letting every existing controller serve both formats without touching individual action methods.
RESTful Web APIs & Controllers;Testing ASP.NET Core Applications
How would you write a custom output formatter to support a non-standard format, like CSV, for specific endpoints?
AdvancedSubclass TextOutputFormatter (or OutputFormatter for binary formats), override CanWriteType to declare which types it supports, set SupportedMediaTypes to the custom MIME type (like text/csv), and override WriteResponseBodyAsync to implement the actual serialization logic -- registered via options.OutputFormatters.Add(new CsvOutputFormatter()) in AddControllers, letting clients request text/csv via Accept to receive CSV instead of JSON from the same endpoint.
public class CsvOutputFormatter : TextOutputFormatter {
public CsvOutputFormatter() {
SupportedMediaTypes.Add("text/csv");
SupportedEncodings.Add(Encoding.UTF8);
}
protected override bool CanWriteType(Type type) => typeof(IEnumerable<Product>).IsAssignableFrom(type);
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding encoding) {
var products = (IEnumerable<Product>)context.Object;
var csv = string.Join("\n", products.Select(p => $"{p.Id},{p.Name},{p.Price}"));
await context.HttpContext.Response.WriteAsync(csv, encoding);
}
}
Real-world example
A reporting API lets analysts request the exact same /api/sales-report endpoint with Accept: text/csv to download data directly importable into Excel, without needing a separate dedicated CSV export endpoint.
API Documentation with Swagger/OpenAPI;File Uploads & Streaming Large Files
How does ASP.NET Core decide which output format to use when a client's Accept header lists multiple acceptable types with quality values (q-values)?
IntermediateThe framework parses the Accept header's comma-separated list of media types, each optionally annotated with a q-value (like application/json;q=0.9,application/xml;q=0.5) indicating relative preference, and selects the highest-priority type for which a matching output formatter is actually registered -- if no requested type has a matching formatter, behavior depends on configuration (either falling back to a default formatter or returning 406 Not Acceptable).
// Accept: application/xml;q=0.9, application/json;q=0.8
// If both XML and JSON formatters are registered, XML wins due to higher q-value
// Accept: application/pdf
// If no PDF formatter is registered: either falls back to default, or returns 406
Real-world example
A client library correctly specifies application/json;q=1.0, application/xml;q=0.5 to express a strong preference for JSON while still accepting XML as an acceptable fallback if JSON somehow weren't available.
RESTful Web APIs & Controllers;Error Handling
Why is RespectBrowserAcceptHeader disabled by default in ASP.NET Core, and what problem does this default avoid?
AdvancedBrowsers typically send an Accept header requesting HTML first (text/html) since that's their primary use case for regular web browsing, even when a developer manually navigates to an API URL expecting JSON -- with RespectBrowserAcceptHeader left at its default false, ASP.NET Core deliberately ignores this browser-supplied preference for API responses and returns the default format (JSON) instead, avoiding the confusing behavior of an API returning an unexpected format just because it was accessed via a browser address bar.
// Browser navigating directly to /api/products sends:
// Accept: text/html, application/xhtml+xml, ...
// With RespectBrowserAcceptHeader = false (default):
// API still returns JSON, ignoring the browser's HTML preference
// With RespectBrowserAcceptHeader = true:
// API might return 406 or attempt HTML formatting, which is rarely desired for a pure API
Real-world example
A developer debugging an API by pasting its URL directly into a browser address bar still sees a properly formatted JSON response instead of a confusing 406 error or garbled HTML attempt, thanks to this deliberate default.
RESTful Web APIs & Controllers;API Documentation with Swagger/OpenAPI
How does System.Text.Json's configuration (via AddJsonOptions) let you customize the default JSON serialization behavior globally?
IntermediateAddJsonOptions lets you configure JsonSerializerOptions applied to every JSON response and request across the API -- common customizations include property naming policy (camelCase vs PascalCase), handling of null values, enum serialization as strings instead of numbers, and reference handling for circular object graphs.
builder.Services.AddControllers()
.AddJsonOptions(options => {
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});
Real-world example
An API configures global JSON options to serialize enums as readable strings ("Active" instead of 1) and omit null fields entirely, producing cleaner, more JavaScript-idiomatic responses for its frontend consumers.
RESTful Web APIs & Controllers;Configuration & Options Pattern
How would you support versioned response shapes for the same resource using content negotiation via custom media types instead of URL-based versioning?
AdvancedMedia type versioning defines custom vendor-specific media types (like application/vnd.myapp.v2+json) that clients specify in their Accept header, with a custom formatter or action constraint selecting the correct response shape based on the requested version embedded in the media type itself -- an alternative to URL-segment versioning that keeps URLs stable while still allowing multiple concurrent response contract versions.
[HttpGet("{id}")]
[Produces("application/vnd.myapp.v1+json", "application/vnd.myapp.v2+json")]
public IActionResult GetProduct(int id) {
var acceptHeader = Request.Headers.Accept.ToString();
return acceptHeader.Contains("v2")
? Ok(MapToV2(product))
: Ok(MapToV1(product));
}
Real-world example
A REST purist API uses media-type versioning (application/vnd.api.v2+json) specifically to keep its resource URLs completely stable and semantically 'permanent' across contract versions, following stricter REST/HATEOAS principles than typical URL-segment versioning allows.
API Versioning;API Documentation with Swagger/OpenAPI
What is the [Produces] attribute, and how does it restrict which content types a specific action can return regardless of what the client's Accept header requests?
Intermediate[Produces("application/json")] applied to a controller or action explicitly restricts the response to only the listed content type(s), overriding normal content negotiation entirely for that scope -- useful when an endpoint should always return a specific format (like a webhook receiver that always expects JSON) regardless of what a misconfigured or unusual client's Accept header might otherwise request.
[Produces("application/json")]
[HttpPost("webhook")]
public IActionResult ReceiveWebhook(WebhookPayload payload) {
// Always returns JSON, even if the caller's Accept header requested XML
return Ok(new { received = true });
}
Real-world example
A webhook receiver endpoint forces JSON output via [Produces] specifically because the calling service (a third-party payment provider) doesn't correctly negotiate content types, and JSON must always be returned regardless of its actual Accept header.
RESTful Web APIs & Controllers;Error Handling
How does content negotiation handle the request body (input formatters) differently from the response body (output formatters)?
AdvancedInput formatters (like the built-in JSON input formatter) deserialize the incoming request body based on its Content-Type header (not Accept, which governs the response), converting raw bytes into the strongly-typed parameter your action method expects -- a mismatch between the actual Content-Type header and the request body's real format (or an unsupported Content-Type with no matching input formatter) results in a 415 Unsupported Media Type response before your action code even runs.
// Client sends Content-Type: application/json with a JSON body -- input formatter deserializes it
[HttpPost]
public IActionResult CreateProduct([FromBody] ProductDto dto) { ... }
// Client sends Content-Type: application/xml but no XML input formatter registered:
// -> 415 Unsupported Media Type, action never executes
Real-world example
A client accidentally sending Content-Type: text/plain with a JSON-formatted body receives a 415 Unsupported Media Type response rather than a confusing model binding failure, since no input formatter registered for text/plain can even attempt to deserialize the request.
Model Binding & Validation;Error Handling
How does minimal API content negotiation differ from the [ApiController]-based MVC approach, given minimal APIs return results via IResult?
IntermediateMinimal APIs primarily default to JSON via Results.Json() / implicit JSON serialization for returned objects, with less built-in support for automatic multi-format content negotiation compared to MVC's formatter pipeline -- supporting additional formats in minimal APIs typically requires more explicit, manual logic (checking the Accept header yourself and choosing a response type accordingly) rather than the declarative, pluggable output formatter system MVC controllers benefit from automatically.
app.MapGet("/products/{id}", (int id, HttpContext context) => {
var product = GetProduct(id);
if (context.Request.Headers.Accept.ToString().Contains("xml")) {
return Results.Text(SerializeToXml(product), "application/xml");
}
return Results.Json(product);
});
Real-world example
A minimal-API-based service supporting a rare XML-consuming legacy client implements manual Accept header inspection for that one specific endpoint, since minimal APIs lack MVC's automatic formatter-based content negotiation pipeline out of the box.
Controllers vs Minimal APIs;RESTful Web APIs & Controllers
Showing 1–10 of 15