在找不到REST资源时返回404是否正确?

时间:2014-11-10 14:05:32

标签: java rest jax-rs http-status-codes jersey-2.0

假设我有一个简单的Jersey REST资源,如下所示:

@Path("/foos")
public class MyRestlet
        extends BaseRestlet
{

    @GET
    @Path("/{fooId}")
    @Produces(MediaType.APPLICATION_XML)
    public Response getFoo(@PathParam("fooId") final String fooId)
            throws IOException, ParseException
    {
        final Foo foo = fooService.getFoo(fooId);

        if (foo != null)
        {
            return Response.status(Response.Status.OK).entity(foo).build();
        }
        else
        {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
    }

}

根据上面的代码,返回NOT_FOUND状态(404)是正确的,还是应该返回204或其他更合适的代码?

非常感谢提前!

3 个答案:

答案 0 :(得分:48)

在这种情况下,404响应非常典型且API用户很容易使用。

一个问题是,由于未找到特定实体,或者由于URI中的结构问题,客户端很难判断他们是否获得了404。在您的示例中,/foos/5可能返回404,因为id = 5的foo不存在。但是,即使存在/food/1的foo,id=1也会返回404(因为foos拼写错误)。换句话说,404表示构造错误的URI或对不存在的资源的引用。

当您拥有引用多个资源的URI时,会出现另一个问题。通过简单的404响应,客户端不知道找不到哪个引用的资源。

通过在响应正文中返回其他信息,让调用者确切知道未找到的内容,可以部分缓解这两个问题。

答案 1 :(得分:19)

是的,对于未找到的资源,返回404是很常见的。就像一个网页,当它找不到时,你会得到一个404.它不仅仅是REST,而是一个HTTP标准。

每个资源都应该有一个URL位置。网址不一定是静态的,可以是templated。因此,实际请求的URL可能没有资源。服务器有责任从模板中分解URL以查找资源。如果他们的资源不存在,那么“Not Found”

这是来自HTTP 1.1 spec

  

404 Not Found

     

服务器未找到与Request-URI匹配的任何内容。没有说明该病症是暂时的还是永久性的。如果服务器通过一些内部可配置的机制知道旧资源永久不可用且没有转发地址,则应该使用410(Gone)状态代码。当服务器不希望确切地说明请求被拒绝的原因,或者没有其他响应适用时,通常会使用此状态代码。


这是204

  

204无内容

     

服务器已完成请求但不需要返回实体主体,并且可能希望返回更新的元信息。响应可以包括实体标题形式的新的或更新的元信息,如果存在,应该与所请求的变体相关联。

     

如果客户端是用户代理,则它不应该从导致请求发送的文档视图中更改其文档视图。此响应主要是为了允许在不导致更改用户代理的活动文档视图的情况下进行操作的输入,尽管任何新的或更新的元信息应该应用于当前在用户代理的活动视图中的文档。

     

204响应绝不能包含消息体,因此总是在头字段之后的第一个空行终止。

通常在更新或创建表示时使用204,并且不需要发回响应主体。在POST的情况下,您可以仅发回新创建的资源的位置。像

这样的东西
@POST
@Path("/something")
@Consumes(...)
public Response createBuzz(Domain domain, @Context UriInfo uriInfo) {
    int domainId = // create domain and get created id
    UriBuilder builder = uriInfo.getAbsolutePathBuilder();
    builder.path(Integer.toString(domainId));  // concatenate the id.
    return Response.created(builder.build()).build();
}

created(URI)将使用Location标头中新创建的URI发回响应。


添加到第一部分。您只需要记住,来自客户端的每个请求都是访问资源的请求,无论是获取资源还是使用PUT进行更新。资源可以是服务器上的任何内容。如果资源不存在,那么一般的响应就是告诉客户端我们找不到该资源。

扩展您的示例。假设FooService压迫数据库。数据库中的每一行都可以被视为资源。并且每个行(资源)都有一个唯一的URL,如foo/db/1可能会找到一个带有主键的行1.如果找不到该ID,那么该资源是< em>“未找到”

答案 2 :(得分:2)

4XX错误代码表示来自客户端的错误 当您将静态资源请求为图像或html页面时,返回404 response有意义:

  

HTTP 404 Not Found客户端错误响应代码表明   服务器无法找到所请求的资源。导致404的链接   页面通常称为断开或死链接,并且可以链接   腐。

当您向客户端提供一些REST方法时,您依赖于HTTP方法,但您不应将REST服务视为简单资源。
对于客户端,REST方法中的错误响应通常处理接近其他处理的错误。

例如,要在REST调用或其他位置捕获错误,客户端可以使用RxJS catchError()

我们可以用这种方式编写一个代码(在TypeScript / Angular 2中作为示例代码),将错误处理委托给一个函数:

return this.http
  .get<Foo>("/api/foos")
  .pipe(
      catchError(this.handleError)
  )
  .map(foo => {...})

问题是任何HTTP错误(5XX或4XXX)都会在catchError()回调中终止。
它可能真的使REST API响应对客户端产生误导。

如果我们与编程语言并行,我们可以将5XX / 4XX视为异常流​​程 一般情况下,我们不会因为找不到数据而抛出异常,我们会在找不到数据时将其抛出并且找不到该数据
对于REST API,我们应该遵循相同的逻辑。

如果找不到实体 ,则在这两种情况下返回OK完全没问题:

@GET
@Path("/{fooId}")
@Produces(MediaType.APPLICATION_XML)
public Response getFoo(@PathParam("fooId") final String fooId)
        throws IOException, ParseException {
    final Foo foo = fooService.getFoo(fooId);

    if (foo != null){
        return Response.status(Response.Status.OK).entity(foo).build();
    }

    return Response.status(Response.Status.OK).build();

}

客户可以根据结果存在或丢失来处理结果 我不认为返回204会带来任何有用的价值 The HTTP 204 documentation声明:

  

客户不需要离开当前页面。

但是请求REST资源,特别是通过GET方法,并不意味着客户端是关于终止工作流程(使用POST / PUT方法更有意义)。

该文件还补充说:

  

常见的用例是作为PUT请求的结果返回204,   更新资源,而不更改页面的当前内容   显示给用户。

我们真的不是这种情况。

经典浏览的一些特定HTTP代码与REST API的返回代码(201,202,401,以及......等)完美匹配,但情况并非总是这样。 因此,对于这些情况,我倾向于使用更通用的代码来简化原始代码:200400