【问题标题】:Is it correct to return 404 when a REST resource is not found?找不到 REST 资源时返回 404 是否正确?
【发布时间】:2014-11-10 14:05:31
【问题描述】:

假设我有一个简单的 (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 或其他更合适的代码?

【问题讨论】:

  • 如果您希望资源在那里,因为它是使用 ID 查找的,那么应该是 404。出现问题,找不到您需要的资源。如果它是一个端点来获取昨天过期的产品列表(例如)但没有,那么 404 将不正确,因为您不能期望总是让产品每天都过期。因此,带有空数组的 200 可能会更好。
  • 我个人更喜欢 204 - 过去,我实际上用对象 data:null, success: false, message: some message 做了 200,前端总是会返回相同的对象,但使用标记某事是否发生(带有消息)。但我来这里是为了找出最佳做法。

标签: rest http-status-codes


【解决方案1】:

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

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

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

这两个问题都可以通过在响应正文中返回额外信息来部分缓解,让调用者确切知道没有找到什么。

【讨论】:

  • 我想补充一点,在/food/1返回404的情况下,因为该网址不存在,这是客户端错误(他输入了错误的网址),因此不是您的问题跨度>
  • @TimCastelijns 我同意这是客户的错误。但是,如果客户是您的客户,那么这将成为您的问题(他们会拿起电话并打电话给某人)。如果我们可以帮助客户使用 API 而不会遇到很多挫折,我们都会赢。
  • 如果您为客户提供了适当的 API 文档,那肯定是他们的错 ;-) 但我明白您的意思
  • 出于几个原因,在这种情况下我更喜欢 204 而不是 404。对于 ops pple,通常 404 敲响了进一步调查的钟声,在许多用例中,只是尚未创建资源,因此还没有内容,就像它多次使用(204)一样,因为在资源。 204 也清楚地表明不会返回任何正文,这对于需要考虑空响应的调用者(客户端)来说非常清楚。对于浏览器,404 多次显示为红色,类似于错误,因此显示为红色。
  • 恕我直言,当 API 端点应该返回资源集合时,您永远不应引发 404 错误。如果没有找到结果,结果应该只是一个空列表。这是解释这种结果的最自然的方式。想象一下,如果每次您的搜索没有产生任何结果时,Google(或任何搜索引擎)都会显示 404 错误。因此,在请求具有 URI 或类似内容的单个资源时,404 错误更合适。
【解决方案2】:

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

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

来自HTTP 1.1 spec

404 未找到

服务器没有找到任何匹配 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,那么 resource 就是 “未找到”

【讨论】:

  • 感谢您的回答!它很棒,因为它非常广泛。这两个答案都非常有帮助,但不幸的是,我只能接受一个...... :(
  • 400 怎么样?
  • @Belun 为什么是 400?
  • url 错误 => 请求错误 (400)
  • @Belun 没有意义。 URL 表示资源的位置。如果未找到该 URL 的资源,则相应的状态为 404“未找到”
【解决方案3】:

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

HTTP 404 Not Found 客户端错误响应代码表示 服务器找不到请求的资源。导致 404 的链接 页面通常被称为损坏或死链接,并且可能受到链接 腐烂。

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

例如,要在 REST 调用期间或其他地方捕获错误,客户端可以使用 catchError() 或 RxJS。

我们可以用这种方式编写代码(示例代码在 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 等等......)非常匹配,但情况并非总是如此。 所以对于这些情况,我宁愿使用更通用的代码来保持它们的简单性,而不是扭曲原始代码:200,400。

【讨论】:

  • @Downvoter 你能分享你的想法吗?它可能很有用。
  • 如果我们想保持安静,那么我认为 API 应该为/foos/{fooId} 之类的路径返回 404,因为在 REST 中,这是一个不存在的资源的 URL。将此与 /foos?id=6 进行比较,其中 URL 指向 foos 资源并且我们正在传递一个过滤器。在这种情况下,可以返回空响应,因为 foos 资源确实存在并且有 0 个实体与过滤器匹配。
  • 如果我们想更新一个不存在的对象怎么办?肯定需要向客户端返回错误
  • @daramasala 正是我对这个 404 与 200 的想法。这取决于端点。
【解决方案4】:

虽然这个问题已经有了一个公认的答案,但我相信这确实是一个固执己见的事情。添加我的两分钱以帮助您对响应代码做出更明智的决定。

404 - 未找到。 (Reference)

源服务器没有找到目标资源的当前表示,或者不愿意透露存在。

资源可能存在而您可能无权查看该资源,也将等同于未找到。因此,在数据不存在的情况下调用 404 是一件非常合适的事情。

现在对于一个不存在的 URL;虽然 404 是一个广泛适用的响应代码,但 400 是一个更合适的代码。

400 - 错误请求 (Reference)

由于某些被认为是客户端错误(例如,格式错误的请求语法、无效的请求消息帧或欺骗性请求路由),服务器无法或不会处理请求。

如果你在请求中输入了一个无效参数,那么响应码是什么? 如果查询参数有错字,响应码应该是什么?

两者的答案都是 400。

大多数文件服务器对于无效 URL 返回 404,因为对于无效 URL,它们会尝试查找在存储中找不到的文件 ~= Resource Not Found

除了 HTTP 状态码之外,响应中还会包含一些关于错误详细信息的信息,其中可以更详细地描述错误并消除歧义。

如果客户端使用无效的 URL 进行调用,则这是一个集成问题,至少在正常情况下应该被捕获。他们绝不会在没有测试和捕获的情况下将代码推送到生产环境中。即使他们这样做了,上帝保佑他们!

tl;dr - 404 表示未找到资源; 400 表示未找到的 URL。

【讨论】:

  • 无权访问资源应返回 403(如果经过身份验证)和 401(如果需要身份验证)而不是 404。
猜你喜欢
  • 2017-06-21
  • 2015-10-01
  • 1970-01-01
  • 2019-05-15
  • 1970-01-01
  • 2020-12-12
  • 2015-04-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多