Wednesday, May 25, 2022

Using custom media types in .NET

How to use and create custom mime types in .NET? How to respond to different mime-types in your Web API? Use the same HTTP Verb but hit different actions based on the Content-Type header value? Have 2 methods that have the same number of parameters and respond to the same HTTP Verb?

These are questions that some of the developers seek answers to.

Accept & Content-Type

There are 2 very important headers that are often overlooked, Accept and Content-Type, and play a major role in a process called Content-Negotiation.

Accept header
With this header, the API consumer tells the server what is the mime type it expects as the format for the response.
eg. Accept: application/json or Accept: application/xml.
If the server can’t build a response with the requested format, it will return a 406 – Not Acceptable status code.

Describes the format of the body of the request that is sent to the server. This tells the server: “Hey, the body you will receive has this format, can you interpret it?”
If the server can’t read the content of the body, it will return a 415 Unsupported Media Type status code.

Content-Negotiation is the process that happens with every request, where basically the two parties involved (i.e the Server and the Consumer/Client) do exactly that. : negotiate on the content. Depending on the format send as value for these 2 header properties, the result can differ.

When we talk about APIs or REST-ful APIs we implicitly talk about different representations of resources.

Introducing the problem

Let’s say we have an API that deals with speakers. For these, we store all kinds of info, but not all are needed every time. Maybe you need to return a full object in one scenario, and just a few properties in another one, like below.

   public class SpeakerModel
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Bio { get; set; }
        public string JobTitle { get; set; }
        public string Website { get; set; }
        public string Email { get; set; }
        public string TwitterHandle { get; set; }
        public string CompanyName { get; set; }
        public string Address { get; set; }
        public string Phone { get; set; }
        public string PassportDetails { get; set; }
public class TrimmedSpeakerModel
        public int Id { get; set; }      
        public string FirstName { get; set; }
        public string LastName { get; set; }

To achieve this, people end up creating new endpoints because the platform forces us to do so, in a way. We have the controller and in the controller we implement implement 2 actions, with different name, and different routes.

/api/speakers and /api/speakers/trimmed are basically two representations of the same resource. It shouldn’t matter what scenario you are in, the endpoint should still be something similar to /api/speakers to the outside world.

In the example below we focus on the output, and not the logic in the action.

        public IActionResult GetSpeakerFull([FromQuery] int speakerId)
           return Ok(new SpeakerModel());
        public IActionResult GetSpeakerTrimmed([FromQuery] int speakerId)
            return Ok(new SpeakerTrimmedDto());

Something like this would be possible without getting an error from the routing mechanism. We know we have a GET request, but which one of the two actions should be selected? We need to help the framework to distinguish between the two of them.

Introducing media types(MIME types)

A media type (also known as a Multipurpose Internet Mail Extensions or MIME type) indicates the nature and format of a document, file, or assortment of bytes. MIME types are defined and standardized in IETF's RFC 6838.

Custom media types

A custom media type is a media type that we can create, often containg vnd as . This media type is significant to the specific business or domain.
You will often find vendor-specific media types used by different businesses as a mean to address specific customers trough the same api. There are hundreds of vendor-specific media types registered with IANA. Such example can be application/vnd.mspowerpoint.
GitHub uses them in their API, and a lot of other businesses.

Media type structure

Media type structure

Responding to media types in controllers

To respond to a specific media type in our action, all you have to do is annotate with the [Consumes("")] attribute, passing the value of your media type.

This attribute will allow us to have actions that return different representations of the same resource.

        public IActionResult GetSpeakerTrimmed([FromQuery] int speakerId)
            return Ok(new SpeakerTrimmedDto());

You will have to send Content-Type : vnd.speaker.trimmed when you make the request, to hit the action.

In a similar manner you can use the [Produces("")] attribute to help the routing mechanism when you want a specific format returned. This attribute will look after Accept header value. Bear in mind that it will work out of the box only with the mime types supported by the framework. Otherwise you will need to create an OutputFormatter, and register it.

In summary

The approach with media types can be used for versioning too. You can read more about versioning your API here.

You can create your custom vendor-specific mime types, and include versions in them. From there you leave the routing mechanism to do its job.

Irina Scurtu
Microsoft MVP, Microsoft Certified Trainer, CTT+, Software Architect, passionate about teaching and training. ASP Net Web Developer - interested in architecture, code quality. Participated in the development and support of various software systems in industries like finance, e-commerce, marketing, media, automotive. Co-Founder of DotNet Iasi group.

Related Articles

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Stay Connected


Subscribe to our newsletter

To be updated with all the latest news, offers and special announcements.

Latest Articles