为什么写这篇文章?
相信不少 Java 开发都在项目中使用过类似 ApiResult 这样的对象来包装 Api 返回类型,这相比什么都不包装有一定的好处,但这真的就是最好的做法吗?
其实统一封装 ResultBean 实际上也是一种重复工作,秉承 DRY 的理念,还有必要对其继续优化。
统一返回 ApiResult 还不是最佳实践,必须不断思考优化,就像 React 所提倡的 Rethinking Best Practices 。
ApiResult 现状
我们先看一个常见的 ApiResult 对象,代码如下:
@Data
public class ApiResult<T> implements Serializable {
private int code;
private String message;
private T data;
}
好处:客户端可以使用统一的处理方式。
存在的问题:
- 在统一返回 ApiResult 的情况下,即使是正常返回,也会带上 code、message 属性,属于冗余
- Controller 层代码存在重复,返回对象重复定义、包装调用编写重复。
public ApiResult<List<Data>> demo() {
return ApiResult.ok(getList());
}
当 API 越来越多时,这些存在的问题会被被放大,如何解决这些问题呢?请接着看。
使用 HTTP 状态码
有许多项目采用的方式是,在 API 调用成功时使用正常的数据模型,而在出现错误时,返回相应的 HTTP 错误码 和描述信息。我们看一段 jhipster 中的代码:
@GetMapping("/authors/{id}")
public ResponseEntity<AuthorDTO> getAuthor(@PathVariable Long id) {
Optional<AuthorDTO> authorDTO = authorService.findOne(id);
return ResponseUtil.wrapOrNotFound(authorDTO);
}
主要 HTTP 状态码的含义:
- 1XX – Informational
- 2XX – Success
- 3XX – Redirection
- 4XX – Client Error
- 5XX – Server Error
采用 HTTP 状态码就不再需要统一返回 ApiResult ,但问题也随之而来,那就是 ApiResult 中定义的 error code 很难跟 HTTP 错误码一一对应,光有 HTTP 错误码和描述信息是不够的,还需要定义专门的错误模型。
API 错误模型
如何定义一个好的 API 错误模型,这需要根据 业务的复杂程度 来定,我们先来看看几个 Big Company 都是怎么做的。
先看 twitter 的,其中省略了无关的 HTTP 输出信息。
HTTP/1.1 400 Bad Request
{
"errors":[{
"code":215,"message":"Bad Authentication data."}]}
使用了错误码,并且错误模型是一个数组,意味着可能会返回多个错误。
再来看 Facebook 的 Graph API。
HTTP/1.1 200
{
"error": {
"message": "Syntax error \"Field picture specified more than once. This is only poss