简介
我想要在本篇博客中介绍一下我在Http Api制定上的一些实践,重点介绍api的返回格式,及实现及前端的处理流程。
Restful协议
网上的资料很多,不用多作介绍。
优点:
充分利用了Http的特性,正宗,规范。
缺点:
1. 实现起来很麻烦,我在spring的官方示例中看过以下代码
return HtttEntity.code(HttpStatus.NO_DEFIND).body(...); //模拟代码,别当真
controller层中对每一个返回都会附上一个状态码,这个显得很多余,且需要程序员熟知所有状态码。
2.语义上的混肴
我听一同事说,曾和前端发生争执,因为前端看到404以为是找不到页面,其实是找不到资源。
尽管到目前为止,还没有见过在项目中使用标准的restful协议,但它确实是一个不错的选择,我想随着Spring-WebFlux的的普及,会有越来越多的人选择使用restful协议。
项目中常见的方案。
很多项目中,会借鉴restful协议,比如充分利用delete,put等方法,往往会自定义一个返回类型,可能格式如下:
class Result{
String code; //表示错误吗,比如说 1:表示成功,其它表示失败
String message; //错误的具体信息
Object result; //成功时返回的数据
....
}
我相信这种方案已经很普遍,发生了业务异常,http状态码依旧为200,Result里面的code会一个自定义的错误码。
缺点:
- 没有利用http状态码。所有的错误判断要依据方法体中的数据
- 即使获取一个简单类型的数据,接口调用端也要调用getResult来获取具体内容。
- 调用重复代码太多,虽然可以对上面的返回类型进行封装,但我很少见过,很多调用端还是会有一堆if判断。
- 不利于网关对其进一步改进。(以后会说明)
我的实现
- 实现方案
1. 新增HTTP状态码:460作为业务异常标识
2. 定义错误返回类型
3. 正确时,直接返回业务数据,或什么都不返回,发生异常时,返回自义意的错误类型。 - 代码展示
@ControllerAdvice
public class TmsExceptionHandler {
@ExceptionHandler(BaseException.class)
ResponseEntity baseException(BaseException exception){
Error err = new Error();
err.setStatus(exception.getStatus());
err.setMessage(exception.getMessage());
return ResponseEntity
.status(460)
.body(resultMap);
}
@ExceptionHandler({MethodArgumentNotValidException.class})
ResponseEntity notValidException(MethodArgumentNotValidException ex){
final StringJoiner joiner = new StringJoiner("\n");
ex.getBindingResult()
.getAllErrors().stream()
.map(err -> err.getDefaultMessage())
.forEach( s ->joiner.add(s));
///@Todo这个地方用map太菜了,后继改进
err.setStatus(4000);
err.setMessage(joiner.toString());
return ResponseEntity.status(460).body(resultMap);
}
}
- 调用展示
不管是前端,还是Android端,只需要在自己的Http添加拦截器,对460错误进行单独拦截。(以后展示代码) - 缺点分析
虽然http协义规定:
4** 状态码表示:客户端错误,请求包含语法错误或无法完成请求
但460却是我的自定义状态码。并不是非常严格地符合http规范,如果用400可能更好,但可能引起混淆。
(未完……)