Click here to Skip to main content
Click here to Skip to main content
Articles » Web Development » ASP.NET » Howto » Revisions
 
Go to top

Gain more performance in ASP Web API, replace standard serialization to MessagePack

, 26 Dec 2012
Rate this:
Please Sign up or sign in to vote.
Boost up your serialization in ASP.NET Web API.
This is an old version of the currently published tip/trick.

Introduction

In my previous tip I introduced to publicity new serialization utility called MessagePack. See link. Today i want to show how to replace default serialization in ASP.NET Web API. 

Background 

As concerns default JSON serializator in Web API, there are few prons in side of replacing it by MessagePack: 

JSON uses 4 bytes to represent null, MessagePack only requires 1 byte;

JSON uses 2 bytes to represent a typical int, MessagePack requires 1 byte and so on;

Also, since it’s binary, it’s faster to read and write than JSON.

If you want more, please consider to read next article link

Implementation 

All this stuff can be achived by definition of our own MediaTypeFormatter.

A media type, also called a MIME type, identifies the format of a piece of data. In HTTP, media types describe the format of the message body. A media type consists of two strings, a type and a subtype.

When an HTTP message contains an entity-body, the Content-Type header specifies the format of the message body. This tells the receiver how to parse the contents of the message body.

For example, if an HTTP response contains a PNG image, the response might have the following headers. 

HTTP/1.1 200 OK
Content-Length: 95267
Content-Type: image/png 

When the client sends a request message, it can include an Accept header. The Accept header tells the server which media type(s) the client wants from the server. For example:

  • MediaTypeFormatter. This class uses asynchronous read and write methods.
  • BufferedMediaTypeFormatter. This class derives from MediaTypeFormatter but wraps the asynchronous read/write methods inside sychronous methods. 
  • Deriving from BufferedMediaTypeFormatter is simpler, because there is no asynchronous code, but it also means the calling thread can block during I/O. 

    So, let implement it, see code below:

    public class MediaTypeFormatterCompatibleMessagePack : MediaTypeFormatter
    {
        private readonly string _mime = "application/x-msgpack";
        Func<Type, bool> IsAllowedType = (t) =>
            {
                if (!t.IsAbstract && !t.IsInterface && t != null && !t.IsNotPublic)
                    return true;
                if (typeof(IEnumerable).IsAssignableFrom(t))
                    return true;
                return false;
            };
        public MediaTypeFormatterCompatibleMessagePack()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue(_mime));
        }
        public override bool CanReadType(Type type)
        {
            if (type == null) throw new ArgumentNullException("type is null");
            return IsAllowedType(type);
        }
        public override bool CanWriteType(Type type)
        {
            if (type == null) throw new ArgumentNullException("Type is null");
                    return IsAllowedType(type);
        }
        public override Task WriteToStreamAsync(Type type, object value, 
          Stream stream, HttpContent content, TransportContext transportContext)
        {
            if (type == null) throw new ArgumentNullException("type is null");
            if (stream == null) throw new ArgumentNullException("Write stream is null");
            var tcs = new TaskCompletionSource<object>();
            if (typeof(IEnumerable).IsAssignableFrom(type)) 
            {     
            }
            var serializer = MessagePackSerializer.Create<dynamic>();
            serializer.Pack(stream, value);
            tcs.SetResult(null);
            return tcs.Task;
        }
    
        public override Task<object> ReadFromStreamAsync(Type type, 
          Stream stream, HttpContent content, IFormatterLogger formatterLogger)
        {
            if (content.Headers != null && content.Headers.ContentLength == 0) return null;
    	    try
    	    {
    	        var serializer = MessagePackSerializer.Create(type);
    	        object result;
    	        using (var mpUnpacker = Unpacker.Create(stream))
    	        {
    	          mpUnpacker.Read();
    	          result = serializer.UnpackFrom(mpUnpacker);
    	        }
    	        tcs.SetResult(result);
    	    }
    	    catch (Exception e)
    	    {
    	        if (formatterLogger == null) throw;
    	        formatterLogger.LogError(String.Empty, e.Message);
    	        tcs.SetResult(GetDefaultValueForType(type));
    	    }
    	    return tcs.Task;
        }
    }
    
    value = (value as IEnumerable<object>).ToList();

    Client consuming

    So lets create simple entity:

    public class Person{
    public int PId {get;set;} 
    public string Name {get;set;}
    public DateTime CreatedAt  {get;set;} 
    public string CreatedBy {get;set;} 
    public string Description {get;set;} 
    }  

     And a simple sender which uses HttpClient:  

    var client = new HttpClient();
    var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:1212/api/msgPack/1");
    request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/x-msgpack"));
    var result = client.SendAsync(request).Result;
    var serializer = MessagePackSerializer.Create<Models.Person>();  

    The same but concerns collections

    var client = new HttpClient();
    var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:1212/api/msgPack/");
    request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/x-msgpack"));
    var result = client.SendAsync(request).Result;
    var serializer = MessagePackSerializer.Create<List<WebApi.MessagePack.Models.Url>>();
    List<Person> data = serializer.Unpack(result.Content.ReadAsStreamAsync().Result); 

    And finally posting example: 

    var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:49745/api/values/");
    request.Content = new ObjectContent<Person>(
        new Person() {
            PId = 1234,
            CreatedAt = DateTime.Now,
            CreatedBy = "Filip",
            Description = "test person" },
        new MessagePackMediaTypeFormatter());
    request.Content.Headers.ContentType.MediaType = "application/x-msgpack";
    StatusCode result5 = client.SendAsync(request).Result.StatusCode;  

    Hope you have found it interesting for you Smile

    License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

    Share

    About the Author

    Oleksandr Kulchytskyi
    Software Developer (Senior) Frog (Aricent group)
    Ukraine Ukraine
    No Biography provided
    Follow on   Twitter

    Comments and Discussions


    Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
     
    QuestionDo you feel any shame for stealing this article? PinmemberDonatas Maciunas4-Nov-13 3:45 
    AnswerRe: Do you feel any shame for stealing this article? PinmemberOleksandr Kulchytskyi4-Nov-13 4:11 
    GeneralRe: Do you feel any shame for stealing this article? PinmemberDonatas Maciunas7-Nov-13 23:24 
    GeneralRe: Do you feel any shame for stealing this article? PinmemberDonatas Maciunas7-Nov-13 23:27 
    GeneralMy vote of 1 PinmemberDonatas Maciunas4-Nov-13 3:42 
    SuggestionImplementation in WebApiContrib PinmemberMember 40987598-May-13 2:56