REST API 404:坏的URI,或缺少资源?

我正在构建一个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结果。它很简单,在我看来非常干净,即使它不一定是公认的实践。但是有没有更好的办法呢?

336769 次浏览

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

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

和大多数事情一样,“视情况而定”。但对我来说,你的做法并不坏,并没有违反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 (No Content)响应。这将向客户传递其他信息。这意味着由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-0800200c9a66优于http://mywebsite/api/user/14。这样做的话,你可以使用你的技术,返回200,而不会泄露太多。

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

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

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

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

404未找到技术上意味着uri当前没有地图到资源。在你的例子中,我解释了一个对http://mywebsite/api/user/13的请求,该请求返回一个404,以暗示这个url是从来没有映射到一个资源。对于客户来说,这应该是谈话的结束。

为了解决歧义问题,可以通过提供其他响应代码来增强API。例如,假设你想允许客户端发出url http://mywebsite/api/user/13的GET请求,你想要传达客户端应该使用规范的url http://mywebsite/restapi/user/13。在这种情况下,你可以考虑通过返回301永久搬迁来发出一个永久重定向,并在响应的位置报头中提供规范的url。这告诉客户端,对于将来的请求,他们应该使用规范url。

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

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

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

如果根据http://mywebsite/restapi/user/13的请求,请求的资源构成URI的一部分,而用户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响应代码(没有详细响应)可能导致不调查问题。

更好的方法是发送__abc0响应。这符合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 < / p >

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

我正在用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 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抛出的正确消息

这是我们团队最近提出的。 我们同时使用带有消息体的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,许多或没有内容。

无论你选择哪种模式,最重要的事情是是一致的 -所以从你的团队购买。

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