我们使用SpringBoot编写接口的时候,最好是返回一个统一格式的JSON,该格式包含错误码,附带信息,以及携带的数据。这样前端在解析的时候就能统一解析,同时携带错误码可以更加容易的排查错误。
public class ApiResult {
/**
* 错误码,对应{@link ErrorCode},表示一种错误类型
* 如果是成功,则code为200
*/
private int code;
/**
* 对错误的具体解释
*/
private String message;
/**
* 返回的结果包装在value中,value可以是单个对象
*/
private final Object value;
//忽略getter和setter,以及构造函数
}
其中ErrorCode可以这样定义
public enum ErrorCode {
SUCCESS(200,"成功"),
NO_PERMISSION(211,"权限不足"),
SERVER_ERROR(10000,"服务器异常"),
AUTH_ERROR(10001,"认证失败"),
PARAMS_ERROR(10002,"参数错误"),
JSON_PARSE_ERROR(10003,"Json解析错误"),
ILLEAGAL_STRING(15001,"非法字符串"),
UNKNOW_ERROR(16000,"未知错误");
private int code;
private String msg;
ErrorCode(int code,String msg){
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
方式一:在每一个函数末尾,都调用ApiResult的构造函数,然后返回,即如下
@RequestMapping("/test")
public ApiResult test(){
String result = "hello world";
return ApiResult.success(result);
}
但是这种方式太麻烦了,我们希望这种包装是透明的,即我们想直接返回对象,然后框架帮我们做一层包装。因此看方式二
方式二:在接口方法返回之后,在序列化器对该结果进行序列化之前,我们可以使用ResponseBodyAdvice在这两个阶段之间对结果进行操作,因此我们可以使用该Adivice在序列化之前,拦截返回结果,对结果进行包装,然后再将包装后的结果返回,最后序列化成对应的JSON字符串。
以下是具体实践
@ControllerAdvice(annotations = RestController.class)
public class ApiResultHandler implements ResponseBodyAdvice {
private ThreadLocal<ObjectMapper> mapperThreadLocal = ThreadLocal.withInitial(ObjectMapper::new);
private static final Class[] annos = {
RequestMapping.class,
GetMapping.class,
PostMapping.class,
DeleteMapping.class,
PutMapping.class
};
/**
* 对所有RestController的接口方法进行拦截
*/
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
AnnotatedElement element = returnType.getAnnotatedElement();
return Arrays.stream(annos).anyMatch(anno -> anno.isAnnotation() && element.isAnnotationPresent(anno));
}
@Override
public Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
Object out;
ObjectMapper mapper = mapperThreadLocal.get();
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if(body instanceof ApiResult){
out = body;
}
else if (body instanceof ErrorCode){
ErrorCode errorCode = (ErrorCode) body;
out = new ApiResult(errorCode.getCode(),errorCode.getMsg(),null);
}
else if (body instanceof String){
ApiResult result = ApiResult.valueOf(body);
try {
//因为是String类型,我们要返回Json字符串,否则SpringBoot框架会转换出错
out = mapper.writeValueAsString(result);
} catch (JsonProcessingException e) {
out = ApiResult.errorOf(ErrorCode.JSON_PARSE_ERROR,e.getMessage());
}
}
else{
out = ApiResult.valueOf(body);
}
return out;
}
}
这时候你再直接返回对象,你会发现该对象已经自动包装成ApiResult了。思考一下,如果发生异常了怎么办呢?发生异常其实也可以使用方式二的思路去解决的,具体解决方法我下次再写吧!希望对大家有帮助~