ASP.Net Web API 2 controller custom content negotiation

By default, ASP.net web api 2 automatically  delivers xml and json content types without any explicit serialization in controller with the help of content negotiation (conneg).

For example if your controller defines a default Get method that returns some object, on client request this object may be returned as xml or json string without adding explicit serialization code inside the method.

public class MyController : ApiController
{
//   Request uri /api/My/1
public Resource get(int id) 
{ 
  return new Resource{ Name="Raj" }; 
}
} 
//where Resource is defined as below 
public class Resource {
 public string Name {get; set;} 
}

When the client requests uri /api/My/1 with header Accept (or Content-Type) set to ‘application/json’, web api returns json representation of Resouce object. If the request header Accept (or Content-Type) set to ‘application/xml’, then controller returns xml representation of Resource object. 

Here No explicit serialization is required for json and xml. If any media type other than json or xml, then custom MediaTypeFormatters can be used. These custom formatters extend either BufferedMediaTypeFormatter or MediaTypeFormatter with some overridden methods and are configured inside WebApiConfig Register method.

  public class CustomTextFormatter : BufferedMediaTypeFormatter
    {
        public CustomTextFormatter()
        {

	    SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/mytype"));
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/mytype"));

	    // 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));
        }

        public override bool CanReadType(Type type)
        {

            return type == typeof(Resource);
        }

        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(Resource);
        }

        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 = MyCustomSerializer.SerializeResourceToMyType((Resource) value);
                sWriter.Write(str);
            }
        }
    }

By default , all the configured  formatters will be applied to all the controllers defined in the project.

  public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

	    //adds a custom formatter to api formatters list
            config.Formatters.Add(new CustomTextFormatter());

        }
    }

What if you want to restrict custom formatter to only one controller. A work around for this scenario is to  use a controller attribute with the implementation of Initialize method of IControllerConfiguration interface.

 public class CustomTextTypeAttribute : Attribute, IControllerConfiguration
    {
        public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
        {
          
	    //Add the custom formatter at first (or simply add to the list)    
            controllerSettings.Formatters.Insert(0,new CustomTextFormatter());

        }
    }

Now add this attribute to your controller

[CustomTextType]
public class MyController : ApiController
{

if the client requests uri /api/My/1 with header Accept (or Content-Type) set to ‘application/mytype’ (or ‘text/mytype’), our custom formatter will be invoked.

image

if a different controller url requested with the new content type ‘application/mytype’, then you may get default configured formatter (the first formatter in the formatters) or a 406 error if no formatters configured.

Leave a Reply

Your email address will not be published. Required fields are marked *