ResponseBodyAdvice的作用:拦截Controller方法的返回值,统一处理返回值/响应体,一般用来统一返回格式,加解密,签名等等。
看下ResponseBodyAdvice的源码:
public interface ResponseBodyAdvice<T> {
/**
* 是否支持advice功能
* true 支持|false 不支持
*/
boolean supports(MethodParameter var1, Class<? extends HttpMessageConverter<?>> var2);
/**
* 处理返回的数据
*/
@Nullable
T beforeBodyWrite(@Nullable T var1, MethodParameter var2, MediaType var3, Class<? extends HttpMessageConverter<?>> var4, ServerHttpRequest var5, ServerHttpResponse var6);
}
所以我们只需要创建一个实现类,实现ResponseBodyAdvice
接口就可以了
首先创建一个统一结果响应体
/**
* 通用返回结果
* @author WangGuocai
* @param
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private boolean flag;
private Integer code;
private String msg;
private T data;
public static <T> Result<T> success(String msg,T data){
Result<T> result = new Result<>();
result.setFlag(true);
result.setCode(20000);
result.setMsg(msg);
result.setData(data);
return result;
}
public static <T> Result<T> fail(Integer code,String msg){
Result<T> result = new Result<>();
result.setFlag(false);
result.setCode(code);
result.setMsg(msg);
return result;
}
}
ResponseBodyAdvice
接口@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
@SneakyThrows
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (o instanceof String) {
return objectMapper.writeValueAsString(Result.success("成功", o));
return Result.success("成功", o);
}
}
@RestControllerAdvice注解@RestControllerAdvice是@RestController注解的增强,可以实现三个方面的功能:
1.全局异常处理
2.全局数据绑定
3.全局数据预处理
if(o instanceof String){
return objectMapper.writeValueAsString(ResultData.success(o));
}
这段的作用是,防止controller直接返回String,如果不加的话是会直接返回String的,
所以我们要把他放进Result里面,变成Json;
好了,,到这里,,我们就不需要通过Result.success
进行响应了,直接在Controller返回原始数据,Springboot就会完成实现类的包装;
#然后我们还可以利用他做到统一的异常处理
注意Exception.class为必须捕获的异常,否则一旦遇到没有处理的异常就会跳过直达,要么直接响应原始异常,要么被响应处理器捕获处理了又给你套上一层,还给你报异常依然显示成功。。。凎
/**
* 全局异常处理
*/
@RestControllerAdvice
@Slf4j
public class RestExceptionHandler {
@ExceptionHandler(ArithmeticException.class)
public Result<String> arithmeticException(Exception e){
log.error("全局异常信息 ex={}",e.getMessage(),e );
return Result.fail(50000,e.getMessage());
}
@ExceptionHandler(UserNotFoundException.class)
public Result<Object> userNotFoundException(Exception e){
log.error("全局异常信息 ex={}",e.getMessage(),e );
return Result.fail(50000,e.getMessage());
}
@ExceptionHandler(Exception.class)
public Result<String> Exception(Exception e){
log.error("全局异常信息 ex={}",e.getMessage(),e );
return Result.fail(50000,"未知异常请联系管理员");
}
}
注意上面捕获的异常必须和参数中的异常一直,或者参数必须是其超类,否则会抛出非法状态异常
这时候当异常发生的时候就可以自动对异常进行封装
总之呢。这个处理器可以在响应前对响应体做一些处理,
o
:代表原响应体
objectMapper.writeValueAsString()
:可以对前端输出响应内容,
respons
和request
上面也具备了;
增强处理器和上面的统一响应处理器是可以单独使用的,
如果单独使用这个处理器,没有什么问题,
但是一旦我们同时启用两个处理器,或者只启用响应处理器但是遇到报错的时候,就会出现一个问题
启用响应处理器
启用响应处理器和异常处理器
相应处理器会把异常处理过的Result或者springboot默认的响应体进行再次封装
所以我们需要加一段
if (o instanceof Result){
return o;
}
在响应处理器中,当结果已经是Result,表示已经被封装过了,那我们就直接响应(不必担心spring默认报错,因为已经被错误处理器拦截并处理了),其他处理器都是先行处理,最终都会到实现了这个接口的实现类的beforeBodyWrite
方法来进行最终处理,这样就可以避免异常处理器处理过的Result被再次处理,同时也能避免我们哪次手滑了返回了封装后的Result,被再次封装,这样只要检测到是Result,就会直接返回
响应处理器完整代码
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
@SneakyThrows
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (o instanceof String) {
return objectMapper.writeValueAsString(Result.success("成功", o));
}
if (o instanceof Result){
return o;
}
return Result.success("成功", o);
}
}