Customizing FormatFilter behavior in ASP.NET Core MVC 1.0

When you are building HTTP APIs with ASP.NET Core MVC, the framework allows you to use FormatFilter to let the calling client override any content negotiation that might have happened on the server side.

This way, the client can – for example – force the return data to be JSON or CSV or any other format suitable (as long as the server supports it, of course) for his consumption.

The built-in mechanism (out of the box version of FormatFilter) is a little limited, so let’s have a look at how you can extend and customize its behavior.

A little FormatFilter background

If you follow this blog, I already blogged about FormatFilter a few months ago.

As a reminder, FormatFilter allows you to use querystring value or a route value to override content negotiation.

In the example from the old blog post:

In this case, format filter would allow us to request this resource the following way:

In the first URI example, the content negotiation process would run normally, so the server would consider the Accept header of the request and determine the response media type that way.

In the latter two examples, the presence of FormatFilter on the controller (it could also be applied on the action), would force the response to pick up a formatter from the FormatterMappings, using the xml key passed in by the caller.

At this point you may ask what are formatter mappings – this was covered in the old post, but in short, formatter mappings let you bind a specific media type to a specific string value. For example:

This sample configuration ensures that if the client passes xml as the format key – in the route data or in querystring, the MVC will always respond with application/xml media type. Remember it’s a two step setup – it’s necessary to decorate a controller/action with [FormatFilter] – setting the formatter mappings will not have any effect without that.

Out of the box, FormatFilter has got both the name “format“, and the fact that it looks at querystring and route data, hardcoded. This means you cannot use a different word and you cannot obtain the format in any other way.

This is where the customization process kicks in.

Customizing FormatFilter behavior

As a interesting (useless?) piece of trivia, I may mention, that the fact that we can customize the FormatFilter at all, is thanks to this monumental 8-character (!) pull request I sent to MVC a while ago.

With this change, we can now subclass FormatFilter and introduce our custom logic.

Let’s imagine we want to use dataType key instead of format and we would like to allow the client to also pass this information in the header – instead of just route data and query string. This may sound silly, but it is a real life scenario I encountered when porting existing web service to ASP.NET Core. Quite often, when you port/migrate you want to keep 100% compatibility and this is one of the good examples.

The customization is shown in the next. First let’s define some extension methods to extra route data, query string and header values from ActionContext. These are merely helpers we can rely on.

Now we can implement our custom filter.

Which can then be registered in the ASP.NET services in place of the default implementation.

If we now look back at the original sample controller we had, we can access the book resource in XML in the following ways:

Of course you can customize it even further. For example, perhaps you would like to override media type per user? This is entirely possible:

There are many other customization scenarios here. Another good example is that you could now register FormatFilter globally – and use the custom FormatFilter to mute its behavior in the controllers/actions you do not wish it to be applied.

And that’s it – hopefully someone finds this useful.