( 十八 )、 SpringMVC 之 ResponseBodyAdvice
( 十八 )、 SpringMVC 之 ResponseBodyAdvice
1、简介
ResponseBodyAdvice接口属于springMVC 框架基础的底层切面接口;实现这个接口的类,可以修改Controller的返回值,即进行功能增强。
源码如下:
public interface ResponseBodyAdvice<T> {
/**
* Whether this component supports the given controller method return type
* and the selected {@code HttpMessageConverter} type.
* @param returnType the return type
* @param converterType the selected converter type
* @return {@code true} if {@link #beforeBodyWrite} should be invoked;
* {@code false} otherwise
*/
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
/**
* Invoked after an {@code HttpMessageConverter} is selected and just before
* its write method is invoked.
* @param body the body to be written
* @param returnType the return type of the controller method
* @param selectedContentType the content type selected through content negotiation
* @param selectedConverterType the converter type selected to write to the response
* @param request the current request
* @param response the current response
* @return the body that was passed in or a modified (possibly new) instance
*/
@Nullable
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response);
}
- supports: 是否支持处理该方法的返回值,返回true 才会执行 beforeBodyWrite。
- beforeBodyWrite: 在选择HttpMessageConverter之后和调用它的 write 方法之前调用。
2、使用示例:
应用场景在Spring项目开发过程中,对controller层返回值进行修改增强处理。比如返回值: "hello-world",需要封装成 {"code":"0", "data":"hello-world", "msg":"success"} 格式返回给前端。
2.1、定义统一响应格式:
@Data
public class Result<T> {
/**
* 业务错误码
*/
private Integer code;
/**
* 信息描述
*/
private String message;
/**
* 返回参数
*/
private T data;
private Result(IResultStatus resultStatus, T data) {
this.code = resultStatus.code();
this.message = resultStatus.message();
this.data = data;
}
private Result(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
/**
* 业务成功返回业务代码和描述信息
*/
public static Result<Void> success() {
return new Result<Void>(ResultStatus.SUCCESS, null);
}
/**
* 业务成功返回业务代码,描述和返回的参数
*/
public static <T> Result<T> success(T data) {
return new Result<T>(ResultStatus.SUCCESS, data);
}
/**
* 业务成功返回业务代码,描述和返回的参数
*/
public static <T> Result<T> success(IResultStatus resultStatus, T data) {
if (resultStatus == null) {
return success(data);
}
return new Result<T>(resultStatus, data);
}
/**
* 业务成功返回业务代码,描述和返回的参数
*/
public static <T> Result<T> success(Integer code, String message, T data) {
return new Result<T>(code, message, data);
}
/**
* 业务异常返回业务代码和描述信息
*/
public static <T> Result<T> fail() {
return new Result<T>(ResultStatus.BUSINESS_ERR, null);
}
/**
* 业务异常返回业务代码和描述信息
*/
public static <T> Result<T> fail(String message) {
return new Result<T>(ResultStatus.BUSINESS_ERR.code(), message, null);
}
/**
* 业务异常返回业务代码,描述和返回的参数
*/
public static <T> Result<T> fail(IResultStatus resultStatus) {
return fail(resultStatus, null);
}
/**
* 业务异常返回业务代码,描述和返回的参数
*/
public static <T> Result<T> fail(IResultStatus resultStatus, String message) {
if (resultStatus == null) {
return new Result<T>(ResultStatus.BUSINESS_ERR, null);
}
return new Result<T>(resultStatus.code(), message, null);
}
}
2.2、定义状态码接口(方便扩展)、枚举类
public interface IResultStatus {
/**
* 业务状态码
* @return Integer
*/
Integer code();
/**
* 业务信息描述
* @return String
*/
String message();
}
2.3、状态码枚举
public enum ResultStatus implements IResultStatus {
/**
* 成功
*/
SUCCESS(200, "OK"),
/**
* 错误的请求,参数异常
*/
BAD_REQUEST(400, "Bad Request params exception"),
/**
* 服务器异常
*/
INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
/**
* 401 未认证异常
*/
UNAUTHORIZED(401, "认证异常"),
/**
* 业务异常
*/
BUSINESS_ERR(10000, "业务异常");
/**
* 业务状态码
*/
private Integer code;
/**
* 业务信息描述
*/
private String message;
ResultStatus (Integer code, String message) {
this.code = code;
this.message = message;
}
/**
* 业务状态码
*
* @return Integer
*/
@Override
public Integer code() {
return code;
}
/**
* 业务信息描述
*
* @return String
*/
@Override
public String message() {
return message;
}
}
2.3、ResponseBodyAdvice 实现类
@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {
private static final Logger log = LoggerFactory.getLogger(ResponseResultBodyAdvice.class);/**
* 判断Controller方法返回值是否支持
*/
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// 如果接口返回的类型本身就是Result那就没有必要进行额外的操作,返回false
if (returnType.getGenericParameterType().equals(Result.class)) {
return false;
}
// 对类或者方法上面注解了@RestController 或者 @ResponseBody 的方法统一处理
return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), RestController.class) ||
returnType.hasMethodAnnotation(ResponseBody.class);
}
/**
* 当类或者方法使用了 @ResponseResultBody 就会调用这个方法
*/
@Override
public Object beforeBodyWrite(Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {
// 防止重复包裹的问题出现
if (body instanceof Result) {
return body;
}
// String类型不能直接包装,所以要进行些特别的处理
if (returnType.getGenericParameterType().equals(String.class)) {
ObjectMapper objectMapper = new ObjectMapper();
try {
// 将数据包装在Result里后,再转换为json字符串响应给前端
return objectMapper.writeValueAsString(Result.success(body));
} catch (JsonProcessingException e) {
throw new BusinessException("返回String类型错误");
}
}
Result<Object> success = Result.success(body);
return success;
}
}