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

Bri*_*acy 190 rest web-services http

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

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

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

考虑以下URI:

http://mywebsite/api/user/13
Run Code Online (Sandbox Code Playgroud)

如果我得到404,那是因为用户13不存在吗?或者是因为我的网址应该是:

http://mywebsite/restapi/user/13
Run Code Online (Sandbox Code Playgroud)

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

Rob*_*evy 83

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

  • 如果返回带有null的200,则违反HTTP规范.你可以这样做,但是你的api不会遵守REST的"Uniformed Interface"约束. (11认同)
  • 这是有道理的.不过,我不得不怀疑,首先返回404实际上是否获得了任何好处,而不是返回带有空响应的200? (6认同)
  • ...并且您的响应正文应包含指向有效URI的超链接.除了根URI,也许还有一两个书签,您的客户端应始终*遵循服务器给他们的链接.然后,没有必要发明关于他们决定如何在系统外工作的详细语义. (5认同)
  • 对于任何返回空结果的查询返回错误(404 或其他)是非常糟糕的。没有数据库会这样做,并且通常您不希望将空结果作为意外情况报告给用户。示例:您查询 api 以检查接下来 30 分钟是否有预约。如果没有,您不应该返回 404。请返回 200 OK 和一个空数组。 (3认同)

nat*_*ood 49

404如果资源不存在,请使用.不要200带空身回来.

这类似于编程中的undefinedvs空字符串(例如"").虽然非常相似,但肯定存在差异.

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

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

  • 我认为任何41x错误都不适合您的用例。我喜欢将未定义字符串与空字符串进行比较,这是我的回答更简洁的版本。有所不同,但这并不意味着空字符串是错误。打个比方:您可以使用一个具有以下实现的方法`String getName()`:`return this.name == null?“”:this.name`。除非您希望客户端知道`this.name`为`null`,否则这不是不正确的。 (2认同)
  • 404仍然是最合适的选择。您可以(并且我们鼓励您)使用 404 响应的正文来通知用户/客户端接收错误的具体详细信息。请参阅:http://stackoverflow.com/a/9999335/105484。在您的情况下,您可能希望使用该正文来建议用户将其 URI 重新格式化为 /restapi/user/USER_ID (2认同)

jhe*_*cks 19

和大多数事情一样,"它取决于".但对我来说,你的实践并不差,并且不违反HTTP规范本身.但是,让我们清楚一些事情.

首先,URI应该是不透明的.即使它们对人不透明,它们对机器也是不透明的.换句话说,之间的差异http://mywebsite/api/user/13,http://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,这会带来一些安全隐患.在未命中而不是404时返回200可以给客户一个至少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认同)

小智 11

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

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


Sim*_*per 10

这是我们团队最近提出的。我们将两者404 Not found与消息正文一起使用并204 No Content基于以下原理。

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

  • http://mywebsite/api/user/13当用户 13 不存在时将返回 404
  • http://mywebsite/api/users?id=13将返回 204 无内容
  • http://mywebsite/api/users?firstname=test将返回 204 无内容

这里的想法是,“查询路由”预计能够返回 1、很多或没有内容。

无论您选择哪种模式,最重要的是保持一致- 因此请获得您团队的支持。


小智 8

所以从本质上讲,听起来答案可能取决于请求的形成方式.

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

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

就个人而言,200仍然感觉不对(虽然我以前认为它确实如此).例如,当发送不正确的ID时,200响应代码(没有详细响应)可能导致不调查问题.

更好的方法是发送204 - No Content回复.这符合w3c的描述*服务器已经完成了请求但不需要返回实体主体,并且可能想要返回更新的元信息.*1在我看来,混淆是由维基百科条目引起的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


Mic*_*ann 7

这篇古老而优秀的文章...... http://www.infoq.com/articles/webber-rest-workflow说到这个......

404 Not Found - 服务太懒(或安全),无法给我们一个真正的原因,我们的请求失败了,但无论什么原因,我们都需要处理它.


zap*_*pee 6

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

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

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

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

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

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

  • 当其余API在给定的URL中不可用时,真正的HTTP 404,它由运行其余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");
}
Run Code Online (Sandbox Code Playgroud)

但是,因为我的Java或JavaScript客户端可以以某种方式接收两种HTTP 404,我需要在HTTP 404的情况下检查响应的主体.如果我可以解析响应主体,那么我确信我回复了有响应的地方没有数据要发送回客户端.

如果我无法解析响应,这意味着我从Web服务器(而不是其他API应用程序)获得了真正的HTTP 404.

它是如此令人困惑,客户端应用程序总是需要额外的解析来检查HTTP 404的真正原因.

老实说,我不喜欢这个解决方案.这很令人困惑,需要一直向客户添加额外的废话代码.

因此,在这两种不同的场景中我没有使用HTTP 404,而是决定我将执行以下操作:

  • 我不再使用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);
}
Run Code Online (Sandbox Code Playgroud)

我认为这可以更好地处理这个问题.

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