DEV Community

Irina Scurtu
Irina Scurtu

Posted on • Originally published at irina.codes on

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.

Content-Type

Describes the format of the body of the request that is sent to the server or to the client. 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.
Enter fullscreen mode Exit fullscreen mode

When we talk about APIs orREST-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; }
}
Enter fullscreen mode Exit fullscreen mode

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.

        [HttpGet("{speakerId}")]
        public IActionResult GetSpeakerFull([FromQuery] int speakerId)
        {
           return Ok(new SpeakerModel());
        }

        [HttpGet("{speakerId}")]
        public IActionResult GetSpeakerTrimmed([FromQuery] int speakerId)
        {
            return Ok(new SpeakerTrimmedDto());
        }
Enter fullscreen mode Exit fullscreen mode

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](https://datatracker.ietf.org/doc/html/rfc6838).
Enter fullscreen mode Exit fullscreen mode

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.

        [HttpGet("{speakerId}")]
      [Consumes("vnd.speaker.trimmed")]
        public IActionResult GetSpeakerTrimmed([FromQuery] int speakerId)
        {
            return Ok(new SpeakerTrimmedDto());
        }
Enter fullscreen mode Exit fullscreen mode

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.

Use this wisely, because currently It will cause a swagger loading error. I’m still looking for a way around it. If you know how, drop a comment or a DM

The post Using custom media types in .NET appeared first on Irina Scurtu.

Top comments (0)