Web services RESTAPI404:错误的URI,还是缺少资源?

Web services RESTAPI404:错误的URI,还是缺少资源?,web-services,http,rest,Web Services,Http,Rest,我正在构建一个RESTAPI,但遇到了一个问题 设计RESTAPI的公认做法似乎是,如果请求的资源不存在,则返回404 然而,对我来说,这增加了不必要的歧义。HTTP 404更传统地与坏URI相关联。所以实际上我们说的是“要么你找到了正确的位置,但具体的记录并不存在,或者互联网上没有这样的位置!我真的不确定是哪一个…” 考虑以下URI: http://mywebsite/api/user/13 如果我得到404,是因为用户13不存在吗?还是因为我的URL应该是: http://mywebsit

我正在构建一个RESTAPI,但遇到了一个问题

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

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

考虑以下URI:

http://mywebsite/api/user/13
如果我得到404,是因为用户13不存在吗?还是因为我的URL应该是:

http://mywebsite/restapi/user/13

在过去,如果记录不存在,我只返回一个空结果,返回一个
http200ok
响应代码。这很简单,在我看来非常干净,即使这不一定是公认的做法。但是有更好的方法吗?

404只是HTTP响应代码。除此之外,您还可以为响应正文和/或其他标题提供开发人员将看到的更有意义的错误消息。

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

与大多数情况一样,“这取决于”。但对我来说,您的实践并不糟糕,也不违反HTTP规范本身。不过,让我们澄清一些事情

首先,URI应该是不透明的。即使它们对人不是不透明的,对机器也是不透明的。换句话说,
http://mywebsite/api/user/13
http://mywebsite/restapi/user/13
http://mywebsite/api/user/13
http://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。i、 e.
http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66
http://mywebsite/api/user/14
。这样做,您可以使用返回200而不付出太多的技巧。

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

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

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


404
并不意味着它是一个“坏URI”。有专门针对URI错误的HTTP代码(例如,
414请求URI太长)。

404未找到技术上意味着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。

这篇古老但优秀的文章。。。这是怎么说的


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

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

如果请求的资源根据对
http://mywebsite/restapi/user/13
并且用户13不存在,然后,404可能是合适和直观的,因为URI代表不存在的用户/实体/文档/等等。对于使用GUID的更安全的技术来说也是如此http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66
和上面的api/restapi参数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"); }
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);
}
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);
     }

}