When in doubt, consult the documentation. Reviewing the W3C definitions for HTTP Status codes, gives us this:
200 OK - The request has succeeded. The information returned with the response is dependent on the method used in the request.
404 Not Found - The server has not found anything matching the Request-URI.
In the context of your API, it very much depends on how queries are created and how objects are retrieved. But, my interpretation has always been that:
- If I ask for a particular object, and it exists return
200
code, if it doesn't exist return the correct 404
code.
- But, if I ask for a set of objects that match a query, a null set is a valid response and I want that returned with a
200
code. The rationale for this is that the query was valid, it succeeded and the query returned nothing.
So in this case you are correct, the service isn't searching for "a specific thing" it is requesting a particular thing, if that thing isn't found say that clearly.
I think Wikipedia puts it best:
200 OK - ... The actual response will depend on the request method used. In a GET request, the response will contain an entity corresponding to the requested resource.
404 Not Found - The requested resource could not be found but may be available again in the future. Subsequent requests by the client are permissible.
Seems pretty clear to me.
Regarding the example requests
/GoalTree/GetByDate?versionDate=...
/GoalTree/GetById?versionId=...
For the format, you said, you always return the nearest revision to that date. It will never not return an object, so it should always be returning 200 OK
. Even if this were able to take a date range, and the logic were to return all objects within that timeframe returning 200 OK - 0 Results is ok, as that is what the request was for - the set of things that met that criteria.
However, the latter is different as you are asking for a specific object, presumably unique, with that identity. Returning 200 OK
in this case is wrong as the requested resource doesn't exist and is not found.
Regarding choosing status codes
- 2xx codes Tell a User Agent (UA) that it did the right thing, the request worked. It can keep doing this in the future.
- 3xx codes Tell a UA what you asked probably used to work, but that thing is now elsewhere. In future the UA might consider just going to the redirect.
- 4xx codes Tell a UA it did something wrong, the request it constructed isn't proper and shouldn't try it again, without at least some modification.
- 5xx codes Tell a UA the server is broken somehow. But hey that query could work in the future, so there is no reason not to try it again. (except for 501, which is more of a 400 issue).
You mentioned in a comment using a 5xx code, but your system is working. It was asked a query that doesn't work and needs to communicate that to the UA. No matter how you slice it, this is 4xx territory.
Consider an alien querying our solar system
Alien: Computer, please tell me all planets that humans inhabit.
Computer: 1 result found. Earth
Alien: Computer, please tell me about Earth.
Computer: Earth - Mostly Harmless.
Alien: Computer, please tell me about all planets humans inhabit, outside the asteroid belt.
Computer: 0 results found.
Alien: Computer, please destroy Earth.
Computer: 200 OK.
Alien: Computer, please tell me about Earth.
Computer: 404 - Not Found
Alien: Computer, please tell me all planets that humans inhabit.
Computer: 0 results found.
Alien: Victory for the mighty Irken Empire!
404 means "the resource you asked for doesn't exist." It's up to the server to decide when that response is appropriate. Google has apparently interpreted it to mean "your API request was malformed." Other REST APIs might also interpret it to mean "your request was well-formed, but you are asking for something which does not exist." This is why you need to read the API docs.
The "server," in turn, is (as your RFC quote indicates) anything that responds to HTTP requests appropriately. If you build a server out of a gigantic web application framework, that's your business. The only requirement is that the client gets the semantically-correct response in any given situation. It will often be the case that the web application is better suited to make that decision than (say) Apache's out-of-the-box behavior, but you can and should set this up in whatever way makes the most sense for your situation.
Best Answer
The short, direct answer
Since the request speaks of executing the list of tasks (tasks are the resource that we're speaking of here), then if the task group has been moved forward to execution (that is, regardless of execution result), then it would be sensible that the response status will be
200 OK
. Otherwise, if there was a problem that would prevent execution of the task group, such as failing validation of the task objects, or some required service isn't available for example, then the response status should denote that error. Past that, when execution of the tasks commences, seeing as the tasks to perform are listed in the request body, then I would expect that the execution results will be listed in the response body.The long, philosophical answer
I suspect that you are experiencing this dilemma because you are diverting from what HTTP was designed for. I suspect that you are attempting to use it as means of RMI (Remote Method Invocation) rather than as means to manage resources.
The RMI perspective is that you would design your URI scheme as you would functions in an application, and upon request, these would execute an action, and then return its result. Although these types of implementations are relatively common still, these often produce situations HTTP is in the way, rather than making things easy.
(Just to note, RMI through HTTP has its merits in some instances, though you'd still be wise to implement these methods in a non-blocking manner. You could maybe offer
startTask
andgetTaskStatus
for instance, both of which would return instantly.)The design of HTTP is asking you to use it to manage resources instead. It wants to express things like "add a task" (via POST), "get a task" (via GET), "delete a task" (via DELETE) and so on. Designing our URI scheme in that way, we rarely find ourselves in conflict with what HTTP has to offer.
To provide an example of what I mean by a URI scheme that conforms to resource management (vs RMI), here's a layout that might work for your case:
/task?complete=[true/false]&start=[start_timestamp]&end=[end_timestamp] ...
GET
searches for tasks according to querystringPOST
adds a single task/task/[id]
GET
responds with a single task's state object/task/[id]/cancellation_request
POST
adds a cancellation request for the task./task/[id]/[property_name]
GET
returns the value of the property of a task of the specified id/task_group?complete=[true/false]&start=[start_timestamp]&end=[end_timestamp] ...
GET
searches for task groups according to querystringPOST
adds a group of tasks/task_group/[id]
GET
responds with a task group object, which includes a list of task objects of all of the tasks in the group.... and so on
As you may have guessed, task execution in this scheme would be an asynchronous thing -- POST to
/task
would not wait until the task has completed, or even until it actually started running -- It would simply queue it for execution and then respond that it succeeded to add that task, or that it failed, if the queue is full, for instance.Note how the URIs have no verbs in them -- They represent resources or collections of resources. The only verbs in this entire scheme are the HTTP methods that are invoked upon these URIs (GET/POST in this case).
Just to hammer this in a little more, URI stands for "Unified Resource Identifier".
Examples of how the above URI scheme would be used
Executing a single task and tracking progress:
POST /task
with the task to executeGET /task/[id]
until response objectcomplete
has positive value while showing current status/progress. You can also implement updates with websocket if you want to avoid polling.Executing a task group and tracking progress:
POST /task_group
with the group of tasks to executeGET /task_group/[groupId]
until response objectcomplete
property has positive value, showing individual task status (3 tasks completed out of 5, for example)