When to use custom formatters

Use a custom formatter when you want the content negotiation process to support a content type that isn't supported by the built-in formatters (JSON and XML).

For example, if some of the clients for your web API can handle the Protobuf format, you might want to use Protobuf with those clients because it's more efficient. Or you might want your web API to send contact names and addresses in vCard format, a commonly used format for exchanging contact data. The sample app provided with this article implements a simple vCard formatter.

Overview of how to use a custom formatter

Here are the steps to create and use a custom formatter:

Create an output formatter class if you want to serialize data to send to the client.

Create an input formatter class if you want to deserialize data received from the client.

Add instances of your formatters to the InputFormatters and OutputFormatters collections in MvcOptions.

The following sections provide guidance and code examples for each of these steps.

You can't do constructor dependency injection in a formatter class. For example, you can't get a logger by adding a logger parameter to the constructor. To access services, you have to use the context object that gets passed in to your methods. A code example below shows how to do this.

Override CanReadType/CanWriteType

Specify the type you can deserialize into or serialize from by overriding the CanReadType or CanWriteType methods. For example, you might only be able to create vCard text from a Contact type and vice versa.

The CanWriteResult method

In some scenarios you have to override CanWriteResult instead of CanWriteType. Use CanWriteResult if the following conditions are true:

Your action method returns a model class.

There are derived classes which might be returned at runtime.

You need to know at runtime which derived class was returned by the action.

For example, suppose your action method signature returns a Person type, but it may return a Student or Instructor type that derives from Person. If you want your formatter to handle only Student objects, check the type of Object in the context object provided to the CanWriteResult method. Note that it's not necessary to use CanWriteResult when the action method returns IActionResult; in that case, the CanWriteType method receives the runtime type.

Override ReadRequestBodyAsync/WriteResponseBodyAsync

You do the actual work of deserializing or serializing in ReadRequestBodyAsync or WriteResponseBodyAsync. The highlighted lines in the following example show how to get services from the dependency injection container (you can't get them from constructor parameters).

To see vCard output, run the application and send a Get request with Accept header "text/vcard" to http://localhost:63313/api/contacts/ (when running from Visual Studio) or http://localhost:5000/api/contacts/ (when running from the command line).

To add a vCard to the in-memory collection of contacts, send a Post request to the same URL, with Content-Type header "text/vcard" and with vCard text in the body, formatted like the example above.

Feedback

We'd love to hear your thoughts. Choose the type you'd like to provide: