我正在构建一个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只是HTTP响应代码。在此基础上,您可以提供一个响应体和/或其他带有开发人员将看到的更有意义的错误消息的报头。

其他回答

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

404只是HTTP响应代码。在此基础上,您可以提供一个响应体和/或其他带有开发人员将看到的更有意义的错误消息的报头。

由于这个讨论似乎能够在时间结束时幸存下来,我将抛出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);
}

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

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

这是我们团队最近提出的。 我们使用404 Not found与消息体和204 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内容 http://mywebsite/api/users?firstname=test将返回204 no内容

这里的想法是,“查询路由”被期望能够返回1,许多或没有内容。

无论你选择哪种模式,最重要的是保持一致——所以要得到你的团队的认可。