Click here to Skip to main content
15,867,308 members
Articles / Web Development / ASP.NET
Tip/Trick

Gain More Performance in ASP Web API, Replace Standard Serialization with MessagePack

Rate me:
Please Sign up or sign in to vote.
2.60/5 (5 votes)
25 Jan 2013CPOL2 min read 37.4K   13   11
Boost up your serialization in ASP.NET Web API.

Introduction

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

Background

There are a few pros in replacing the default JSON serializator in Web API  with 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 info, please consider reading this article link.

Implementation

All this stuff can be achieved by definition using 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:

Accept: text/html,application/xhtml+xml,application/xml

This header tells the server that the client wants either HTML, XHTML, or XML.

To create a media formatter, derive from one of these classes:

  • MediaTypeFormatter: This class uses asynchronous read and write methods.
  • BufferedMediaTypeFormatter: This class derives from MediaTypeFormatter but wraps the asynchronous read/write methods inside synchronous 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's implement it, see code below:

C#
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));
    }
    {
        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 (type!=typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
        {
         value = (value as IEnumerable<object>).ToList();
        }
        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;
    }
}
public override bool CanReadType(Type type)

Client consuming

So let's create a simple entity:

C#
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:

C#
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"));
public class Person{
var result = client.SendAsync(request).Result;
var serializer = MessagePackSerializer.Create<Models.Person>();

The same but for collections:

C#
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 an example:

C#
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 find this interesting for you.

License

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


Written By
Team Leader Delphi LLC
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 1 Pin
Member 1067111313-Nov-14 9:15
Member 1067111313-Nov-14 9:15 
GeneralMy vote of 1 Pin
StrandedPirate27-Oct-14 15:00
StrandedPirate27-Oct-14 15:00 
QuestionDo you feel any shame for stealing this article? Pin
Donatas Maciunas4-Nov-13 3:45
Donatas Maciunas4-Nov-13 3:45 
AnswerRe: Do you feel any shame for stealing this article? Pin
Oleksandr Kulchytskyi4-Nov-13 4:11
professionalOleksandr Kulchytskyi4-Nov-13 4:11 
GeneralRe: Do you feel any shame for stealing this article? Pin
Donatas Maciunas7-Nov-13 23:24
Donatas Maciunas7-Nov-13 23:24 
GeneralRe: Do you feel any shame for stealing this article? Pin
Donatas Maciunas7-Nov-13 23:27
Donatas Maciunas7-Nov-13 23:27 
GeneralMy vote of 1 Pin
Donatas Maciunas4-Nov-13 3:42
Donatas Maciunas4-Nov-13 3:42 
SuggestionImplementation in WebApiContrib Pin
Simon Elliston Ball8-May-13 2:56
Simon Elliston Ball8-May-13 2:56 
GeneralRe: Implementation in WebApiContrib Pin
Oleksandr Kulchytskyi8-May-13 9:19
professionalOleksandr Kulchytskyi8-May-13 9:19 
BugSome problems serializing only a string Pin
gerrits31-Dec-12 3:03
gerrits31-Dec-12 3:03 
GeneralRe: Some problems serializing only a string Pin
User 67245133-Jan-13 22:18
User 67245133-Jan-13 22:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.