"
 
 
 
ASP.NET (snapshot 2017) Microsoft documentation and samples

Content Negotiation in ASP.NET Web API

by Mike Wasson

This article describes how ASP.NET Web API implements content negotiation.

The HTTP specification (RFC 2616) defines content negotiation as “the process of selecting the best representation for a given response when there are multiple representations available.” The primary mechanism for content negotiation in HTTP are these request headers:

The server can also look at other portions of the HTTP request. For example, if the request contains an X-Requested-With header, indicating an AJAX request, the server might default to JSON if there is no Accept header.

In this article, we’ll look at how Web API uses the Accept and Accept-Charset headers. (At this time, there is no built-in support for Accept-Encoding or Accept-Language.)

Serialization

If a Web API controller returns a resource as CLR type, the pipeline serializes the return value and writes it into the HTTP response body.

For example, consider the following controller action:

[!code-csharpMain]

   1:  public Product GetProduct(int id)
   2:  {
   3:      var item = _products.FirstOrDefault(p => p.ID == id);
   4:      if (item == null)
   5:      {
   6:          throw new HttpResponseException(HttpStatusCode.NotFound);
   7:      }
   8:      return item; 
   9:  }

A client might send this HTTP request:

[!code-consoleMain]

   1:  GET http://localhost.:21069/api/products/1 HTTP/1.1
   2:  Host: localhost.:21069
   3:  Accept: application/json, text/javascript, */*; q=0.01

In response, the server might send:

[!code-consoleMain]

   1:  HTTP/1.1 200 OK
   2:  Content-Type: application/json; charset=utf-8
   3:  Content-Length: 57
   4:  Connection: Close
   5:   
   6:  {"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}

In this example, the client requested either JSON, Javascript, or “anything” (*/*). The server responsed with a JSON representation of the Product object. Notice that the Content-Type header in the response is set to “application/json”.

A controller can also return an HttpResponseMessage object. To specify a CLR object for the response body, call the CreateResponse extension method:

[!code-csharpMain]

   1:  public HttpResponseMessage GetProduct(int id)
   2:  {
   3:      var item = _products.FirstOrDefault(p => p.ID == id);
   4:      if (item == null)
   5:      {
   6:          throw new HttpResponseException(HttpStatusCode.NotFound);
   7:      }
   8:      return Request.CreateResponse(HttpStatusCode.OK, product);
   9:  }

This option gives you more control over the details of the response. You can set the status code, add HTTP headers, and so forth.

The object that serializes the resource is called a media formatter. Media formatters derive from the MediaTypeFormatter class. Web API provides media formatters for XML and JSON, and you can create custom formatters to support other media types. For information about writing a custom formatter, see Media Formatters.

How Content Negotiation Works

First, the pipeline gets the IContentNegotiator service from the HttpConfiguration object. It also gets the list of media formatters from the HttpConfiguration.Formatters collection.

Next, the pipeline calls IContentNegotiatior.Negotiate, passing in:

The Negotiate method returns two pieces of information:

If no formatter is found, the Negotiate method returns null, and the client recevies HTTP error 406 (Not Acceptable).

The following code shows how a controller can directly invoke content negotiation:

[!code-csharpMain]

   1:  public HttpResponseMessage GetProduct(int id)
   2:  {
   3:      var product = new Product() 
   4:          { Id = id, Name = "Gizmo", Category = "Widgets", Price = 1.99M };
   5:   
   6:      IContentNegotiator negotiator = this.Configuration.Services.GetContentNegotiator();
   7:   
   8:      ContentNegotiationResult result = negotiator.Negotiate(
   9:          typeof(Product), this.Request, this.Configuration.Formatters);
  10:      if (result == null)
  11:      {
  12:          var response = new HttpResponseMessage(HttpStatusCode.NotAcceptable);
  13:          throw new HttpResponseException(response));
  14:      }
  15:   
  16:      return new HttpResponseMessage()
  17:      {
  18:          Content = new ObjectContent<Product>(
  19:              product,                // What we are serializing 
  20:              result.Formatter,           // The media formatter
  21:              result.MediaType.MediaType  // The MIME type
  22:          )
  23:      };
  24:  }

This code is equivalent to the what the pipeline does automatically.

Default Content Negotiator

The DefaultContentNegotiator class provides the default implementation of IContentNegotiator. It uses several criteria to select a formatter.

First, the formatter must be able to serialize the type. This is verified by calling MediaTypeFormatter.CanWriteType.

Next, the content negotiator looks at each formatter and evaluates how well it matches the HTTP request. To evaluate the match, the content negotiator looks at two things on the formatter:

If there are multiple matches, the match with the highest quality factor wins. For example:

[!code-consoleMain]

   1:  Accept: application/json, application/xml; q=0.9, */*; q=0.1

In this example, application/json has an implied quality factor of 1.0, so it is preferred over application/xml.

If no matches are found, the content negotiator tries to match on the media type of the request body, if any. For example, if the request contains JSON data, the content negotiator looks for a JSON formatter.

If there are still no matches, the content negotiator simply picks the first formatter that can serialize the type.

Selecting a Character Encoding

After a formatter is selected, the content negotiator chooses the best character encoding by looking at the SupportedEncodings property on the formatter, and matching it against the Accept-Charset header in the request (if any).





Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/AspNet-DocAndSamples-2017/aspnet/web-api/overview/formats-and-model-binding/content-negotiation.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>