RESTful API 错误最佳实践
Posted
技术标签:
【中文标题】RESTful API 错误最佳实践【英文标题】:Best Practice for Errors in RESTful API 【发布时间】:2013-03-14 01:52:54 【问题描述】:在 RESTful API 中返回 HTTP 状态代码的最佳做法是什么?我将 Laravel 4 用于我的 php 框架。
如果出现错误,我应该使用
return Response::json('User Exists', 401);
或
包含success
的标志
return Response::json([
'success' => false,
'data' => 'User Exists'],
401
);
或
用200代替4xx,依靠success
判断是否有错误
return Response::json([
'success' => false,
'data' => 'User Exists'],
200
);
而且在成功且不需要返回任何数据的情况下,你还返回什么吗?
PHP API 代码
public function getCheckUniqueEmail()
// Check if Email already exist in table 'users'
$uniqueEmail = checkIfEmailExists();
// Return JSON Response
if($uniqueEmail)
// Validation fail (user exists)
return Response::json('User Exists', 401);
else
// Validation success
// - Return anything?
【问题讨论】:
【参考方案1】:当您查看list of available HTTP status codes 时,您有时会意识到其中有很多,但单独使用它们并不能真正解释错误。
所以要回答你的问题,有两个部分。一个是:您的 API 如何传达错误的原因并添加 API 的用户(在大多数情况下是另一个开发人员)可以阅读和采取行动的有用信息。您应该添加尽可能多的信息,包括机器可读和人类可读的。
另一部分:HTTP 状态码如何帮助区分某些错误(和成功)状态?
后一部分实际上比一件可能的事情要难。在某些明显的情况下,使用 404 来表示“未找到”。 500 表示服务器端的任何错误。
我不会使用状态 401,除非我真的希望在存在 HTTP 身份验证凭据的情况下允许操作成功。 401通常会在浏览器中触发一个对话框,这很糟糕。
如果资源是唯一的并且已经存在,则状态“409 冲突”似乎是合适的。如果创建用户成功,状态“201 Created”听起来也是个好主意。
请注意,还有更多的状态代码,其中一些与 HTTP 协议的扩展有关(如 DAV),一些完全不标准化(如 Twitter API 中的状态“420 增强你的冷静”)。查看http://en.wikipedia.org/wiki/List_of_HTTP_status_codes 以了解到目前为止已使用的内容,并决定是否要使用适合您的错误情况的内容。
根据我的经验,简单地选择一个状态代码并使用它很容易,但很难始终如一地按照现有标准这样做。
但是,我不会仅仅因为其他人可能会抱怨就停在这里。 :) 正确做 RESTful 接口本身就是一项艰巨的任务,但是接口越多,积累的经验就越多。
编辑:
关于版本控制:将版本标记放入 URL 被认为是不好的做法,如下所示:example.com/api/v1/stuff
它可以工作,但效果不好。
但第一件事是:您的客户如何指定他想要获得哪种表示形式,即他如何决定获得 JSON 还是 XML?答:使用Accept
标头。他可以为 JSON 发送Accept: application/json
,为 XML 发送Accept: application/xml
。他甚至可能接受多种类型,然后由服务器决定返回什么。
除非服务器被设计为使用一种以上的资源表示(JSON 或 XML,客户端选择)来回答,否则客户端确实没有太多选择。但是让客户端至少发送“application/json”作为他唯一的选择,然后返回一个标头Content-type: application/json
作为响应仍然是一件好事。这样,双方就可以清楚地表明他们希望对方看到的内容是什么样的。
现在是版本。如果您将版本放入 URL,您可以有效地创建不同的资源(v1 和 v2),但实际上您只有一个资源(= URL)并具有不同的方法来访问它。当请求的参数和/或响应中的表示发生与当前版本不兼容的重大更改时,必须创建 API 的新版本。
因此,当您创建使用 JSON 的 API 时,您无需处理通用 JSON。你处理一个具体的 JSON 结构,它对你的 API 来说是独一无二的。您可以并且可能应该在服务器发送的Content-type
中指出这一点。 “供应商”扩展就是为此:Content-type: application/vnd.IAMVENDOR.MYAPI+json
将告诉全世界基本数据结构是 application/json,但真正告诉您期望哪种结构的是您的公司和您的 API。这正是 API 请求的版本适合的地方:application/vnd.IAMVENDOR.MYAPI-v1+json
。
因此,您希望客户端发送一个Accept: application/vnd.IAMVENDOR.MYAPI-v1+json
标头,而不是将版本放在URL 中,并且您也使用Content-type: application/vnd.IAMVENDOR.MYAPI-v1+json
进行响应。对于第一个版本,这确实没有任何改变,但让我们看看当第 2 版出现时事情会如何发展。
URL 方法将创建一组完全不相关的新资源。客户会怀疑example.com/api/v2/stuff
是否与example.com/api/v1/stuff
是同一资源。客户端可能已经使用 v1 API 创建了一些资源,并且他存储了这些东西的 URL。他应该如何将所有这些资源升级到 v2?资源真的没变,都是一样的,唯一不同的是v2看起来不一样。
是的,服务器可能会通过向 v2 URL 发送重定向来通知客户端。但是重定向并不表示客户端也必须升级 API 的客户端部分。
在版本中使用接受标头时,所有版本的资源 URL 都相同。客户端决定使用版本 1 或 2 请求资源,服务器可能非常友好并且仍然使用版本 1 响应来响应版本 1 请求,但所有版本 2 请求都使用新的和闪亮的版本 2 响应。
如果服务器无法响应版本 1 的请求,他可以通过发送 HTTP 状态“406 Not Acceptable”告诉客户端(请求的资源只能根据发送的 Accept headers 生成不可接受的内容请求。)
客户端可以发送包含两个版本的接受标头,这使服务器能够以他最喜欢的一个响应,即智能客户端可能实现版本 1 和 2,现在将两者都作为接受标头发送,并等待服务器从版本 1 升级到 2。服务器将在每个响应中告诉它是版本 1 还是版本 2,并且客户端可以相应地采取行动 - 他不需要知道服务器版本升级的确切日期。
总结一下:对于一个非常基本的 API,使用有限,可能是内部的,即使有一个版本也可能是矫枉过正。但你永远不知道这是否会在一年后成为现实。在 API 中包含版本号始终是一个非常好的主意。最好的地方是你的 API 将要使用的 mime 类型。检查单个现有版本应该很简单,但您可以选择稍后透明地升级,而不会混淆现有客户端。
【讨论】:
restful api 仅供我自己站点的 ajax/backbonejs/jquery 使用。在这种情况下,我应该坚持使用相同的错误代码(比如 400)吗?或者为每个 AJAX 响应返回 200,并检查success
变量以查看是否发生错误
不,通过错误代码指示基本的“成功/错误”事实是一个非常好的主意。 HTTP 客户端处理错误响应的方式不同,例如它们不应该被缓存等。在数据有效负载中指示相同也是一件好事。我自己的 API 要么在成功时有“数据”,要么在失败时有“错误”。这与“成功=真/假”基本相同,它对我有用。但要知道状态“400 Bad request”不是您认为的一般客户端错误状态。没有这种东西,你必须指定错误状态。
很好的答案,斯文!但是,您说: > 关于版本控制:将版本 > 标记放入我不同意的 URL 被认为是不好的做法。这是来自 Apigee 的 guide,他认为在 URL 中使用版本号确实是的最佳实践。他们还阐明了返回状态代码。
您可以不同意。请注意,在您的视频中,据说 Apigee 遵循务实的方法。我不会称其为“最佳”做法。你会发现支持者和反对者一样多。支持者通常认为将版本放入 url 是一种合理且简单的方法。反对者认为,将“你得到什么”和“它看起来如何”这两个东西混合到一个东西(URL)中是不好的。【参考方案2】:
我不会对所有事情都使用 200 状态。那只会令人困惑。
Jquery 允许您以不同的方式处理不同的响应代码,已经有内置的方法可以利用它们在您的应用程序中使用它们,并且当您的应用程序增长时,您也可以提供该 API 供其他人使用。
编辑: 另外,我强烈建议您观看有关 Laravel 和 API 开发的演讲:
http://kuzemchak.net/blog/entry/laracon-slides-and-video
【讨论】:
视频链接很有帮助,但演讲者在实现 RESTful API 时做了一些坏事,比如在 URL 中包含版本号。这就是“接受”标题的用途。 @Sven 您能否更详细地解释一下如何使用它?我很好奇=)【参考方案3】:Illuminate\Http\Response
有一些 HTTP 状态代码列表,扩展至 Symfony\Component\HttpFoundation\Response
。你可以在课堂上使用它。
例如:
use Illuminate\Http\Response as LaravelResponse;
...
return Response::json('User Exists', LaravelResponse::HTTP_CONFLICT);
它更具可读性。
【讨论】:
以上是关于RESTful API 错误最佳实践的主要内容,如果未能解决你的问题,请参考以下文章