Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Content Negotiation and Custom Formatter in ASP.NET Web API

0.00/5 (No votes)
29 Dec 2016 1  
In this article we will learn about Content Negotiation and its default use in ASP.NET Web API. Further we will see how to implement Custom Content Formatter to fulfill client’s request with a special format.

Introduction

Content Negotiation is a key concept of RESTful web services. In this article we will learn about Content Negotiation and its default use in ASP.NET Web API. Further we will see how to implement Custom Content Formatter to fulfill client’s request with a special format.

Outlines

  • What is Content Negotiation
  • How Content Negotiation works
  • Overview of Demo Web API Service
  • Creating Demo Client Page
  • Creating Custom Media Formatter

Note:

ASP.NET Web API is a platform to build RESTful services. In this article we will not discuss in detail about Web API and RESTful services. If you are new to them visit Home Page of Web API and ASP.NET Web API Tutorials

What is Content Negotiation

Content Negotiation is a kind of conversation between client and server over HTTP call. Before sending any request to server, client can specify expected type of format and server will send data in requested format. If server is not able to send data in requested format then sever will send data in its default format. In case of ASP.NET Web API, JSON is default format. Following diagram shows how client and server communicate in Content Negotiation context.

In the diagram, Server supports three types of content type: json (default), xml (in built with ASP.NET WebAPI) and mycustomtype (created for this demo). From client perspective:

  • Client 1 requests to get data in JSON format then server will send data in JSON format. JSON is default format for sending data.
  • Client 2 requests to get data in XML format then server will send data in XML format as this format is supported by server.
  • Client 3 requests to get data in XYZ format. Server does not support such kind of format then server will send data in JSON (default) format.
  • Client 4 requests to get data in custom format: mycustomtype and server is also aware about requested custom format then server will send data in custom format.

Content Negotiation between server and clients

In next section we will see how client informs server to get data in expected format.

How Content Negotiation works

When client makes any request to server via URI, along with the URI, HTTP Header also goes to the server which contains "Accept" parameter under Request Header. The sever send response with parameter "Content-Type" under Response Header. There two parameters ("Accept" and "Content-Type") play role in Content Negotiation process. The description of these parameters is given below.

Accept: Accept is a parameter comes under HTTP Header (Request Header). Client can specify expected format such as xml, json, image, video, audio etc. If client does not specify any value for Accept parameter then server sends data in its default format.

Content-Type: Content-Type is a parameter of HTTP Header (Response Header). This parameter is available with Response body when response goes from server to client. Server specifies value for Content-Type according to the format data is being sent to the client.

Here specified values, either for Accept parameter from client side or Content-Type parameter from server side, is called Media Type. Other than Accept parameter, The HTTP/1.1 standard defines more standard parameters like Accept-Charset, Accept-Encoding, Accept-Language , which we can be specified in HTTP header. In this article we will discuss on Accept parameter only in detail.

In further section of this article we will see how to specify value for Accept parameter and how client/server set value for Accept/Content-Type parameter through code.

Overview of Demo Web API Service

To understand Content Negotiation mechanism practically, first we will create ASP.NET Web API RESTful service then we will create a very simple client to consume that service.

Let us first create ASP.NET Web API service using Visual Studio 2012 targeting to .NET 4.0 framework. You can go for higher version also. Now navigate to “ASP.NET MVC 4 Web Application” template and give it name as "ContentNegotiationWebAPI" and click "OK".



Next we get Project Template window, select "Web API" template and click "Ok".

Project structure will be created for us. We are not using all folder and files added to the project by default. So I have removed all unnecessary folder and files from the solution attached with this article. Then added a new controller called CustomerController and in Model folder added a model called Customer. Modified Solution structure is shown in below screenshot.

CustomerController is inherited from ApiController. As we know when a controller inherited with ApiController then controller's action method always returns data, not view. We write a method in CustomerController called GetCustomer which will return a customer. Similar code is shown below.

public class CustomerController : ApiController
{
    // GET api/Customer
    [HttpGet]
    public Customer GetCustomer()
    {
        return new Customer { Name="Jim", City="Bangalore"};
    }
}

If client makes a "Get" request by default ASP.NET Web API look for a method called Get. But our method name is GetCustomer. So default a route is defined in WebApiConfig.cs file will not work. For that we need to add action segment inside MapHttpRoute method of WebApiConfig.cs file as shown in below code.

config.Routes.MapHttpRoute(
     name: "DefaultApi",
     routeTemplate: "api/{controller}/{action}/{id}",
     defaults: new { id = RouteParameter.Optional }
      );

You need to add following code inside of system.webServer tag in Web.config file to make the service available for clients. It will set a http response header while sending response to the clients. Since Custom response header contains information with name and value pairs. These name and value pairs are returned with responses from a Web server.

<httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*"/>
        <add name="Access-Control-Allow-Headers" value="Content-Type"/>
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS"/>
      </customHeaders>
</httpProtocol>

Creating Demo Client Page

In above section we have created simple ASP.NET Web API Service which is having a single method called GetCustomer. Now in this section we will create a client to consume the service. Here client will be a simple html page with a button and on button click event jQuery’s ajax function is called with a URI. URI targets to GetCustomer resource of the service. Response from server displayed as text inside div tag called divResult. Following code is written on "SendRequest" button click event. formatRequested is a local variable which holds type of possible request format. Then beforeSendSpecifyContentFormat from function setRequestHeader function will be called by passing the value of "Accept" parameter.

$("#btnSendRequestToWeb").click(function () {
    // To hold various type of content format
    var formatRequested;
    //formatRequested = "text/json";
    //formatRequested = "text/xml";
    //formatRequested = "someNonSupportedFormat";
    //formatRequested = "text/mycustomtype";
    //formatRequested = "application/mycustomtype";
    // This method is used in AJAX call.
    // You can use any of above metioned content format and see the different results
    beforeSendSpecifyContentFormat = function (req) {
        req.setRequestHeader("Accept", formatRequested);
    },
    $.ajax({
        url: "http://localhost:1764/api/Customer/getcustomer",
        //beforeSend: beforeSendSpecifyContentFormat,
        success: function (result) {
            var output;
            var checkIfXML = $.isXMLDoc(result);
            if (checkIfXML) {
                var oSerializer = new XMLSerializer();
                output = oSerializer.serializeToString(result);
            }
            else {
                output = JSON.stringify(result);
            }
            $("#divResult").text(output);
        }
    });
    .....
    .....
});

Open "ClientPage.html" page in browser (preferably Chrome). Click on “Send Request” button. You will be able to see response in JSON format.



Now Press F12 key to open Developer tool in Chrome Web Browser. Look at Content-Type:application/json; charset=utf-8 parameter of Response header. Here Media Type is "application/json; charset=utf-8". This is the default Media Type of ASP.NET Web API service. In Request Header Accept:*/*, here Media Type is "*/*". It means client did not specify any Media Type to get data in a particular format and client is ready to accept data in default format from ASP.NET Web API service.

Now lets try to get data in XML format from server. So at client side, in JavaScript code, first un-comment below line:

formatRequested = "text/xml";

Now in $.ajax method just above url un-comment following code: As per code sample it is specified by formatRequested as "text/xml".

beforeSend: beforeSendSpecifyContentFormat,

Here before sending the request to server we are specifying that which format client is expecting. Open/refresh "ClientPage.html" in browser again and click on "Send Request" button. This time received response will be in xml format as shown below:

Now press F12 again, notice the Accept parameter of Request Headers. This time value for Accept parameter is "text/xml", this is because before sending request to sever client has specified expected format is XML. In similar way notice the Content-Type parameter of Response Headers. Since client has specified expected format is xml that is why value for Content-Type parameter is text/xml.

Creating Custom Media Formatter

There might be some scenario where client expect data in different format other than JSON or XML format. In such situation we can create Custom Content Media Formatter to serve client's request. For example client is expecting data in simple text with some kind of template. Since server can not send data, by default, using some kind of template, we must create a Custom Content Media Formatter. It will send response in simple string format but with desired tempalate. To do that first we will add a folder called CustomFormat in service poject. And add a class called CustomTextFormatter in CustomFormat folder.

Whenever we are required to create Custom Content Media Formatter then we can derive our custom formatter class either from MediaTypeFormatter or BufferedMediaTypeFormatter. Our CustomTextFormatter class is inhereted with BufferedMediaTypeFormatter class. BufferedMediaTypeFormatter class is abstract class inhereted from MediaTypeFormatter abstract class. There are three abstract method of MediaTypeFormatter class called CanReadType, CanWriteType and WriteToStream which we have overrided in CustomTextFormatter class. In the constructor of CustomTextFormatter class we are specifing two value as string of MediaType. while sending request to server client need to specify same string to get data in plain text or to use CustomTextFormatter class.

public class CustomTextFormatter : BufferedMediaTypeFormatter
  {
      public CustomTextFormatter()
      {
          SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/mycustomtype"));
          SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/mycustomtype"));
          // Important to add the below code otherwise if the client request doesn't contain
          // any encoding header, a 500 server error will be thrown
          SupportedEncodings.Add(new UTF8Encoding(false, true));
          SupportedEncodings.Add(new UnicodeEncoding(false, true, true));
      }
      // CanReadType method will deserialize Customer.
      public override bool CanReadType(Type type)
      {
          //return type == typeof(Customer);
          return false;
      }
      // CanWriteType method will serialize Customer.
      public override bool CanWriteType(Type type)
      {
          //Ensure we are serializing only for Resource object
          // if more types to be allowed, add the conditions
          return type == typeof(Customer);
      }
      // WriteToStream method serializes Customer type by writing it to a stream
      public override void WriteToStream(Type type, object value, Stream writeStream, HttpContent content)
      {
          Encoding effectiveEncoding = SelectCharacterEncoding(content.Headers);
          using (StreamWriter sWriter = new StreamWriter(writeStream, effectiveEncoding))
          {
              var str = SerializeResourceToMyType((Customer)value);
              sWriter.Write(str);
          }
      }
      private string SerializeResourceToMyType(Customer customer)
      {
          return "Our valued customer " + customer.Name + " lives in " + customer.City;
      }
  }

Registring CustomTextFormatter in WebApiConfig.cs File: After creating Custom Formatters, we need to register it inside the Register method WebApiConfig.cs.

config.Formatters.Add(new CustomTextFormatter());

Now CustomTextFormatter is ready to use by client. So let us use CustomTextFormatter from ClientPage.html. This time we will set formatRequested as "text/mycustomtype". The client side code should similar to as shown below:

formatRequested = "text/mycustomtype";
// This method is used in AJAX call.
// You can use any of above metioned content format and see the different results
beforeSendSpecifyContentFormat = function (req) {
     req.setRequestHeader("Accept", formatRequested);
      },
 $.ajax({
      url: "http://localhost:1764/api/Customer/getcustomer",
      beforeSend: beforeSendSpecifyContentFormat,
......
......

While sending request to Server, name of Media Type must be same as we have given in the Constructor of CustomTextFormatter class in service. In the Constructor of CustomTextFormatter class we have add two SupportedMediaTypes by calling MediaTypeHeaderValue with "application/mycustomtype" and "text/mycustomtype" strings. So from client we can send either "application/mycustomtype" or "text/mycustomtype" as formatRequested.

Now open "ClientPage.html" in browser and click on "Send Request" button. We must be able to get response in simple string format as shown below.

Conclusion

In this article, we had a walkthrough to understand default Content Negotiation in ASP.NET WebAPI. And then we created a custom content formatter and used it in server application and consumed from client. Thanks for reading. Your comments and suggestions for improvement are most welcome.

References

  1. Mozilla Developer Network - Content Negotiation
  2. Wikipedia - Content Negotiation
  3. MSDN - Content Negotiation in ASP.NET Web API

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here