Recently I've been working on converting an ASP.NET WebAPI service to run on ASP.NET Core instead.
This service's inputs and outputs are all encoded with Google's Protobuf, and for WebAPI I wrote a custom MediaTypeFormatter
to deserialize requests and serialize responses into fully-structed objects.
For ASP.NET Core this has been redone to be a lot simpler, but isn't heavily documented - or if it is, I couldn't find much on it.
Creating Custom Formatters
In ASP.NET Core, formatters have been split into two. There is now IInputFormatter
and IIOutputFormatter
. These each have corresponding abstract classes, InputFormatter
and OutputFormatter
respectively, that do a lot of the heavy lifting and default HTTP content negotiation.
When subclassing InputFormatter
or OutputFormatter
, in the constructor, add your supported media types to the SupportedMediaTypes
property, then there are a handful of methods that you can override in order to implement the actual deserialization and serialization.
Setting a default Formatter
To set a default input formatter - i.e., the formatter that is used when the request's Content-Type
header is blank or undefined - I first tried inserting the formatter at the beginning of the list in the application's Startup
procedures:
// This method gets called by the runtime. Use this method to add services to the container.
public virtual void ConfigureServices(IServiceCollection services)
{
var mvc = services.AddMvc(options =>
{
options.InputFormatters.Insert(0, new ProtobufMediaTypeInputFormatter());
options.OutputFormatters.Insert(0, new ProtobufMediaTypeOutputFormatter());
});
...
}
Unfortunately, this didn't help. After digging into the source of InputFormatter
[1], I eventually discovered that bool CanRead(...)
returns false
if Content-Type
is null or empty. This was easy enough to fix by overriding CanRead
in my ProtobufMediaTypeInputFormatter
:
public override bool CanRead(InputFormatterContext context)
{
if (string.IsNullOrEmpty(context.HttpContext.Request.ContentType))
{
return CanReadType(context.ModelType);
}
return base.CanRead(context);
}
With that in place, and being at the beginning of the list of formatters, ASP.NET Core will try and use it for any request without a Content-Type
header.
Hooray for ASP.NET Core being fully open-source! ↩︎