REST API 404:错误的 URI 或缺少资源?

Posted

技术标签:

【中文标题】REST API 404:错误的 URI 或缺少资源?【英文标题】:REST API 404: Bad URI, or Missing Resource? 【发布时间】:2012-04-13 09:55:52 【问题描述】:

我正在构建一个 REST API,但遇到了问题。

似乎在设计 REST API 时公认的做法是,如果请求的资源不存在,则返回 404。

但是,对我来说,这增加了不必要的歧义。 HTTP 404 传统上与错误的 URI 相关联。所以实际上我们是在说“要么你到了正确的地方,但是那个特定的记录不存在,或者互联网上没有这样的位置!我真的不确定哪个一个……”

考虑以下 URI:

http://mywebsite/api/user/13

如果我返回 404,是因为用户 13 不存在吗?还是因为我的网址应该是:

http://mywebsite/restapi/user/13

过去,如果记录不存在,我只是返回带有HTTP 200 OK 响应代码的NULL 结果。这很简单,在我看来非常干净,即使它不一定被接受。但是有没有更好的方法来做到这一点?

【问题讨论】:

可能是重复的。 ***.com/questions/3821663/… 另一个问题似乎与URI查询字符串格式有关。关于 404 的讨论不足以回答我的问题,即是否有更合适或更有用的方法来确定 404 的实际含义。我在发布之前查看了那个。 浏览器concole出现404错误是否正常?当我使用正确的 uri 进行常规操作但找不到资源时。 404 并不表示 URI 错误,它表示未找到资源。那可能是因为没有用户 '13',也可能是因为没有资源 /mywebsite/api。 【参考方案1】:

404 只是 HTTP 响应代码。最重要的是,您可以提供响应正文和/或其他标头,其中包含开发人员将看到的更有意义的错误消息。

【讨论】:

这是有道理的。不过,我不得不怀疑,首先返回 404 与返回空响应的 200 相比,是否真的有任何好处? 如果您返回 200 且为空值,您将违反 HTTP 规范。您可以这样做,但是您的 api 将不遵守 REST 的“统一接口”约束。 ...并且您的响应正文应包含指向有效 URI 的超链接。除了根 URI,也许还有一两个书签,您的客户端应该始终跟随服务器提供给他们的链接。那么就没有必要为他们决定如何在系统外工作而发明详细的语义。 @DarrylHebbes 你是什么意思,我可以在 Chrome 开发者控制台的网络标签中发出请求并查看响应的完整内容。 对于任何返回空结果的查询返回错误(404 或其他)是非常糟糕的。没有数据库会这样做,通常您不希望将空结果作为意外情况报告给用户。示例:您查询您的 api 以检查是否有安排在接下来的 30 分钟内的约会。如果没有,则不应返回 404。请返回 200 OK 和空数组。【参考方案2】:

如果资源不存在,请使用404。不要返回 200 时的正文是空的。

这类似于编程中的undefined 与空字符串(例如"")。虽然非常相似,但肯定有区别。

404 表示该 URI 中不存在任何内容(如编程中未定义的变量)。返回 200 并带有一个空的主体意味着那里确实存在某些东西,并且现在某些东西只是空的(就像编程中的一个空字符串)。

404 并不意味着它是一个“错误的 URI”。有一些用于 URI 错误的特殊 HTTP 代码(例如 414 Request-URI Too Long)。

【讨论】:

嘿,我想你可能正在做点什么。返回“41”之一不是更有意义吗?请求的资源有问题时出错?例如,410 Gone 表示“请求的资源在服务器上不再可用,并且不知道转发地址。” --(参见w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.11) 其实我上面引用的资源也说,“如果服务器不知道,或者没有能力判断条件是否永久,状态码404(不是Found) 应该改用。” 所以也许这也不一定是最好的选择.. 我认为任何 41x 错误都不适合您的用例。我喜欢将未定义字符串与空字符串进行比较,这是我在回答中的观点的更简洁版本。有区别,但这并不意味着空字符串是错误的。打个比方:你可以有一个方法String getName(),它的实现如下:return this.name == null ? "" : this.name。除非您希望客户知道 this.namenull,否则这并没有错。 404 仍然是最合适的选项。您可以(并且被鼓励)使用该 404 响应的主体来通知用户/客户端接收错误的具体细节。请参阅:***.com/a/9999335/105484。在您的情况下,您可能希望使用该正文向用户建议他们重新格式化其 URI 以看起来像 /restapi/user/USER_ID 好的,我可以看到这里的一些优点,但是 4XX 是错误代码,怎么会是错误情况?客户端如何防止 404 发生?【参考方案3】:

与大多数事情一样,“这取决于”。但对我来说,您的做法还不错,并且不违反 HTTP 规范本身。但是,让我们澄清一些事情。

首先,URI 应该是不透明的。即使它们对人不透明,它们对机器也是不透明的。换句话说,http://mywebsite/api/user/13http://mywebsite/restapi/user/13 之间的差异与http://mywebsite/api/user/13http://mywebsite/api/user/14 之间的差异相同,即不相同不是同一时期。所以 404 完全适合 http://mywebsite/api/user/14(如果没有这样的用户),但不一定是唯一合适的响应。

您还可以返回空的 200 响应或更明确的 204(无内容)响应。这将向客户传达其他信息。这意味着http://mywebsite/api/user/14 标识的资源没有内容或基本上什么都没有。这确实意味着存在这样的资源。但是,这并不一定意味着您声称有一些用户持续存在 ID 为 14 的数据存储中。这是您的私人问题,而​​不是发出请求的客户端的问题。因此,如果以这种方式对资源进行建模有意义,请继续。

向您的客户提供信息会带来一些安全隐患,这会使他们更容易猜测合法的 URI。返回 200 未命中而不是 404 可能会给客户一个线索,即至少 http://mywebsite/api/user 部分是正确的。恶意客户端可能只是不断尝试不同的整数。但对我来说,恶意客户端无论如何都能猜到http://mywebsite/api/user 部分。更好的补救措施是使用 UUID。即http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66 优于http://mywebsite/api/user/14。这样做,您可以使用返回 200 的技巧而无需付出太多。

【讨论】:

这是我选择并使用 204 而不是 404 的解决方案。对我来说,204 意味着“我找到了你的代码,但没有找到结果”,而 404 意味着“我找不到找到要执行的代码,这是错误的路径吗?”【参考方案4】:

404 Not Found 在技术上意味着 uri 当前没有映射 到资源。在您的示例中,我将向 http://mywebsite/api/user/13 的请求解释为返回 404 以暗示此 url 从未 映射到资源。对客户来说,这应该是谈话的结束。

要解决歧义问题,您可以通过提供其他响应代码来增强您的 API。例如,假设您希望允许客户端发出 GET 请求 url http://mywebsite/api/user/13,您希望传达客户端应该使用规范 url http://mywebsite/restapi/user/13。在这种情况下,您可能需要考虑通过返回 301 Moved Permanently 并在响应的 Location 标头中提供规范 URL 来发出永久重定向。这告诉客户端对于未来的请求,他们应该使用规范的 url。

【讨论】:

【参考方案5】:

这是一个非常古老的帖子,但我遇到了类似的问题,我想与大家分享我的经验。

我正在使用其他 API 构建微服务架构。我有一些其他的 GET 服务,它们根据请求参数从后端系统收集数据。

我遵循其余的 API 设计文档,当没有符合查询条件的数据(例如选择零记录)时,我向客户端发送了带有完美 JSON 错误消息的 HTTP 404。

当没有数据发送回客户端时,我准备了一个带有内部错误代码等的完美 JSON 消息,以告知客户端“未找到”的原因,并通过 HTTP 将其发送回客户端404. 效果很好。

后来我创建了一个 rest API 客户端类,它是一个隐藏 HTTP 通信相关代码的简单助手,当我从我的代码中调用我的 rest API 时,我一直使用这个助手。

但我需要编写令人困惑的额外代码,因为 HTTP 404 有两个不同的功能:

当 REST API 在给定的 url 中不可用时,真正的 HTTP 404 由运行 REST API 应用程序的应用服务器或 Web 服务器抛出 根据查询的 where 条件,当数据库中没有数据时,客户端也会返回 HTTP 404。

重要提示:我的 rest API 错误处理程序会捕获后端服务中出现的所有异常,这意味着如果出现任何错误,我的 rest API 总是会返回带有消息详细信息的完美 JSON 消息。

这是我的客户端辅助方法的第一个版本,它处理两个不同的 HTTP 404 响应:

public static String getSomething(final String uuid) 
    String serviceUrl = getServiceUrl();
    String path = "user/" + , uuid);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_UTF8)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) 
        // HTTP 200
        return response.readEntity(String.class);
     else 
        // confusing code comes here just because
        // I need to decide the type of HTTP 404...

        // trying to parse response body
        try 
            String responseBody = response.readEntity(String.class);
            ObjectMapper mapper = new ObjectMapper();
            ErrorInfo errorInfo = mapper.readValue(responseBody, ErrorInfo.class);

            // re-throw the original exception
            throw new MyException(errorInfo);

         catch (IOException e) 
            // this is a real HTTP 404
            throw new ServiceUnavailableError(response, requestUrl, httpMethod);
        

    // this exception will never be thrown
    throw new Exception("UNEXPECTED ERRORS, BETTER IF YOU DO NOT SEE IT IN THE LOG");

但是,因为我的 Java 或 javascript 客户端可以以某种方式接收两种 HTTP 404,所以我需要在 HTTP 404 的情况下检查响应正文。如果我可以解析响应正文,那么我我确定我收到了没有数据可发送回客户端的响应。

如果我无法解析响应,则意味着我从 Web 服务器(而不是从其他 API 应用程序)返回了一个真正的 HTTP 404。

这太令人困惑了,客户端应用程序总是需要做额外的解析来检查 HTTP 404 的真正原因。

老实说,我不喜欢这种解决方案。很混乱,需要一直向客户端添加额外的废话代码。

因此,我决定在这两种不同的场景中使用 HTTP 404,而不是使用以下方法:

我不再在我的 REST 应用程序中使用 HTTP 404 作为响应 HTTP 代码。 我将使用 HTTP 204(无内容)而不是 HTTP 404。

在这种情况下,客户端代码可以更优雅:

public static String getString(final String processId, final String key) 
    String serviceUrl = getServiceUrl();
    String path = String.format("key/%s", key);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    log(requestUrl);

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_JSON_UTF8)
            .header(CustomHttpHeader.PROCESS_ID, processId)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) 
        return response.readEntity(String.class);
     else 
        String body = response.readEntity(String.class);
        ObjectMapper mapper = new ObjectMapper();
        ErrorInfo errorInfo = mapper.readValue(body, ErrorInfo.class);
        throw new MyException(errorInfo);
    

    throw new AnyServerError(response, requestUrl, httpMethod);

我认为这可以更好地解决这个问题。

如果您有更好的解决方案,请与我们分享。

【讨论】:

Apache HttpClient 方法不会对 204 响应抛出异常。如果您的客户只是回答业务对象(模型),那么这将返回 null。有效,但最终用户很难检测到 204 情况。 一切都很好,但问题是:您多久为 404 编写 if 语句?那会是什么?另一方面,如果您调用 api,其中定义了可能的 404 错误 json,您可以针对这种情况进行处理。 zappee 我同意你的看法。我有一个返回数据的服务。但也有数据被破坏的情况。请求 URL 是正确的(所以不是 404),但它并不是真正的逻辑错误(500),所以 204 似乎是最合适的。缺少消息正文真的很烦人。虽然可以说我可以在标题中插入一个原因。【参考方案6】:

因此,从本质上讲,答案可能取决于请求的形成方式。

如果根据对http://mywebsite/restapi/user/13 的请求,请求的资源构成 URI 的一部分并且用户 13 不存在,那么 404 可能是合适且直观的,因为 URI 代表不存在的用户/实体/文档/等等。使用 GUID http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66 和上面的 api/restapi 参数的更安全的技术也是如此。

但是,如果请求的资源 ID 包含在请求标头中[包括您自己的示例],或者实际上,作为参数包含在 URI 中,例如 http://mywebsite/restapi/user/?UID=13,那么 URI 仍然是正确的(因为用户确实在http://mywebsite/restapi/user/) 处退出;因此可以合理地预期响应为 200(带有适当详细的消息),因为称为 13 的特定用户不存在,但 URI 存在。这样我们说 URI 是好的,但是数据请求没有内容。

个人感觉 200 仍然不正确(尽管我之前曾认为它确实如此)。例如,当发送不正确的 ID 时,200 响应代码(没有详细响应)可能会导致无法调查问题。

更好的方法是发送204 - No Contentresponse。这符合 w3c 的描述 *服务器已完成请求,但不需要返回实体主体,并且可能希望返回更新的元信息。*1 在我看来,混乱是由 Wikipedia 条目声明 引起的204 No Content - 服务器成功处理了请求,但没有返回任何内容。 通常用作对成功删除请求的响应最后一句话很有争议。考虑没有那句话的情况,解决方案很简单——如果实体不存在,只需发送 204。甚至还有返回 204 而不是 404 的参数,请求已被处理并且没有返回任何内容!请注意,204 不允许响应正文中的内容

来源

http://en.wikipedia.org/wiki/List_of_HTTP_status_codes 1.http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

【讨论】:

【参考方案7】:

这篇古老但出色的文章...http://www.infoq.com/articles/webber-rest-workflow 对此进行了说明...

404 Not Found - 该服务太懒惰(或安全),无法为我们提供请求失败的真正原因,但无论是什么原因,我们都需要处理它。

【讨论】:

【参考方案8】:

这是我们团队最近提出的。 我们使用带有消息正文的404 Not found204 No Content 基于以下理性。

如果请求 URI 指示单个资源的位置,我们使用 404 Not found。当请求查询一个 URI 时,我们使用 204 No Content

当用户 13 不存在时,http://mywebsite/api/user/13 将返回 404 http://mywebsite/api/users?id=13 将返回 204 no content http://mywebsite/api/users?firstname=test 将返回 204 no content

这里的想法是,“查询路线”预计不会返回任何内容。

无论您选择哪种模式,最重要的是保持一致 - 所以请从您的团队那里获得支持。

【讨论】:

【参考方案9】:

统一资源标识符是指向资源的唯一指针。格式不佳的 URI 不会指向资源,因此对其执行 GET 不会返回资源。 404 表示服务器没有找到任何匹配 Request-URI 的东西。如果您输入了错误的 URI 或错误的 URI,这就是您的问题,也是您无法访问 HTML 页面或 IMG 资源的原因。

【讨论】:

【参考方案10】:

对于这种情况,HTTP 404 是来自 REST API 的响应的响应代码 Like 400, 401, 404, 422 无法处理的实体

使用异常处理来检查完整的异常消息。

try
  // call the rest api
 catch(RestClientException e) 
     //process exception
     if(e instanceof HttpStatusCodeException)
        String responseText=((HttpStatusCodeException)e).getResponseBodyAsString();
         //now you have the response, construct json from it, and extract the errors
         System.out.println("Exception :" +responseText);
     


此异常块为您提供 REST API 抛出的正确消息

【讨论】:

以上是关于REST API 404:错误的 URI 或缺少资源?的主要内容,如果未能解决你的问题,请参考以下文章

以 %20 结尾的 WCF REST URI 失败并出现 404

Wordpress REST API (wp-api) 404 错误:无法访问 WordPress REST API

春季启动 REST API 404 错误

Yii2:REST API - 404 错误

spring boot rest api测试得到404错误

运行 REST API 时找不到 Tomcat 错误 404