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

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

然而,对我来说,这增加了不必要的模糊性。HTTP 404通常与错误的URI联系在一起。所以实际上我们在说:“要么你去了正确的地方,但那个特定的记录不存在,要么互联网上没有这样的位置!我真的不知道是哪一个……”

考虑下面的URI:

http://mywebsite/api/user/13

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

http://mywebsite/restapi/user/13

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


当前回答

如果资源不存在,请使用404。不要返回一个空的200。

这类似于未定义vs空字符串(例如。"")。虽然非常相似,但绝对有区别。

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

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

其他回答

统一资源标识符是指向资源的唯一指针。格式较差的URI不指向资源,因此对其执行GET操作将不会返回资源。404表示服务器没有找到任何与请求uri匹配的内容。如果你输入了错误的URI或坏的URI,这就是你的问题,也是你无法访问资源的原因,无论是HTML页面还是IMG。

这篇古老但优秀的文章……http://www.infoq.com/articles/webber-rest-workflow是这么说的…

404 Not Found——服务太懒惰(或者太安全)了,不能给我们一个请求失败的真实原因,但是不管是什么原因,我们都需要处理它。

由于这个讨论似乎能够在时间结束时幸存下来,我将抛出JSON:API规范

404未找到 服务器在处理获取不存在的单个资源的请求时必须响应404 Not Found,除非请求保证200 OK响应并以null作为主要数据(如上所述)。

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json

{
  "links": {
    "self": "http://example.com/articles/1/author"
  },
  "data": null
}

也请看看这个Stackoverflow问题

这是一个非常老的帖子,但我也面临着类似的问题,我想与你们分享我的经验。

我正在用rest api构建微服务架构。我有一些rest 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,而是执行以下操作:

我不再在我的应用程序中使用HTTP 404作为响应HTTP代码。 我将使用HTTP 204 (No Content)而不是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);
}

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

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

所以从本质上讲,听起来答案可能取决于请求是如何形成的。

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

However, if the requested resource ID was included in the request header [include your own example], or indeed, in the URI as a parameter, eg http://mywebsite/restapi/user/?UID=13 then the URI would still be correct (because the concept of a USER does exits at http://mywebsite/restapi/user/); and therefore the response could reasonable be expected to be a 200 (with an appropriately verbose message) because the specific user known as 13 does not exist but the URI does. This way we are saying the URI is good, but the request for data has no content.

就我个人而言,200还是感觉不对(尽管我之前也说过)。例如,当发送不正确的ID时,200响应代码(没有详细响应)可能导致不调查问题。

A better approach would be to send a 204 - No Contentresponse. This is compliant with w3c's description *The server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation.*1 The confusion, in my opinion is caused by the Wikipedia entry stating 204 No Content - The server successfully processed the request, but is not returning any content. Usually used as a response to a successful delete request. The last sentence is highly debateable. Consider the situation without that sentence and the solution is easy - just send a 204 if the entity does not exist. There is even an argument for returning a 204 instead of a 404, the request has been processed and no content has been returned! Please be aware though, 204's do not allow content in the response body

来源

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