9

I am developing a simple RESTful service for tournaments and schedules. When a tournament is created through a POST request containing a JSON body, the tournament is inserted in a BiMap, declared as follows in a DAO implementation:

private BiMap<String, Tournament> tournaments = Maps.synchronizedBiMap(HashBiMap.create());

When a tournament is created, its associated string id is returned so the user can have future reference of that tournament. He/she can get information back from the new tournament performing the following request:

GET http://localhost:8080/eventscheduler/c15268ce-474a-49bd-a623-b0b865386f39

But what if no tournament with such id is found? So far, I am returning a 204 response. Well, Jersey is doing it for me when returning null from one of its methods. This is the method that corresponds to the route above:

@Path("/{id}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Tournament getTournament(@PathParam("id") String id) {
    Optional<Tournament> optTournament = tournamentDao.getTournament(id);
    if (optTournament.isPresent())
        return optTournament.get();
    return null;
}

My question is: is it OK to return a 204: No Content response, or should it be a 404 response instead, since the resource was not found?

If I should change it to a 404, obvious question: I should change the method signature right? Since now a tournament (of type Tournament) might not be returned, the method should look different. Should I use the Response type as the return type instead?

26

HTTP 204 means that something was found, but it's empty. For instance, imagine that you're serving log files through HTTP, with the requests such as http://example.com/logs/[date-goes-here]. On May 18th, 2015:

  • http://example.com/logs/2015-05-19 would return HTTP 404, which means that there are no logs, because, well, it's difficult to log the future.

  • http://example.com/logs/2015-05-18, however, would return either HTTP 200 with the log entries in the content of the response, or HTTP 204 if the log file was created, but there are still no logs recorded yet for this date.

If you provide null to the framework as a response to a request, it assumes that you found an entry, and this entry is empty, thus HTTP 204. Instead, you should throw new NotFoundException(); to indicate to the framework that the entry doesn't exist, so that it would generate a HTTP 404.

If I should change it to a 404, obvious question: I should change the method signature right?

No, you don't. That's the nice thing about throw new NotFoundException();. It will work no matter what is the actual return type of your method.

  • 5
    Take special note of RFC 2616. 204 responses are only spec-compliant if you are omitting the message body entirely. To some extent, the point of a 204 response is to say, "no, it isn't an accident that I returned no content." To expand on MainMa's example: If a log lookup tool spit out text files (e.g., a thin wrapper around log files that just spits out the log file as-is), a 204 would be appropriate for an empty log file. If the response was an empty JSON object (e.g., {content: ''}), a 204 response would be inappropriate. – Brian Jun 22 '16 at 14:41
  • "because, well, it's difficult to log the future." -- This bit depends on an arbitrary date; why not make it something that doesn't require the reader to pretend it's not today? Maybe using 2015-02-29 would be better, since it's a date that doesn't exist at all? – Nic Hartley Mar 23 '18 at 20:39
3

You should return a 404. You can do it by throwing a NotFoundException (https://jersey.java.net/apidocs/2.6/jersey/javax/ws/rs/NotFoundException.html).

Also please look at this SO question if you need to control the returned content type https://stackoverflow.com/questions/23858488/how-i-return-http-404-json-xml-response-in-jax-rs-jersey-on-tomcat

1

Your request is GET http://localhost:8080/eventscheduler/c15268ce-474a-49bd-a623-b0b865386f39.

If http://localhost:8080/eventscheduler/ does not exist as an endpoint, you should return a 404. You are attempting to access a resource (/eventscheduler/) that does not exist. This would indicate to a client that a server exists on localhost:8080, but there's nothing at the eventscheduler endpoint.

If http://localhost:8080/eventscheduler/ exists as an endpoint but required resources are unavailable, a 5xx error is appropriate. A good example of this would be if a database is offline, where you could return a 503. Of course, you may just want to return a generic 500 error instead of a specific instance.

If http://localhost:8080/eventscheduler/ exists but the thing represented by c15268ce-474a-49bd-a623-b0b865386f39 does not exist, I would return a 200 with a body indicating the details. The endpoint exists, the request made was totally valid and could be processed, but there was no match.

If your client's request to the endpoint was not valid, you would look at the other 4xx errors. You can indicate that the client is not authorized to access the endpoint or the item(s) requested with a 401 or 403 or can use a 400 to indicate that the request is invalid. With any of these, additional information can be provided in the response body.

  • There's no need to distinguish between the endpoint and the resource. If the URI does not match any resources, for whatever reason, the server should return 404. – bdsl Mar 24 '18 at 10:37
  • Check the response code on this site for an example of a site doing the right thing. It's a website, not an API, but the same http spec applies. softwareengineering.stackexchange.com/questions/266183385 – bdsl Mar 24 '18 at 10:40
  • @bdsl I can tell you that is absolutely wrong. For example, I'm interacting with a user service that can return a user profile. Let's say that this endpoint is /user and is used like /user?email=test@example.com. As an API consumer, I want to know if /user for some reason doesn't exist on the server (maybe it was added in v2 of the API and the server is on v1 or it was renamed in v3) or if the user with the email test@example.com doesn't exist. The first is a 404, the second is a 200 with a body that indicates no user with that email address. – Thomas Owens Mar 24 '18 at 10:41
  • @bdsl That's nice, but that doesn't mean it's right or best. I also don't think that you can compare a website that is designed for human interaction through a web browser versus an API designed for use in a software system. – Thomas Owens Mar 24 '18 at 10:43
  • Thomas Owens all those details can go in the body of the 404 response. If I'm using an http client library that throws exceptions on errors and I call /user?email=test@example.com then I want to know that I can deserialise whatever's returned into a user object, and I don't want to have to implement my own check for an error message in the body first. – bdsl Mar 24 '18 at 10:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.