2015年冬天,我写下第一篇也是目前惟一一篇关于 Restful API 设计的文章。时间过的飞快,转眼三年前过去了。这三年间经历过的项目中,后台逐渐微服务化,restful 也成为你们耳熟能详的设计方案。这里记下些本身的经验和教训,以供对照。数据库
基本的 code 原则很简单,2xx 表示成功,4xx 表示客户端错误,5xx 表示服务端错误。数组
那如何分辨是客户端仍是服务端错误呢?我总结了如下几种常见的客户端错误,以及对应的错误码。restful
总之,凡是客户的锅,都返回 4xx 。若是刚好不在上面所列的三种状况中,则用 400 代替。微服务
服务端自身错误则包含两类状况:测试
第一种错误是不可避免的,属于不可控的外部环境问题。第二种错误虽然能够经过 review 代码加上各类测试来预防,但最好有个兜底的错误处理以避免程序挂掉。ui
我司对于服务端错误统一返回 500(internal server error),由于考虑到服务端错误对于客户来说毫无建设意义,毕竟客户绝对没有办法帮助咱们解决错误。即便对于工程师来讲,日志也比 code 更有表现力。相对而言,客户端错误则尽可能设计的详细由于大部分状况下客户端要据此来引导用户回到正常的业务中来。好比,若是返回 401,则引导用户登录或者注册。若是业务比较复杂,还要考虑扩展 reponse 来指明更加具体的错误。如:设计
400 bad request { "code": 123, "message": "Name is required" }
GET /ordersrest
200 OK { "offset": 0, "limit": 20, "count": 100, "elements": [...] }
对于这个 List API,若是资源不存在,返回应该是什么。受 404 概念的普及影响,不少人会选择返回日志
404 NotFound
难道说,若是不存在 orders(订单) 就是错误吗?好比我历来没有在淘宝下过单,那订单列表也就应该显示客户端错误吗?这显然是不对的。实际上,404 是指所请求的资源不存在。而对于 orders 来讲,它是一个集合概念。无论下没下过单,这个集合总归是存在的。按照这个理论,正确的返回应该是:code
200 OK { "offset": 0, "limit": 20, "count": 100, "elements": [] // 空数组 }
因此对于 List API 来讲,没有 404。
restful API 的路径能够表现资源的从属关系。好比,用户能够有多个地址。
/users/{user_id}/addresses/
那么,对于一个并不存在的用户而言,访问上述 API,应该返回什么?
用户不存在,他的地址也必然不存在,那彷佛是个简单的客户端错误。但咱们确实有必要参考 Parent resource 的状态吗?这从理论上讲彷佛毫无破绽,但实际操做及其困难。假如 Parent resource 的状态为 s1, Child resource 的状态为 s2,若是必须参考 s1 才能定义 s2,则 Child resource 的状态为 s1 s2。这仍是简单的层次,若是 Parent 之上还有 Parent,则最终 Child 的状态会变成 s0 s1 * s2。若是随着业务的升级,每一个节点的状态推算都要这样愈来愈复杂,那结果必然是整个系统的崩塌。
因此,目前比较推崇的作法是,仅仅考虑目标资源或者资源集合的状态。即,addresses,无论它从属于谁。