Why learn… about Media Types
Media types are used to communicate the type of data being sent over the web. They are used
- in an HTTP response to declare the type of content being sent
- in requests to declare what sort of content the client can interpret
- in POST requests to declare the type of content being sent.
Basically, wherever there is data on the web, there will be a media type to tell you what sort of data it is. And if there’s not, well there really ought to be.
This article will explain how to interpret a media type and also explain how they are used in HTTP headers to make sure we get the data that we can understand.
- Who is this for?
- Where Have I seen Media Types Before?
- Media Type Syntax
- Media Types in HTTP Message Headers
- Content Negotiation
- Roll Your Own Media Type
- Summary
- Q and A
Who is this for?
This is for web developers who are interested in understanding how to move data over the web. Some knowledge of HTTP, specifically headers, is useful but not necessary.
Where Have I Seen Media Types Before?
The first time I saw a media type was when I wrote my first HTML file - specifically to get some CSS:1
<link rel="stylesheet" src="style.css" type="text/css" />
There’s our first media type - text/css
.
The next place I bumped into them was when I was returning JSON from a server:
response.contentType = "application/json"
There’s another one - application/json
.
So they’re not completely alien to us - they’re a way of saying that “the file is full of CSS”, and “I’m sending you some JSON”. Let’s take a closer look at these two types while investigating the syntax of media types.
Media Type Syntax
A media type is made up of two fields separated by a /
. The first is the type
and the second is the subtype
.
Straight off we can see that CSS (text/css
) has a type of text
, and a subtype of css
. The text
type is very broad - it says that the media is just text, which covers quite a few types of content - content such as:
text/html
- it’s what you’re reading right now!text/plain
- it’s just plain text!text/markdown
- what I’m writing this in which will get turned intotext/html
text/csv
…
you get the idea.
application/json
has a subtype of json
, which is appropriate as, well, it is JSON. The application
type informs us that this is a type that’s meant to be processed by a application. This kinda makes sense, as it’s JSON, which is meant to be read by computers.
(But isn’t CSS only meant to be processed by an application? Yes, it is. Maybe it should have been application/css
. Maybe it should have been text/json
. Who knows. Media types are a bit wonky. The whole web is a bit wonky. Don’t worry about it too much. Part of being a web developer is learning to live with a fair amount of kludge and ambiguity that happened before some of us were born. Learn to live with it.)
Other fun application
types are
application/pdf
for PDFsapplication/zip
for zip files- and many, many more…
All the types
We’ve seen text
and application
types - here’s a list of types that you’re likely to come across with examples:
type | purpose | example |
---|---|---|
application |
media for application consumption | application/json |
audio |
audio media | audio/mp3 |
font |
font formats | font/ttf |
image |
visual media | image/gif |
multipart |
media that needs to be sent in parts | multipart/form-data |
text |
plain text | text/html |
video |
video media | video/mp4 |
Type and subtype are the only parts of a media type that are required, but there are also couple of optional parts.
Parameters
Media types can have a list of parameters tagged on to them after a semicolon. For instance
text/plain;charset=UTF-8
is a text/plain
media type that’s using the UTF-8 character encoding. If we want our sweet emojis to work we need to remember to say we’re using UTF-8, or else the client will assume we’re writing in ASCII like 1970s cavemen.
Parameter types aren’t dictated by the media type specification,2 and you can have as many of them as you like:
text/plain;charset=UTF-8,blog-post=true,author=gypsydave5
They gain their meaning through context; browsers, for instance, will look for and use a charset
parameter when they read a media type over HTTP. More on browsers in a bit.
Structured Syntax Name Suffix
This one’s a bit weird, but it’s also cool, so bear with me. Take a look at this media type:
image/svg+xml
This is the type for SVG images - Scalable Vector Graphics. image
is the type, svg
is the subtype - that’s what we’d expect.
But what’s this +xml
doing at the end? Well, as you may already know, SVGs are written in XML - it’s one of the nicest things about them. So the +xml
is telling us that the syntax of SVG is XML; you could say that XML is the ‘base’ language for SVG. This is called the ‘structured syntax name’, and it’s a nice way of letting us know that, hey, you may not know what SVGs are but, it’s OK, it’s just XML under the hood so you’ll be fine.
(Unless you’re me and you panic everytime you hear the word XML and you find it ridiculous that some people think it’s human readable and you wish that the whole world was represented as JSON, but that’s another story…)
Look, I can tell you’re really excited by all these media types. Now you know how to understand them, you can read the list of all the media types that have been registered with IANA, the Internet Assigned Numbers Authority. Go on, read them all now. I’ll wait.
There’s a lot, right? Try not to panic - you’ll probably only ever use ten of them at most. But did you see the one for SNES ROMs?
Media Types in HTTP Message Headers
The most important place you’ll use media types is in HTTP messages. You’ll use them to describe the media type of the data you want in the Accept
header of an HTTP Request, and the type of data you’re sending in the Content-Type
header of an HTTP Response.
The Accept
Header
The Accept
header is the way a client - like your browser - can tell a server what sort of content it wants.
At its simplest it can look like this:
Accept: application/json
If I put this in the header of my request, I’d be making it very clear to the server that I want JSON back. No questions asked.3
But say you wanted some audio back from the server, but didn’t mind what subtype - audio/mp3
is just as good as audio/wav
for you. In this case you can say:
Accept: audio/*
*
represents a wildcard - it means ‘give me anything’. The server could legitimately reply with an audio
media type with any subtype.
Finally, if you just don’t care what comes back from the server you can just say
Accept: */*
The Content-Type
Header
Content-Type
is the header you should add to an HTTP Response message to tell the client what they’re getting in the response body. This is how the client knows how to interpret the message that you’re sending.
The Content-Type
header should have one media type, so it could be as simple as this:
Content-Type: text/html
Wait, can’t I just use a file extension?
You can imagine that you’ve got some data that you can get to through a URL:
http://gypsydave5/data
Maybe if I wanted to get the data as JSON I could request:
http://gypsydave5/data.json
But if I want XML instead I could do:
http://gypsydave5/data.xml
The benefit being that this is pretty easy to understand if I’m used to using a filesystem - I can just identify the type of file by the extension, and so I can now do the same with a URL.
What’s the harm? Probably the most annoying thing is that, even if you provide an extension as above, you’ll still have to provide a Content-Type
header. This is because, on the web, information about content isn’t meant to be encoded in URLs; it’s meant to be in the Content-Type
header.
Some popular frameworks do this,4 but it’s a bad idea. Don’t do it.
The biggest problem is that you’ll miss out on being able to perform content negotiation.
Content Negotiation
Pretend you’re going to a sandwich shop and your friend asks you to get a sandwich. Sure, you say, what do you want? I don’t know, they say, what do they have? Um… you say, I really don’t know - here’s the menu but they’ve often run out of some of the fillings by this time of day.
OK, says your friend, here’s what I’d like:
“I want tuna mayo or a New Yorker - either is fine. If they don’t have those, then I’ll take an egg salad. And if they don’t have an egg salad, then please, get me anything - I’m starving.”
Doesn’t sound too bad. You can use your friend’s set of sandwich requirements when you get to the sandwich shop to get them a sandwich that they’ll like.
The same thing happens every day on the web, with clients sending a list of media types over to servers to try and make sure that they’ll get something they like. Take a look at this Accept
header of an HTTP Request sent from a web browser:5
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
The browser wants one of the above media types. But it’s got some opinions about which ones it would prefer, which it’s expressing using q
parameters.6 q
parameters have a value between 1
and 0
, and the default value of a q
parameter (when it’s not supplied) is 1
.
Here’s the media types above written out with their q
values.
media type | q value |
---|---|
text/html |
1.0 |
application/xhtml+xml |
1.0 |
application/xml |
0.9 |
*/* |
0.8 |
A series of choices ranked by preference. If you or I were going to the server to get the browser some content, the browser would tell us something like:
“I really want
text/html
andapplication/xhtml+xml
. But, if they don’t have those, I’d go for some sweetapplication/xml
. And if they don’t have that then, whatever, just get me anything.”
Which is really what you’d want a browser to do - you always want to get something back.
Accept: audio/*
Is like we’re being sent to the record store:
“Could you get me the new Nirvana album?7 I don’t care if it’s on vinyl, tape or CD. Hell, 8 Track will do. Just make sure I can listen to it, OK - I don’t just want the poster.”
This is content negotiation - our request to the server has given it our preferences regarding the content type we get back. It can go through those prefences, looking at what content types it can return, and give us the type that matches the most closely.
Content Negotiation and Testing
It’s easier to write tests on the data that builds an HTML page than the HTML itself. If you build your application with content negotiation, you can test the contents of a ‘page’ by requesting the JSON representation rather than the HTML representation.
Roll your own media type
You can invent your own media types, and it’s not as rare as you might think. In fact it’s a powerful technique for writing web APIs. The media type specification has space for subtypes that aren’t registered (and should never be registered). They start with x-
:8
application/x-myapplication-orderstatus+json
This could be the media type for the order status in my application. +json
lets everyone know that it’s written in JSON, but we’ve tightly specified that the content is a part of myapplication
- it’s not just any old JSON.
This is just vanity if you don’t choose to leverage it to pass on additional information about the media type. For instance, if you want to change the structure of the application/x-myapplication-orderstatus+json
media type, you can add a version parameter:9
application/x-myapplication-orderstatus+json;v=1.1
This is very useful when describing the content types that are sent and received across an evolving REST API.
Finally you can register your media type with IANA to let the world know how to interact with your application.
Summary
We’ve learned:
- The syntax of a media type and what they mean.
- How to use a media type in
Content-Type
headers to declare what we’re sending. - How to use them in
Accept
headers to control the content type we get back. - How to use them to perform content negotiation with a server.
Media types are important - they help smooth the path of passing data around on the web. Using them - and using them well - will help make your web applications easier for you others to use and understand.
Q and A
Q: “Wait, I didn’t put a
Content-Type
header on the HTML I sent but my browser still knew it was HTML - what gives?”
A: There are ways of working out the media type of data; go read up on content sniffing.
Q: Wait, don’t you mean MIME types?
No, I don’t. MIME means Multipurpose Internet Mail Extensions and was the first place that media types were used. But since they’re not used solely for ‘internet mail’ (email to you and me), the proper name is ‘media type’. People still tend to use MIME, media and content type interchangeably, but now you know the right answer you can look smug at parties and demand a pay rise.
- This is HTML4 - you don’t need to include the type in HTML5. But that would ruin this perfectly good example.
- Apart from
q
, this one is specified. - In fact the server should send back a 406: Not Acceptable code if it can’t supply the media type asked for… but this rarely happens.
- Active Resource in Rails
- In this case Firefox.
- The
q
stands for quality. I’m not even joking. - I am very much down with popular culture.
- or
x.
. In factx.
is preferred by IANA butx-
is more widely used. - You could also try
application/x-myapplication-orderstatus_1.0+json
- it depends on how you want to parse the version information and what your versioning policy is (how compatible the versions are).