ResponseBodyAdvice类使用以及问题处理

  • 作用:

    允许在执行标有@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;
				}
			}
		}
  • 源码分析
  1. 遍历this.messageConverters,识别到converter转换器是StringHttpMessageConverter

  2. 调用自定义的beforeBodyWrite方法,得到body类型是Resp类型数据

  3. 采用StringHttpMessageConverter处理Resp类型的数据,导致类型转换异常

  1. MappingJackson2HttpMessageConverter、StringHttpMessageConverter

    long、int等类型使用MappingJackson2HttpMessageConverter转化器处理

    String类型使用 StringHttpMessageConverter转化器处理

  2. 解决方案

    在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);
        }
    
    }
    
posted @ 2022-01-01 21:35  永无八哥  阅读(1567)  评论(0编辑  收藏  举报