REST API 404:URI错误或资源丢失?


219

我正在构建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结果。它很简单,而且我认为它很干净,即使不一定接受这种做法。但是有更好的方法吗?



7
另一个问题似乎与URI查询字符串格式有关。关于404的讨论不足以回答我的问题,即是否有更合适或更有用的方法来确定404的实际含义。我在发布前先审核了那个。
Brian Lacy

当浏览器混淆包含错误404时正常吗?当我用正确的uri进行常规操作但找不到资源时。
Vasiliy Mazhekin 2014年

3
404并不表示错误的URI,它表示资源未找到。那可能是因为没有用户'13',或者是因为没有资源/ mywebsite / api。
ChrisV

Answers:


101

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


7
这很有道理。不过,我想知道,首先返回404与返回带有空响应的200相比,实际上是否会获得任何好处?
Brian Lacy 2012年

15
如果返回的200为空,则会违反HTTP规范。您可以执行此操作,但是您的api将不会遵循REST的“统一接口”约束。
2012年

5
...并且您的响应正文应包括指向有效URI的超链接。除非根URI(可能还有一两个书签),否则您的客户端应始终遵循服务器提供给它们的链接。则无需发明关于它们决定如何在系统外工作的详细语义。
fumanchu

@DarrylHebbes是什么意思,我可以提出要求,并在Chrome开发者控制台的“网络”标签中查看响应的完整内容。
jaapz 2014年

59

404如果资源不存在,请使用。不要200空着身子回来。

这类似于在编程中使用undefinedvs空字符串(例如"")。虽然非常相似,但绝对有区别。

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

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


1
嘿,我想您可能正在尝试一些事情。返回“ 41”之一不是更有意义吗?请求的资源有问题时出现错误?例如,410 Gone表示“请求的资源在服务器上不再可用,并且不知道转发地址。” - (见w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.11
布赖恩·拉齐

实际上,我在上面引用的资源还说:“如果服务器不知道该条件是否永久存在,或者无法确定该条件是否永久存在,则应改用状态代码404(未找到)。” 因此,也许不一定是最好的选择要么..
布赖恩·拉齐

2
我认为任何41x错误都不适合您的用例。我喜欢将未定义字符串与空字符串进行比较,这是我的回答更简洁的版本。有所不同,但这并不意味着空字符串是错误。舒展的比喻:你可以有一个方法,String getName()有这样一个实现:return this.name == null ? "" : this.name。除非您希望客户知道那this.name是正确的,否则这是不正确的null
jhericks 2012年

1
404仍然是最合适的选择。您可以(并鼓励使用)该404响应的主体来通知用户/客户有关接收错误的特定详细信息。请参阅:stackoverflow.com/a/9999335/105484。在您的情况下,您可能希望使用该正文来向用户建议他们将其URI重新格式化为/ restapi / user / USER_ID
nategood 2012年

好的,我在这里可以看到一些好处,但是4XX是错误代码,那是什么错误情况?客户如何防止404发生?
Krzysztof Kubicki

20

与大多数情况一样,“取决于”。但对我来说,你的做法是不是不好,是不会对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-0800200c9a66http://mywebsite/api/user/14。这样做,您可以在不付出太多的情况下使用返回200的技巧。


1
这是我选择并使用204而不是404的解决方案。对我来说204表示“我找到了您的代码,但未找到结果”,而404表示“我找不到任何要执行的代码,这是走错了路?”
马特·桑德斯

11

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,然后在响应的“ 位置”标头中提供规范的url 。这告诉客户端,对于将来的请求,他们应该使用规范的url。


10

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

如果所请求的资源根据对一个请求的请求而构成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我认为,这种混淆是由Wikipedia条目指出204引起的-服务器成功处理了请求,但未返回任何内容。通常用作对成功删除请求的响应最后一句很有争议。考虑没有句子的情况,解决方案很容易-如果实体不存在,只需发送204。甚至有一个返回204而不是404的参数,请求已被处理,没有内容返回!不过请注意,204不允许在响应正文中显示内容

资料来源

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


9

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

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

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

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

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

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

  • 当剩余API在给定的URL中不可用时,它是真实的HTTP 404,它由运行剩余API应用程序的应用程序服务器或Web服务器抛出
  • 当数据库中没有基于查询条件的数据时,客户端也将返回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:

  • 我不再在其余应用程序中使用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情况。
chrisinmtown

一切都很好,但是一个问题是:您多久为404编写一次if语句?那会是什么?另一方面,如果您对api进行了调用,该API定义了内部可能存在json的404错误,则可以针对这种情况进行处理。
最多

我同意你的看法。我有一个返回数据的服务。但是在某些情况下,数据已损坏。请求网址是正确的(因此不是404),但实际上不是逻辑错误(500),因此204最合适。缺少消息正文的东西真让人讨厌。尽管可以说我可以在Header中插入原因。
cs94njw


1

统一资源标识符是资源的唯一指针。格式不正确的URI不会指向资源,因此对它执行GET不会返回资源。404表示服务器未找到与请求URI匹配的任何内容。如果您输入了错误的URI或错误的URI,这就是您的问题,也是无法获取HTML页面或IMG资源的原因。


0

对于这种情况,HTTP 404是来自REST API的响应的响应代码,例如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抛出的正确消息

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.