上周五,我在作一个 HTTP 接口的迁移。恰好跟同事进行了一场关于 HTTP 接口的状态返回的讨论。很巧合地是,咱们的观点互不相同,在他看来:segmentfault
全部业务返回的响应状态码都应该是200,包含业务异常的响应状态码也是200,错误的消息放在 Body 里面描述;未到达业务层的请求,例如:5XX, 403 这样的错误应该算做是 “链接层” 的错误。设计
这样作的优势是:code
对于 5XX, 403 这样的错误能够概括为 “链接” 错误。能够统一处理?接口
客户端只需在接受到 200 的状态码时才作业务处理。资源
可是在我看来,这有一个很大的缺点:get
对于业务异常没法在第一层的 status code 里面发现,须要进一步解析 body, 响应的 body 有 2 个 Schema, 即 error message 和业务响应实体的 schema。这样作的厂商其实不少,例如: LeanColud 的获取数据的接口。class
GET /1.1/classes/<className>/<objectId>
在使用这个接口获取一个不存在的 objectId
时,响应的格式是:扩展
Response Status Code: 200 Response Body: {}
这个接口把 className
与 objectId
都作成了 path parameter, 一般地,咱们不该该用 404 来描述一个 URL 不存在的状况吗?object
若是我要使用这个接口,我在客户端接收到响应以后大概要作如下的处理:接口设计
判断 http status code 是不是 200。
解析 http body 是不是一个 error message。
解析 http body 为业务实体。
判断解析后的业务实体是不是"空"。
因此,个人观点恰好相反:全部响应的状态要充分利用 http status code 来描述响应的异常状况, 而且配合 http body 来描述更详细的 error message。
我同事他认为这样作的缺点是:
http status code 是很是有限的,且不能扩展。
那么咱们该怎么处理这种 http status code 表达能力不足的状况呢? 答案是: 使用 http status code 来表明某一个类型的异常,同时用 http response body 来描述更详细的异常消息。 例如:
Response Status Code: 400 Response Body: { "code": "missing_parameter_name", "message": "请求中必须包含参数 name" } Response Status Code: 400 Response Body: { "code": "id_not_matched", "message": "id 格式应该为: \w+" }
上面的例子中咱们使用了 400 来描述了一个 Bad Request
, 而且将更加详细的业务异常表达出来了! 来看看我如何改造 LeanCloud 的接口返回:
Response Status Code: 200 Response Body: { "objectId": "xxx", "name": "这是一个业务实体" } Response Status Code: 404 Response Body: { "error": "resource_not_found", "message": "没法在 className 下找打 objectId 资源" }
总结一下我这种设计方式的优势:
能够根据 http status code 判断响应是否正常。
无需将一个 response body 适配多个 schema 解析。
客户端友好,无需过多包装 response body,在正常返回的状况下数据能够直接使用,无需拆箱。
接口设计能够变幻无穷,能够说没有哪种方案是完美的,咱们在设计的接口的时候也要尽量地站在使用者的角度考虑下面的原则:
客户端友好,响应的层次尽量简洁。
数据不要过渡封装,最好能作到即拿即用。
接口名称简洁明了。
对于 HTTP 接口的更多实践经验能够查看个人文章 RESTful Best Practices