最近发现了使用标准的HTTP
状态码出现了没法准确表达业务的问题。java
登陆状态,一样是401
未受权,须要表达用户名或密码错误、验证码错误等多种场景,历史的处理方式没法知足场景。算法
考虑参照各大厂商规范,制定新的接口响应规范。json
以国内腾讯、阿里、京东、微博为首的API
响应规范以下:小程序
在《阿里巴巴开发手册(泰山版)》中已经给出了通用的业务code
规范。微信小程序
{ "code": 0, "message": "OK", "data": {} }
全部接口的状态码都是200
,经过code
区分业务。在微信小程序里尤甚,wx.request
的响应,401
、500
也算success
,只有无响应(数据包丢了)才走error
。api
最初设计尝试使用自定义code
的形式,须要从原响应架构迁移,为了下降迁移成本,设计方案以下(为了让图片看起来更清晰,换成了白色主题):微信
指望结果,在原API
不变的状况下:架构
@GetMapping public List<Student> getAll() { }
返回结果从:app
[{ "id": 1, "name": "Hello Kitty" }, { "id": 2, "name": "史努比" }]
变动为:框架
{ "code": 0, "message": "OK", "data": [{ "id": 1, "name": "Hello Kitty" }, { "id": 2, "name": "史努比" }] }
其余code
不为0
的状况,统一在异常处理中进行变动。
通过一系列的DEBUG
尝试:
框架调用API
方法处理HTTP
请求,返回结果须要经过实现HandlerMethodReturnValueHandler
接口的相关类中的handleReturnValue
方法进行处理。Spring Boot
中有诸多响应值处理器,框架根据当前返回的是页面、普通对象仍是响应式对象来调用相关的处理器。
核心思想就是自定义处理器,将返回值包装为code
格式的类型。
在WebMvcConfigurer
接口中已经定义过相关添加返回值处理器的方法addReturnValueHandlers
:
void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) { }
经测试无效,新加的handler
添加到List
中后,Spring
其他的handler
会添加到自定义handler
的前面,因Spring
选择处理器是根据最早匹配算法进行选择,因此选择不到自定义的处理器。
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) { boolean isAsyncValue = isAsyncReturnValue(value, returnType); for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue; } if (handler.supportsReturnType(returnType)) { return handler; } } return null; }
Spring Boot
默认存在15
个返回值处理器,而处理普通对象json
响应的处理器为RequestResponseBodyMethodProcessor
,另外一种实现方案就是替换掉默认的RequestResponseBodyMethodProcessor
。
核心思想是使用装饰器与RequestMappingHandlerAdapter
进行实现,具体代码请参考https://www.tuicool.com/artic...
注:我的以为代码写得不规范,注意适当参考,使用ListIterator
与ApplicationListener
实现会更优雅。
在综合参考了Google
、Microsoft
、Twitter
的接口设计后,决定仍是放弃国内的API
形式,跟随Google
的设计规范,仍然沿用状态码的形式。(有人说Microsoft
的规范更好,可是我没太看懂)
Google API
设计规范:https://cloud.google.com/apis...
请求成功(状态码为200
时)的设计不变:
[{ "id": 1, "name": "Hello Kitty" }, { "id": 2, "name": "史努比" }]
失败时添加state
状态与message
错误详细信息:
{ "state": "INVALID_ARGUMENT", "message": "密码不能为空" }
HTTP 状态码 | 状态 | 错误详细信息 |
---|---|---|
400 | INVALID_ARGUMENT |
密码不能为空 |
401 | CODE_INVALID |
验证码无效 |
401 | CREDENTIALS_INVALID |
用户名或密码错误 |
控制器负责抛出携带详细错误信息的异常,异常处理器根据异常类型肯定返回的状态码与业务状态。
@ExceptionHandler(InvalidArgumentException.class) public HttpErrorResponse invalidArgumentExceptionHandler(InvalidArgumentException exception, HttpServletResponse response) { return this.error(HttpErrorType.INVALID_ARGUMENT, exception, response); } private HttpErrorResponse error(HttpErrorType type, RuntimeException exception, HttpServletResponse response) { response.setStatus(type.getCode()); return new HttpErrorResponse(type.getValue(), exception.getMessage()); }
除非特殊要求,正常状况下,优先推荐选择标准HTTP
状态码形式设计API
,使用state
区分业务状态。