Isaac.

web

Content Negotiation in APIs

Implement flexible content negotiation for API clients.

By Emem IsaacJune 16, 20232 min read
#content negotiation#api design#formats#accept headers
Share:

A Simple Analogy

Content negotiation is like a restaurant menu. Customer requests their preferred format, server delivers it.


Why Content Negotiation?

  • Flexibility: Support multiple formats
  • Compatibility: Old and new clients
  • Performance: Compress when requested
  • Standards: Follow REST principles
  • Future-proof: Add formats without breaking

Accept Header

GET /api/products HTTP/1.1
Accept: application/json

GET /api/products HTTP/1.1
Accept: application/xml

GET /api/products HTTP/1.1
Accept: application/json, application/xml;q=0.9

The q parameter indicates preference (0-1).


ASP.NET Core Implementation

// Startup
services.AddControllers()
    .AddXmlSerializerFormatters()
    .AddXmlDataContractSerializerFormatters();

// Endpoint respects Accept header automatically
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
    var product = new Product { Id = id, Name = "Widget" };
    return Ok(product);
}

// Client requests:
// Accept: application/json → JSON response
// Accept: application/xml → XML response

Custom Formatters

public class CsvOutputFormatter : TextOutputFormatter
{
    public CsvOutputFormatter()
    {
        SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/csv"));
        SupportedEncodings.Add(Encoding.UTF8);
    }
    
    public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
    {
        var response = context.HttpContext.Response;
        var items = context.Object as List<Product>;
        
        var csv = new StringBuilder();
        csv.AppendLine("Id,Name,Price");
        
        foreach (var item in items)
        {
            csv.AppendLine($"{item.Id},{item.Name},{item.Price}");
        }
        
        await response.WriteAsync(csv.ToString(), selectedEncoding);
    }
}

// Register
services.AddControllers(options =>
    options.OutputFormatters.Add(new CsvOutputFormatter()));

Quality Values

public static ContentNegotiator GetNegotiator()
{
    var formatters = new MediaTypeFormatterCollection
    {
        new JsonMediaTypeFormatter(),
        new XmlMediaTypeFormatter(),
        new FormUrlEncodedMediaTypeFormatter()
    };
    
    return new DefaultContentNegotiator(includeMatchOnTypeLevel: true);
}

Best Practices

  1. Default format: Usually JSON
  2. Explicit: Prefer Accept header over query params
  3. Clear: Document supported formats
  4. Error messages: Format errors per request
  5. Caching: Include format in cache key

Related Concepts

  • Content-Type header
  • Media types
  • REST principles
  • API versioning

Summary

Implement content negotiation to support multiple response formats based on Accept headers, using custom formatters as needed.

Share:

Written by Emem Isaac

Expert Software Engineer with 15+ years of experience building scalable enterprise applications. Specialized in ASP.NET Core, Azure, Docker, and modern web development. Passionate about sharing knowledge and helping developers grow.

Ready to Build Something Amazing?

Let's discuss your project and explore how my expertise can help you achieve your goals. Free consultation available.

💼 Trusted by 50+ companies worldwide | ⚡ Average response time: 24 hours