Filtered by Month (3) and Year (2012)
In this post, we will create RouteDataMapping. This custom MediaTypeMapping will allow us to involve the decision-making process about the response format according to RouteData values.
@ 03-03-2012
by Tugberk Ugurlu


I have talked about on ASP.NET Web API Content-Negotiation algorithm and MediaTypeMapping on my previous post. As I said there, creating one custom MediaTypeMapping is fairly simple.

In this post, we will create RouteDataMapping. This custom MediaTypeMapping will allow us to involve the decision-making process about the response format according to RouteData values. Here is the complete implementation:

public class RouteDataMapping : MediaTypeMapping {

    private readonly string _routeDataValueName;
    private readonly string _routeDataValueValue;

    public RouteDataMapping(
        string routeDataValueName, 
        string routeDataValueValue, 
        MediaTypeHeaderValue mediaType) : base(mediaType) {

        _routeDataValueName = routeDataValueName;
        _routeDataValueValue = routeDataValueValue;
    }

    public RouteDataMapping(
        string routeDataValueName, 
        string routeDataValueValue, 
        string mediaType) : base(mediaType) {

        _routeDataValueName = routeDataValueName;
        _routeDataValueValue = routeDataValueValue;
    }

    protected override double OnTryMatchMediaType(
        System.Net.Http.HttpResponseMessage response) {

        return (
            response.RequestMessage.GetRouteData().
            Values[_routeDataValueName].ToString() == _routeDataValueValue
        ) ? 1.0 : 0.0;
    }

    //Don't use this
    //This will be removed on the first RC (according to team members)
    protected override double OnTryMatchMediaType(
        System.Net.Http.HttpRequestMessage request) {

        throw new NotImplementedException();
    }
}

The implementation is fairly simple.One thing that you might notice is that we are returning double in order to tell the framework if it is a match or not. Here is the reason why:

The returned double value is used by the conneg algorithm to find the appropriate formatter to write. Its similar to how you can set the quality value in Accept header. - Kiran Challa

Let’s try this out. we will use the same sample on my previous post but we will make a few changes. First of all, we will change our route a little to add an extension. Keep in mind that you do not need to make this an extension. It will work for every RouteData value.

GlobalConfiguration.Configuration.Routes.MapHttpRoute(
    "defaultHttpRoute",
    routeTemplate: "api/{controller}.{extension}",
    defaults: new { },
    constraints: new { extension = "json|xml" }
);

Then we will make sure that we have the following registry inside our Web.config file in order for our Urls with extensions to work.

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
</system.webServer>

And finally, we will hook up our RouteDataMapping to the formatters:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.
    MediaTypeMappings.Add(
        new RouteDataMapping(
            "extension", "json", "application/json"
    )
);

GlobalConfiguration.Configuration.Formatters.XmlFormatter.
    MediaTypeMappings.Add(
        new RouteDataMapping(
            "extension", "xml", "application/xml"
    )
);

Now, when you navigate to /api/cars.json, you will get the data as json. If you navigate to /api/cars.xml, you will get the result as xml as below.

routeDataMapping

We will see how Content-Negotiation (Conneg) Algorithm works on ASP.NET Web API with MediaTypeFormatters and MediaTypeMappings
@ 03-03-2012
by Tugberk Ugurlu


If you have read my post on Getting Started With ASP.NET Web API, you probably saw me talking about exposing your data to the world with various types of formats. This feature has been made possible by formatters. Formatters handles serializing and deserializing strongly-typed objects.

For two days, I have been really looking into formatters and explored a lot of useful stuff and I thought that sharing those would be great.

Before diving deeply, ASP.NET Web API team moderating ASP.NET Web API Forum pretty often and provide you a way to solve your problems. Also, community is heavily involved. Hence the framework is so new, I was stuck at a few places but team and the community helped me out a lot.

One other place you should be aware of is ASP.NET Web API Reference on MSDN.

ASP.NET Web API beta was shiped with 3 different formatters: JsonMediaTypeFormatter, XmlMediaTypeFormatter, FormUrlEncodedMediaTypeFormatter. All these classes are derived from MediaTypeFormatter abstract class. As you might guess, it is fairly easy to create one and hook it up but in this post, I won’t talk about custom MediaTypeFormatters. I would like to talk about how they are being chosen and assigned to process the request by the framework, especially on MediaTypeMappings.

ASP.NET Web API decides which formatter to process request with according to its Content-Negotiation (Conneg) Algorithm. Kiran Challa has two great blog posts on this:

On these posts, you will find how the Conneg algorithm works inside the framework. It has various options and as default it looks at the http headers to decide the most suitable format.

For this post, I have created a very simple Web API project. I did that by creating an empty ASP.NET Web Application, installing AspNetWebApi nuget package (had to install System.Json package separately). Then I registered my route:

protected void Application_Start(object sender, EventArgs e) {

    GlobalConfiguration.Configuration.Routes.MapHttpRoute(
        "defaultHttpRoute",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}

Finally, I created a simple API:

public class CarsController : ApiController {

    public string[] Get() {

        return new string[] { 
            "BMW",
            "Ferrari",
            "FIAT"
        };
    }
}

When I fire up the development web server IIS Express and navigate to /api/cars, I get the list of cars as expected. This is not the clearest way of explaining it, is it? Let’s see the headers:

Request:

GET http://localhost:4446/api/cars HTTP/1.1
User-Agent: Fiddler
Host: localhost:4446

Response:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcRHJvcGJveFxBcHBzXEFTUE5FVFdlYkFQSVNhbXBsZXNcQ29ubmVnQWxnb3JpdGhtU2FtcGxlXHNyY1xDb25uZWdBbGdvcml0aG1TYW1wbGVcYXBpXGNhcnM=?=
X-Powered-By: ASP.NET
Date: Sat, 03 Mar 2012 10:52:49 GMT

18
["BMW","Ferrari","FIAT"]
0

As you see, we have the response back as json because it is the first formatter registered (yes, order matters) by default and we didn’t specify which format we are interested in. When you add "Accept: application/xml" to your request, you will see that you will be getting the response back as xml. Approve it or not, this is the RESTFul way of negotiating between client and server. But sometimes we would like to decide the format according to QueryString. If so, you have an OOB support for this.

Intro to MediaTypeMappings

By default, Accept and Request Content-Type headers play role on deciding which format you serve. One other way of involving a formatter to process your request is MediaTypeMapping.

MediaTypeMapping provides a way for us to participate the Conneg algorithm decision making process and decide if we would like the formatter to take part in writing the response. There are several built in MediaTypeMappings (actually 4) supported out of the box. These are QueryStringMapping, RequestHeaderMapping, UriPathExtensionMapping, MediaRangeMapping. All these classes are derived from MediaTypeMapping abstract class (yes, creating a custom one is tedious and I plan on writing a post on that as well). We have these mappings and the other great stuff is that all default formatters has a hook up point in order to register mappings.

Let’s assume that we would like to decide the format of response based on a query string value as well. As we have QuesryStringMapping, we can use this and can provide our data on json format if request comes with ?format=json quesry string and xml format if it is ?format=xml. Here is the configuration in order to enable this:

protected void Application_Start(object sender, EventArgs e) {

    GlobalConfiguration.Configuration.Routes.MapHttpRoute(
        "defaultHttpRoute",
        routeTemplate: "api/{controller}"
    );

    GlobalConfiguration.Configuration.Formatters.JsonFormatter.
        MediaTypeMappings.Add(
            new QueryStringMapping(
                "format", "json", "application/json"
        )
    );

    GlobalConfiguration.Configuration.Formatters.XmlFormatter.
        MediaTypeMappings.Add(
            new QueryStringMapping(
                "format", "xml", "application/xml"
        )
    );
}

When we make a request with accept header and format query string, we will see that framework honors our mapping registrations:

Request:

GET http://localhost:4446/api/cars?format=xml HTTP/1.1
User-Agent: Fiddler
Host: localhost:4446
Accept: appication/json

Response:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Transfer-Encoding: chunked
Content-Type:
application/xml
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcRHJvcGJveFxBcHBzXEFTUE5FVFdlYkFQSVNhbXBsZXNcQ29ubmVnQWxnb3JpdGhtU2FtcGxlXHNyY1xDb25uZWdBbGdvcml0aG1TYW1wbGVcYXBpXGNhcnM=?=
X-Powered-By: ASP.NET
Date: Sat, 03 Mar 2012 11:36:04 GMT

e9
<?xml version="1.0" encoding="utf-8"?><ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><string>BMW</string><string>Ferrari</string><string>FIAT</string></ArrayOfString>
0

Pretty powerful stuff. Enjoy Smile



Hi 🙋🏻‍♂️ I'm Tugberk Ugurlu.
Coder 👨🏻‍💻, Speaker 🗣, Author 📚, Microsoft MVP 🕸, Blogger 💻, Software Engineering at Deliveroo 🍕🍜🌯, F1 fan 🏎🚀, Loves travelling 🛫🛬
Lives in Cambridge, UK 🏡