Content Negotiation with ASP.NET Core REST APIs – Part 1

Content Negotiation is a powerful and core part of HTTP, but vastly under-utilized in ASP.NET Web APIs.

This series aims to:

  1. Convince you why it is useful
  2. Grumble that it is difficult to use in ASP.NET
  3. Show what ASP.NET provides
  4. Show a better way via codecs

What is HTTP Content Negotiation?

Congrats! You just used Content Negotiation. Browsers use it in HTTP requests for HTML, images, videos, etc. It lets the browser and the server converge on a data format that both sides support.

  1. The browser sends a list of formats that it understands.
  2. The server compares that list with what it can return and picks the best option.
  3. The server returns a response, and indicates which format it chose

I recommend this MDN article on content negotiation. It should prove useful as we dig deeper. Please note the term “representations”, which we’ll discuss shortly.

Useful for APIs

ASP.NET supports a coarse-grained negotiation of XML vs JSON out of the box. This is good, but Content Negotiation offers many more benefits when fully utilized:

  • Versioning, Evolution, and Backwards-Compatibility
  • Client / Server deployment decoupling
  • Clean and semantically meaningful endpoints

Illustrated via Donuts

Let’s go get some donuts to illustrate the point. Of these three shops… which would you rather visit?

Shop #1: Donut Dictator

The sign on the door reads:

We know best! We pick the flavors. You get what you get.
We are busy! Don’t ask which is which. You’ll figure it out after you take a bite.

– Donut Dictator

Many APIs work like this shop. The server dictates what is returned. The client has no say in the matter.

The responses from these APIs often lack metadata to clarify what’s in the body. This makes it hard for clients to decode (deserialize) the content. They either peek inside to detect the flavor, or they throw bits blindly into a deserializer and hope for the best.

Shop #2: Binary Donuts

The sign on the door reads:

Simplicity is our goal. No talking necessary.
Separate lines for each type of donut.
You indicate how many. If we’re out, we simply shake our heads no.

– Donut Machines

Some APIs increase the number of endpoints, with each endpoint supporting a different flavor. This is sometimes used to support multiple versions as the API evolves. While functional, this is not ideal. More on this later.

Shop #3: Donut Discussions

The sign on the door reads:

Welcome. We love to discuss donuts!
Tell us your preferences, and we’ll do our best to match it.
You’ll know exactly what you get because we label each donut in your order.

– Bakery Team

I prefer interactions like Shop #3. The interactions are natural, efficient, and most likely to give me what I want. The rest of this series embraces this style.

Representations

A “representation” is key to “ReST”. It is part of the name “Representational State Transfer“!

A representation is an encoding of a thing. There can be multiple encodings of a given thing. For example, a contact can be encoded as a VCARD, XML, JSON, or a JPEG image.

HTTP uses the MediaType specification (RFC 2045 and RFC 2046) to identify what is in the body of a request or response. The MediaType is an excellent mechanism to qualify and specify which representation is used.

MediaTypes can be much more specific than just “application/json”. MediaTypes support key-value parameters to further specify details. For a ReST API, these could express things like the version (vcard 3 vs vcard 4) or the domain (contact vs order vs payment). Here are a few example media types for a contact resource:

application/json; domain=alner.contact; version=2

application/xml; domain=alner.contact; version=2

text/vcard; version=3

avro/binary; domain=alner.contact; version=1

I have successfully used these service-specific media types across many services. The pattern is robust and adds much value. The ability to express the version of a representation is a highly useful tool in a versioning strategy. Expressing the domain of a representation is key to properly decoding responses. Without such information, the system needs to embed this metadata inside the representation, or act on faith that the data matches expectations. Neither is ideal.

What’s Next (Part 2)

In part 2, we’ll explore how ASP.NET implements content negotiation. We’ll show how to customize the behavior and get full control.

This post is part of a series:

  1. What is Content Negotiation (this post)
  2. ASP.NET Core Support for Content Negotation
  3. Codecs and Transcoders (coming soon)
  4. Using Codecs in Clients (coming soon)
  5. Using Codecs for Data Persistence (coming soon)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at WordPress.com

Up ↑

%d bloggers like this: