Wednesday, December 11, 2013

ASP.NET Web API 2 Content Negotiation & Media Type Formatting

ASP.NET Web API supports requests using HTTP methods. These requests can use any HTTP method including (but not limited to) GET and POST methods. The data to / from the service can use any media type as long as the service is supporting those formats. The client requests can specify the media type format using Accepts or Content-Type details of HTTP message. In the previous posts we discussed how ASP.NET Web API uses content negotiation to provide data to clients based on the requested format. There are some default media type formatters in ASP.NET Web API 2.

All of these MediaTypeFormatter(s) are available in System.Net.Http.Formatting assembly except ODataMediaTypeFormatter, which is available in System.Web.Http.OData assembly. All of them inherit from MediaTypeFormatter abstract base type.

MediaTypeFormatter has two abstract members. These methods are used to determine if a particular type can be formatted using the particular formatter. The methods return true if they support so. Here CanReadType is used when the service receives a request with content type as passed in the argument. On the other hand, CanWriteType is used when some data is to be pushed to client. Again the argument would refer to the data type of the object being pushed to the client.

Here the Read and Write methods in MediaTypeFormatter are asynchronous. BufferedMediaTypeFormatter provides a synchronous wrapper around these methods.

Pipe Delimited Formatter
Let's assume that our service needs to supports providing and accepting students' data in pipe delimited format. Now we need to add support to understand how to parse Student's data when provided by a client. We also need to convert the data pushed to our clients into pipe delimited format. ASP.NET Web API supports formatting data using MediaTypeFormatters. There are two such formatters added by default. They are to support XML and JSON data. If we are planning to support a new format, we need to add an explicit MediaTypeFormatter for such purpose. Here is a simpleton formatter to support pipe delimited data.

As you can notice from the above definition, the same formatter is used to read and write data by the service. Each formatter can be used to support a number of media types. They are identified through SupportedMediaTypes collection in MediaTypeFormatter. Our formatter is supposed to support pipe delimited text data, so the supported media type is specified as "text/pdv".

We must know that the same formatter is used both for reading and writing data by our service. The decision for supported types can be done in CanReadType / CanWriteType method pair. They are from the perspective of our service. Here CanReadType will be used for GET based requests when we need to push data to clients. On the other hand CanWriteType is used when service accepts some data from a client. In order for actual reading and writing of data for these types ReadFromStream and WriteToStream methods are used respectively.

Now we need to add the custom formatter to the list of Media Type Formatters used by the service. This is part of HttpConfiguration for the service. We can update WebApiConfig as follows:

Requesting Data for Configured Media Type Format
In the earlier posts we have seen how we can use Fiddler to request Web API to provide data in the format of the specified media type. We just need to use Accepts header. Here we are specifying the media type as "text/pdv".

Posting Data in Configured Media Type Format
Since our MediaTypeFormatter supports reading Student data in text/pdv format, we can post data to the service in this format. Let us add an action in our InstituteController. The action supports POST method of HTTP. Since we are using Attribute routing, we need to specify the route on the action.

We can compose the request using Fiddler. Here we are posting student's data in Pipe delimited format to the service.

Based on the content type specified in the request, the service picks up the correct MediaTypeFormatter. Since we are supporting data for Student type, the provided data is parsed into Student type format using ReadFromStream method of the formatter. The parsed object is then passed to the requested action as follows:


No comments: