1

In REST using HTTP, are status codes relevant to the request, or the resource?

In non-REST HTTP request/response calls, the status code refers to the success/failure/etc of the request being made. Did the request fail because the client sent an invalid request? Then return 400, and so on.

But with REST, the URI is stated to refer to a "resource", and the status code often appears to be used to refer to the resource (i.e. it's metadata about the resource). But if the request to retrieve the resource fails, how does the client know if there's a problem with the resource, or just a problem with the request to GET the resource?

My question is - have I understood that correctly? And if so, what happens if the request fails server-side?

Example:

  • Client calls POST /api/order/ with a payload.
  • Server validates the request and enqueues it before immediately returning 202 Accepted with Location: /api/order/43573/status and Retry-After: 60.
  • Client waits the requisite 60 seconds before polling GET /api/order/43573/status.
  • Server returns 202 Accepted, including a payload with more information and Retry-After: 60.
  • (The waiting and polling continues until processing completes.)
  • One of two things happens:
    • Success
      • Background processing succeeds.
      • Client polls GET /api/order/43573/status.
      • Server returns 303 See Other, with Location: /api/order/43573.
      • Client calls GET /api/order/43573.
      • Server returns 200 OK with appropriate payload.
    • Failure
      • Background processing fails for some reason.
      • Client polls GET /api/order/43573/status.
      • Server returns 200 See Other, with payload indicating failure.

My problem is that last request - returning 200 OK seems right for the web request, but the only body it'll ever contain is information about failure.

Neil Barnwell
  • 41,080
  • 29
  • 148
  • 220
  • Facebook returns 404 on a profile you've been blocked on. It really depends on how honest you want to be and who the client is, I think. It this a public api, or a secure one? Will it potentially be scanned for vulnerabilities? I think the idea here is not so much what is factually true matters, but what you need to communicate in order to keep things running smoothly. It could be said this question is a bit philosophical. – Any Day May 21 '21 at 10:40
  • I'm not sure that analogy is close enough to my example to answer my (entirely non-philosophical) question. There is presumably a "correct" way to interpret this stuff according to the REST spec, I just don't know it well enough and haven't been able to work it out for myself. – Neil Barnwell May 21 '21 at 10:51
  • This is not a question about REST but about HTTP as REST just states that standardized media types/representation formats should be used to exchange state information and that through certain abstraction mechanism client and server should get decoupled. HTTP is actually the transport layer used to exchange documents and as such the response code is tide to the response issues for a request. – Roman Vottner May 21 '21 at 10:51
  • The question is about REST OVER HTTP. I want to know what HTTP status codes refer to, because I have seen them used to represent the status of the resource indicated by the URI (example: https://learn.microsoft.com/en-us/azure/architecture/patterns/async-request-reply), but as I stated there are issues with that. – Neil Barnwell May 21 '21 at 11:05
  • 1
    [Jim Webber](https://www.youtube.com/watch?v=aQVSzMV8DWc&t=191s) gave a great talk on how we should think of "REST calls" and the like. As such, HTTP response codes are nothing more than coodrination data, according to Jim, that gives clients a clue on the outcome of a request and lets them act accordingly. – Roman Vottner May 21 '21 at 11:06

2 Answers2

2

TL;DR

a HTTP response code belongs to the response issued for a request and not the resource itself


Unfortunately there is a widespread misunderstanding of what REST is and what it isn't.

As Jim Webber correctly pointed out, HTTP is an application protocol whose domain is the transfer of documents over a network and any business activities we deduce are just side effect of the actual document management. It is therefore our task to narrow down these side-effects to something useful.

REST or more precisely the REST architecture is a set of constraints that mainly deal with the decoupling of clients from servers by relying on standardized document types / representation formats and on an interaction model that is similar to the one used on the Web. The main goal here is to allow server to evolve freely in future without having to fear that introduced changes will break clients, as they only operate on standardized document formats they'll understand and are able to process. Through careful design and focus on above mentioned standardized formats the interoperability between different systems should be guaranteed.

In example, REST puts a strong focus on how server teach clients on what they can do next. Jim Webber compared it to a text based computer game where you are given options on what to do next. He later on gave also the example of a typical Web based Amazon-like checkout where you play along a given predefined checkout protocol until you reached the end of that state-machine in some way or the other.

A strong hint that a HTTP response code belongs to the response issued for a request and not the resource itself is, when you i.e. have resource hidden behind some authentication. In the first request you do not include an Authorization HTTP request header which then results in a 401 Unauthorized status code. Your browser will now ask you for a username and password and upon entering it the same reuqest will be reissued but this time with the Authorization header set which then might succeed, depending on your input and/or the server implementation.

what happens if the request fails server-side?

HTTP is based upon a request-response model meaning that for each request a response must be issued. As mentioned before, the HTTP status code returned here acts as coordination metadata that allows a client to act upon. I.e. with the above example with the 401 Unauthorized status code the browser (client) asked for credentials to send along and reissued the request.

Further, each of the HTTP operations used contain some defined properties a client can assume to hold true. Whether a server sticks to these is a different story, but a well-behaved server should. Such properties are safety and idempotency. The primer one does guarantee that a resource state wont be change when a request issued with such an operation is processed. Prominent examples here are GET and HEAD which are used to retrieve docuements. Itempotency is a property that is useful in case of a network connection issue and the client can't be sure whether the request reached the server at all or just the response got lost mid-way. It basically allows a server to resend the request without further thinking as the outcome here should be the same regardless if the initial request was processed at the server or not. PUT and DELETE are often used as example here.

Other HTTP operations such as POST or PATCH don't have such properties, unfortunately. So in case of a network issue the client can't be sure whether a request can be issued automatically. I.e. in case of a network issue while performing an order and later on resending the request, a server might have taken the order twice. As such, if something cruicial should be performed, such a payment, an expansive order or the like it is probably better to use an idempotent operation i.e. by using a POST-PUT creation pattern.

A further solution to prevent processing of certain requests my be to send them as conditional requests containing either ETag, If-Modified-Since or similar request headers. This though usually is used if certain unsafe operations should be performed on very specific resource states. I.e. a patch is usually dependent on the current, checkout version. If some changes are done to the remote state in the meantime, we usually don't want the patch to be applied to the altered version and as such want the request to fail which forces us to download the new state and modify the request in a way so that the desired outcome will be as desired.

According to your question:

While it seems natural to combine 202 Accepted with a Location response header, unfortunately RFC 7231 does not mention the location header as requirement for a 202 Accepted response nor does the definition of the Location header state that it can be used in a 202 Accepted response and as such, according to a response from Roy Fielding, a Location header outside of 201 Created and 3xx repsonses the Location header has no defined meaning. Instead of returning a 202 Accepted initially, you could have returned a 303 See Other response code containing a Location header for the status of the resource, which then could have returned a 202 Accepted status and later on perform a redirect to the final state as you did.

My problem is that last request - returning 200 OK seems right for the web request, but the only body it'll ever contain is information about failure.

According to RFC 7231

The representation sent with this response ought to describe the request's current status and point to (or embed) a status monitor that can provide the user with an estimate of when the request will be fulfilled.

So, in case the processing of a long-running process (partially) failed, the payload itself could indicate a failed processing to start with. You might also perform a redirect to a resource that then returns a 4xx status code containing i.e. a application/problem+json response payload giving hints on the reason why processing failed and so on.


I feel like 202 isn't appropriate for GET requests, so I would return 200 with a status payload until such time that returning a redirect makes sense?

The actual definition of 202 Accepted is

The 202 (Accepted) status code indicates that the request has been accepted for processing, but the processing has not been completed. The request might or might not eventually be acted upon, as it might be disallowed when processing actually takes place. There is no facility in HTTP for re-sending a status code from an asynchronous operation.

The 202 response is intentionally noncommittal. Its purpose is to allow a server to accept a request for some other process (perhaps a batch-oriented process that is only run once per day) without requiring that the user agent's connection to the server persist until the process is completed. The representation sent with this response ought to describe the request's current status and point to (or embed) a status monitor that can provide the user with an estimate of when the request will be fulfilled.

So, depending on how you interpret the request has been accepted for processing part you can return a 202 Accepted for a GET request, as the actual process might still not have been finished, or return a 200 OK response. I think the important part here is though the usage of a media type (a.k.a. representation format) a client understands and knows how to process and interpret. Based on that response a client should know whether the actual process completed with or without failures or is still pending. I have to admit though that I am currently unaware of any suitable media types that can be used to express such knowledge.

Roman Vottner
  • 12,213
  • 5
  • 46
  • 63
  • This is excellent, thank you for taking the time. I'm with you throughout, and it's confirmed how I originally thought things "should" be done. I've seen many examples on the web that appear to misuse status codes which has caused some confusion. I do have one question on this point: "which then could have returned a 202 Accepted status and later on perform a redirect to the final state as you did". I feel like 202 isn't appropriate for GET requests, so I would return 200 with a status payload until such time that returning a redirect makes sense? Otherwise this is really helpful, thanks. :) – Neil Barnwell May 21 '21 at 13:45
  • By the way, if you wanted to include a TL;DR to your already superb answer, it should be this: "a HTTP response code belongs to the response issued for a request and not the resource itself". That is the most concise answer to the question I was trying to pose. :) – Neil Barnwell May 21 '21 at 13:47
1

The status-code element is a 3-digit integer code describing the result of the server's attempt to understand and satisfy the client's corresponding request. The rest of the response message is to be interpreted in light of the semantics defined for that status code. -- RFC 7230

That's always true. It's part of the uniform interface constraint that we all use the same self descriptive messages, regardless of the target resource.

But with REST, the URI is stated to refer to a "resource", and the status code often appears to be used to refer to the resource (i.e. it's metadata about the resource).

I think you've been mislead here. The motivation for the uniform interface is that we can use general purpose components (browsers, caches) in our web applications because all servers use the same messages the same way. That would go right out the window if "REST" resources had different message semantics than the "non-REST" sort.


My problem is that last request - returning 200 OK seems right for the web request, but the only body it'll ever contain is information about failure.

You should probably review Jim Webber's 2011 talk. In summary: HTTP is an application protocol, the domain of that application is the transfer of documents over a network. The status line and headers are metadata for the "transfer of documents over a network" domain.

On the web, we transfer documents ("web pages") for the human beings to read. The audience for the metadata is the browser, not the human. Uniform interface means that you can use the same browser to do your banking, book shopping, track your sales funnel, and ask programming questions . It's the metadata that allows the components of the HTTP application to do useful work (ex: cache-invalidation).

So in your case, you are returning a document (technically: a representation of a resource) that describes the progress of a side effect of an earlier request, presumably with headers that describe how caches can re-use the response.

Community
  • 1
  • 1
VoiceOfUnreason
  • 52,766
  • 5
  • 49
  • 91