- 作用:
允许在执行标有@ResponseBody注解或响应内容是ResponseEntity的控制器方法之后,但在使用HttpMessageConverter类编写主体之前自定义响应。
- 实践:
使用ResponseBodyAdvice统一处理包装Controller方法中返回值,不用在每个方法都重复写Result<类型>
- 说明:
- 是否执行增强的方法beforeBodyWrite()
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
// ......
}
- 对返回结果集增强的逻辑
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
// ......
}
问题一
在beforeBodyWrite方法中如果直接返回Resp对象,对字符串类型不做任何处理。会导致方法(writeWithMessageConverters)使用字符串转化器(StringHttpMessageConverter)处理Resp对象,出现异常问题。
- AbstractMessageConverterMethodProcessor源码分析
// 对返回的String类型进行了特殊处理
if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
else {
body = value;
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
// ...........
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
// 1.遍历this.messageConverters,识别到converter转换器是StringHttpMessageConverter
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
// 2.调用自定义的beforeBodyWrite方法,得到body类型是Resp类型数据
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
// 3.采用StringHttpMessageConverter处理Resp类型的数据,导致类型转换异常
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}
- 源码分析
- 遍历this.messageConverters,识别到converter转换器是StringHttpMessageConverter
- 调用自定义的beforeBodyWrite方法,得到body类型是Resp类型数据
- 采用StringHttpMessageConverter处理Resp类型的数据,导致类型转换异常
- MappingJackson2HttpMessageConverter、StringHttpMessageConverter
long、int等类型使用MappingJackson2HttpMessageConverter转化器处理
String类型使用 StringHttpMessageConverter转化器处理
- 解决方案
在beforeBodyWrite方法中进行处理,如果遇到字符串,则提前进行处理返回。
问题二
- 不处理Resp统一返回对象,使得报文data数据显示不对
{
"code": 200,
"msg": "SUCCESS",
"success": true,
"data": {
"code": 400,
"msg": "请求失败!",
"success": false,
"data": null
}
}
- 原因:先进行了异常处理,然后再走了beforeBodyWrite方法
- 解决方法:如果body为Resp对象,则直接返回
if (body instanceof Resp) {
return body;
}
问题三
- 结合swagger使用时,导致swagger页面访问不了
- 原因:
swagger相关api返回的数据默认也走了beforeBodyWrite方法,并对其数据进行了处理,导致swagger获取不到它想要的格式
- 解决方法
在supports中排除swagger相关类
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
// 排除拦截swagger相关
return !returnType.getDeclaringClass().getName().contains(SPRING_FOX_STR);
}
代码如下
/**
* @Description: 拦截Controller方法的返回值,统一处理返回值
* @Author party-abu
* @Date 2021/12/27 16:47
*/
@RestControllerAdvice
public class GlobalResultHandler implements ResponseBodyAdvice<Object> {
private static final Logger log = LoggerFactory.getLogger(GlobalResultHandler.class);
private static final String SPRING_FOX_STR = "springfox";
@Autowired
private ObjectMapper objectMapper;
/**
* 是否执行 beforeBodyWrite方法
* true:执行 false:不执行
*
* @param returnType
* @param converterType
* @return
*/
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
// 排除拦截swagger相关
return !returnType.getDeclaringClass().getName().contains(SPRING_FOX_STR);
}
/**
* 对返回值进行处理
*
* @param body
* @param returnType
* @param selectedContentType
* @param selectedConverterType
* @param request
* @param response
* @return
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
// 遇到字符串时,提前返回处理,
// 避免使用StringHttpMessageConvert处理使用Resp类型封装字符串后的数据,导致数据处理异常问题
if (body instanceof String) {
try {
return objectMapper.writeValueAsString(Resp.data(body));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
// 解决与统一异常处理产生的冲突问题
if (body instanceof Resp) {
return body;
}
return Resp.data(body);
}
/**
* 业务异常统一管理
*
* @param biz
* @return
*/
@ExceptionHandler(BizException.class)
public Resp<Object> bizExceptionHandler(BizException biz) {
log.error("业务异常 状态码:{},提示信息:{}", biz.getCode(), biz.getMsg());
return Resp.fail(biz.getCode(), biz.getMsg());
}
/**
* 全局异常统一管理
*
* @param r
* @return
*/
@ExceptionHandler(RuntimeException.class)
public Resp<Object> bizExceptionHandler(RuntimeException r) {
r.printStackTrace();
return Resp.fail(ResultCodeEnum.INTERNAL_SERVER_ERROR);
}
}