ASP.NET Core Web API does not force clients to consume JSON. By relying on Content Negotiation, your API can dynamically look at an incoming HTTP request, see what format the client prefers, and automatically format the response data into JSON, XML, or even custom binary formats without altering your Controller logic.
Content negotiation triggers when the client sends an Accept HTTP Header with their request.
Accept: application/json (The web industry default)Accept: application/xml (Used heavily in legacy enterprise systems/SOAP integrations)By default, the ASP.NET Core Web API template only ships with the JSON Formatter enabled. If a client requests XML, the pipeline simply ignores the request and forces JSON anyway. You must explicitly configure the MVC pipeline to respect XML demands.
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
// If a client requests a format we DON'T support (e.g. application/pdf),
// ASP.NET Core will return JSON anyway. True REST APIs should instead
// reject the request by returning a 406 Not Acceptable status code.
options.ReturnHttpNotAcceptable = true;
})
// This single line enables full bi-directional XML support
.AddXmlDataContractSerializerFormatters();
While global content negotiation is powerful, there are times when an endpoint generates a file specifically designed for one format (e.g., generating an XML Sitemap for Google SEO). You can override negotiation using the [Produces] attribute.
[ApiController]
[Route("api/sitemap")]
public class SeoController : ControllerBase
{
[HttpGet]
[Produces("application/xml")] // Forces the output to be XML, skipping negotiation
public IActionResult GenerateSitemap()
{
var urls = _repo.GetSitemapUrls();
return Ok(urls);
}
}
Q: "The client sends `Accept: application/xml`, but our API is still returning JSON. Furthermore, we enabled `AddXmlDataContractSerializerFormatters()`. Why is the format still JSON?"
Architect Answer: "The issue almost always lies in the payload itself. If you return an anonymous internal C# object (e.g., `return Ok(new { msg = 'Success' })`), the JSON serializer handles it beautifully because JSON has no rigid structural schema rules. However, XML is strongly typed. The XML Serializer physically cannot serialize an anonymous class because it doesn't know what to name the root XML Node. Whenever XML serialization fails silently, ASP.NET Core gracefully falls back down the chain to the default JSON formatter. To fix it, you MUST return a concrete named class (e.g., a DTO) instead of an anonymous type."