Responding appropriately with a 415 error in a RESTful web app - rest

I am building a web application which can accept resource representations via PUT, POST and PATCH in both x-www-form-urlencoded and JSON formats. If I receive a request body in another format, I would like to send a 415 response, plus some additional data declaring what formats I do accept (in a similar manner to the 405 response's mandatory Allow: header). I have seen on one answer at HTTP 406 and 415 error codes where the person answering did not know if there was such a mechanism defined, RFC 2616 mentions nothing in this regard, and some cursory Googling turns up nothing either.
I would like to just use Accept: even though that is defined as a request header. It seems most appropriate just to re-use it for this response. Do folks agree? Does anyone have a better suggestion?
Edit: I have since found Specify supported media types when sending "415 unsupported media type" which asks specifically if there is a standard for this. The correct and accepted answer was basically no, but the respondant there also had the same idea as me, that Accept would be a good header to use to provide this information. This prompted a message from Julian Reschke to the HTTP Working Group asking if it should be defined what sending an Accept: header in a response should mean. That email only received one response, which agreed that it was needed and that Accept seemed appropriate.
Note that I am not asking if I am permitted to send an Accept header, any header is allowed to be sent in either direction, but only those defined in the spec have meaning (semantics) to intermediaries, and also, any unexpected headers not prefixed with X- might clash with future versions of HTTP. This doesn't bother me.

As you said, Accept is a request-header. It is wrong to use it in a response.
To quote Wikipedia,
Content negotiation is a mechanism defined in the HTTP specification that makes it possible to serve different versions of a document (or more generally, a resource representation) at the same URI, so that user agents can specify which version fit their capabilities the best.
So it is the client who says in the request what media types he can Accept. If the server is not able to deliver this media type he responds with 406 Not Acceptable.
Since the client must be able to deal with the returned representation of the requested resource, it should specify which media types it can understand. It can specifiy multiple media types:
Accept: application/json, application/xml, x-www-form-urlencoded
If the client really wants to accept any media type, it can set
Accept: */*
The server will set a proper Content-Type response header even for such a request.

You receiving an unrecognized Content-Type will most probably be from a developer currently implementing a client to your service. Since there does not exist a mechanism for a server to advertise its supported content types, you might as well mention in the message body what types you do support.

I'm not aware of any standard mechanism to do this but you may be able to slightly re-purpose the Alternates header http://www.ietf.org/rfc/rfc2295.txt to do what you want.

Related

REST: Why version with headers using vnd and not adding new header for versioning?

In REST API, I was checking recommendation to version API with accept header as:
Accept: application/vnd.com.myservice.v2+json
Now, server can extract this information and send v2 response. Why are we sending vnd.com.myservice.v2 in accept header where we should only send Accept: application/json? Why shouldn't we create separate header for this?
Accept is a standard header and is used in media type negotiation. The media type determines how a resource is represented over the wire. Ultimately, that is the API version - just representation of resource.
Accept also has other useful semantics that a custom header doesn't. For example, Accept allows for multiple, quality (e.g. weighted) media types. A client could ask for:
accept: application/vnd.com.myservice.v2+json;q=0.8, application/vnd.com.myservice.v1+json
This indicates to the server that the client prefers V2 of the JSON format with a 80% weight. If it's not available, then the client will also accept V1.
A media type can also use custom parameters. The following is also valid:
accept: application/json;q=0.8;v=2.0, application/json;v=1.0
This says the same thing, but uses a standard media type with a custom parameter instead of a custom media type. This approach is more generic and generally easier to consume in web stacks (in my experience).
I hope that helps.

Accept-Language header - prefer some language but accept all

I investigated postman's request headers and they have this as an accept-language value
'accept: '*/*'
'accept-language': 'en-US,en;q=0.8',
so I also used this in my app.
Is this enough if I want the server to accept English but also accept any other language besides English?
Let's analyse it one after the other.
Accept header is to
specify certain media types which are acceptable for the response
So right now you are accepting all media types (text/plain, text/html and so on) and it has nothing to do with language.
Accept-language restricts
the set of natural languages that are preferred as a
response to the request
And
if the header field is present in a request and none of the available
representations for the response have a matching language tag, the
origin server can either disregard the header field by treating the
response as if it is not subject to content negotiation or honor the
header field by sending a 406 (Not Acceptable) response. However, the
latter is not encouraged, as doing so can prevent users from accessing
content that they might be able to use (with translation software, for
example).
Theoretically 406 response code is possible (but it's unlikely), so better specify your header as en-US,en;q=0.8,*;q=0.7

For REST services that consume multiple formats, is there a generally accepted default Content Type?

If you have a REST service that accepts multiple formats:
JSON
XML
HTML form data
Is there a widely accepted 'default' Content Type or:
You pick it yourself based on the most frequent / common use case
Don't accept a missing content type, require it explicitly by consumer
For example, according to W3C the default content type for POST via HTML, is application/x-www-form-urlencoded.
I strongly suggest that a server should reject a request that has a missing or inappropriate Content-Type header. RFC 7231 Has an explicit code for such:
6.5.13. 415 Unsupported Media Type
The 415 (Unsupported Media Type) status code indicates that the
origin server is refusing to service the request because the payload
is in a format not supported by this method on the target resource.
The format problem might be due to the request's indicated
Content-Type or Content-Encoding, or as a result of inspecting the
data directly.
Even though it doesn't explicitly mention a missing Content-Type, this is the accepted practice. See: HTTP status code for unaccepted Content-Type in request
Just as you should send the content-type in a response, you should also expect to have a content-type in the request.
Also, it's quite common to expect the correct content-type, see Jira REST API for instance:
Make sure the content type in the request is set to 'application/json', as shown in the example.
Or Twilio, where they have a list of accepted content-type and say:
If the content-type header does not match the media, Twilio will reject the request.
And I'm pretty sure that also the Outlook Mail REST API needs it to be correctly set.
So, yes, I'd say:"Don't accept a missing content type".

What is the correct fallback if content negotiation fails?

What is the correct fallback if Content-Negotiation does not find a reasonable result due to a non 2xx status code? For example:
A client wants to download a PDF and sends following header Accept: application/pdf. Due to insufficient privileges the server would return a 403 Forbidden. You might want to explain the reason in more detail but it does not make much sense to return a PDF. How would you deal with that?
Should the server return an empty body? Should he ignore the Accept header and send some other representation like text/plain. Or is it the job of the client to provide alternatives per Accept: application/pdf, text/plain, */*.
Obviously if the user does not have permission to access the resource, there is no real alternative what a server can do. The server can however respond with content (even in an error case) which describes the error in more detail, if that response can be made using a media-type accepted by the client. The client should provide an Accept: header that describes all the media-types it can parse. So an automatic client would be able to do:
Accept: application/pdf, application/vnd.myapi.error
If content-negotiation fails, the returned code should be 406 Not Acceptable. The server can produce this if it can not provide any answer which would be acceptable to the client.
However, the specification states that the server may actually return an answer not explicitly acceptable by the client if it wants to, it is up to the client, to inspect the response headers to see what media-type the answer is.
Source: https://www.rfc-editor.org/rfc/rfc7231#section-3.4.1

Can I change the headers of the HTTP request sent by the browser?

I'm looking into a restful design and would like to use the HTTP methods (POST, GET, ...) and HTTP headers as much as possible. I already found out that the HTTP methods PUT and DELETE are not supported from the browser.
Now I'm looking to get different representations of the same resource and would like to do this by changing the Accept header of the request. Depending on this Accept header, the server can serve a different view on the same resource.
Problem is that I didn't find a way to tell my browser to change this header.
The <a..> tag has a type attribute, that can have a mime type, looked like a good candidate but the header was still the browser default (in Firefox it can be changed in about:config with the network.http.accept.default key).
I would partially disagree with Milan's suggestion of embedding the requested representation in the URI.
If anyhow possible, URIs should only be used for addressing resources and not for tunneling HTTP methods/verbs. Eventually, specific business action (edit, lock, etc.) could be embedded in the URI if create (POST) or update (PUT) alone do not serve the purpose:
POST http://shonzilla.com/orders/08/165;edit
In the case of requesting a particular representation in URI you would need to disrupt your URI design eventually making it uglier, mixing two distinct REST concepts in the same place (i.e. URI) and making it harder to generically process requests on the server-side. What Milan is suggesting and many are doing the same, incl. Flickr, is exactly this.
Instead, a more RESTful approach would be using a separate place to encode preferred representation by using Accept HTTP header which is used for content negotiation where client tells to the server which content types it can handle/process and server tries to fulfill client's request. This approach is a part of HTTP 1.1 standard, software compliant and supported by web browsers as well.
Compare this:
GET /orders/08/165.xml HTTP/1.1
or
GET /orders/08/165&format=xml HTTP/1.1
to this:
GET /orders/08/165 HTTP/1.1
Accept: application/xml
From a web browser you can request any content type by using setRequestHeader method of XMLHttpRequest object. For example:
function getOrder(year, yearlyOrderId, contentType) {
var client = new XMLHttpRequest();
client.open("GET", "/order/" + year + "/" + yearlyOrderId);
client.setRequestHeader("Accept", contentType);
client.send(orderDetails);
}
To sum it up: the address, i.e. the URI of a resource should be independent of its representation and XMLHttpRequest.setRequestHeader method allows you to request any representation using the Accept HTTP header.
Cheers!
Shonzilla
I was looking to do exactly the same thing (RESTful web service), and I stumbled upon this firefox addon, which lets you modify the accept headers (actually, any request headers) for requests. It works perfectly.
https://addons.mozilla.org/en-US/firefox/addon/967/
I don't think it's possible to do it in the way you are trying to do it.
Indication of the accepted data format is usually done through adding the extension to the resource name. So, if you have resource like
/resources/resource
and GET /resources/resource returns its HTML representation, to indicate that you want its XML representation instead, you can use following pattern:
/resources/resource.xml
You have to do the accepted content type determination magic on the server side, then.
Or use Javascript as James suggests.
ModHeader extension for Google Chrome, is also a good option. You can just set the Headers you want and just enter the URL in the browser, it will automatically take the headers from the extension when you hit the url. Only thing is, it will send headers for each and every URL you will hit so you have to disable or delete it after use.
Use some javascript!
xmlhttp=new XMLHttpRequest();
xmlhttp.open('PUT',http://www.mydomain.org/documents/standards/browsers/supportlist)
xmlhttp.send("page content goes here");