环境:Springboot2.4.11
很多时候我们对接口的返回值都会做统一的处理,返回{code, message,data}等信息标识本次请求的处理结果,这统一的处理也都是在各自的Controller上做自行的处理。本篇内容告诉你如何通过ResponseBodyAdvice对象来实现对结果的统一处理,也就是说在Controller上我们不再对返回结果进行处理了,而是由统一的一个ControllerAdice Bean对象进行处理。这对我们的Controller接口来说可读性更强,也业务无关的东西一概不出现,同时代码也更加简洁。
ResponseBodyAdvice是什么
ResponseBodyAdvice类型的Bean对象允许在执行@ResponseBody或ResponseEntity控制器方法之后但在使用HttpMessageConverter写入正文之前自定义响应。实现可以直接向
RequestMappingHandlerAdapter和ExceptionHandlerExceptionResolver注册,或者更可能使用@ControllerAdvice进行注释,在这种情况下,它们都将被自动检测到。
定义统一返回对象
public class R {
public R() {
}
private int code ;
private String message ;
private Object result ;
private String errorDetails ;
public R(int code, String message, Object result) {
super();
this.code = code;
this.message = message;
this.result = result;
}
public R(int code, String message, Object result, String errorDetails) {
super();
this.code = code;
this.message = message;
this.result = result;
this.errorDetails = errorDetails ;
}
public R(int code, String message, String errorDetails) {
super();
this.code = code;
this.message = message;
this.errorDetails = errorDetails ;
}
public R(int code, String message) {
super();
this.code = code;
this.message = message;
}
public static R success() {
return success(null) ;
}
public static R success(Object data) {
return success("成功", data) ;
}
public static R success(String message, Object data) {
return new R(ResultCode.SUCCESS, message, data) ;
}
public static R failure() {
return failure("失败") ;
}
public static R failure(Object data) {
return failure("失败", data) ;
}
public static R failure(String message) {
return failure(message, null) ;
}
public static R failure(int code, String message) {
return new R(code, message) ;
}
public static R failure(String message, Object data) {
return new R(ResultCode.FAILURE, message, data) ;
}
public static R failure(String message, String errorDetails) {
return new R(ResultCode.FAILURE, message, errorDetails) ;
}
public static R error(String message, String errorDetails) {
return new R(ResultCode.ERROR, message, errorDetails) ;
}
public static interface ResultCode {
int SUCCESS = 0 ;
int FAILURE = -1 ;
int ERROR = 500 ;
}
}
定义ResponseBodyAdvice
@RestControllerAdvice
public class ResponseResultControllerAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return returnType.getParameterType() != Void.TYPE
&& (!returnType.hasMethodAnnotation(NoPack.class)
&& !returnType.getMethod().getDeclaringClass().isAnnotationPresent(NoPack.class)) ;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof R) {
return body ;
}
return R.success(body) ;
}
}
上面的类非常简单
supports 方法判断当前的请求接口是否支持,在这里对于void,或是有@NoPack注解的都不进行处理。
beforeBodyWrite 该方法判断如果返回值类型本身就是R类型就之间进行输出了,否则就进行包装处理。
测试接口
@GetMapping("/get1")
public Users get1() {
Users users = new Users() ;
users.setAge(100) ;
return users ;
}
包装成功!!!
修改测试接口
在接口中抛出一个异常
@GetMapping("/get1")
public Users get1() {
Users users = new Users() ;
users.setAge(100) ;
System.out.println( 1 / 0 ) ;
return users ;
}
当有异常的时候并不能被处理,这里我们需要结合统一异常处理的机制。
异常处理
@RestControllerAdvice
public class ExceptionControllerAdvice {
private static final Logger logger = LoggerFactory.getLogger(ExceptionControllerAdvice.class) ;
@ExceptionHandler(Exception.class)
public R jsonErrorHandler(HttpServletRequest req, Exception e){
String fullThrowMessage = getStackTrace(e);
if (e instanceof ServletRequestBindingException) {
return R.error("参数绑定异常,请检查接口入参: " + e.getMessage(), fullThrowMessage) ;
}
if (e instanceof ClassCastException) {
return R.error("请检查入参是否正确", fullThrowMessage) ;
}
if (e instanceof MethodArgumentNotValidException) {
return R.failure(((MethodArgumentNotValidException)e).getBindingResult().getAllErrors().stream().map(err -> err.getDefaultMessage()).collect(Collectors.toList())) ;
}
Throwable cause = e.getCause() ;
if (cause != null) {
if (cause instanceof SQLDataException) {
return R.error("数据格式错误,请检查入参是否正确", fullThrowMessage) ;
}
if (cause instanceof NumberFormatException) {
return R.error("数据格式错误,请检查!" + cause.getMessage(), fullThrowMessage) ;
}
}
logger.error("未捕获的异常:{}", e) ;
return R.error(e.getMessage(), fullThrowMessage) ;
}
private static String getStackTrace(final Throwable throwable) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
throwable.printStackTrace(pw);
return sw.getBuffer().toString() ;
}
}
在Springboot中可以通过@RestControllerAdvice注解标识一个类,让其可以统一处理未捕获的异常。查看这篇文章《Spring MVC 异常处理方式》该篇文章详细的讲解了异常的处理。
在进行测试
对于异常也统一的处理了。
实现原理
在Spring MVC中返回值的处理都是由
HandlerMethodReturnValueHandler对象进行处理的
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver implements HandlerMethodReturnValueHandler {
protected <T> void writeWithMessageConverters(@Nullable T value, ...) {
// 根据当前的请求获取客户端能产生的响应类型
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
// 遍历所有的HttpMessageConverter对象
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
// 在输出内容前先通过ResponseBodyAdvice处理内容信息
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
// ResponseBodyAdvice处理完后,最后通过GenericHttpMessageConverter输出内容到客户端了。
// 后续就是处理其它的比如拦截器的afterCompletion方法等操作了。
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
} else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
} else {
}
return;
}
}
}
}
完毕!!!
求关注+转发
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/79988.html