ResponseBodyAdvice和HttpMessageConverter应用浅析

目录

1 使用ResponseBodyAdvice

2 使用HttpMessageConverter

3 使用ResponseBodyAdvice和RequestBodyAdvice给controller做环绕日志

4 源码跟踪


此文的目的是想要对@ResponseBody修饰的方法的返回值,从大写下划线转成小写驼峰,而突然发现官方文档表示“HandlerInterceptor的postHandle() 不支持@ResponseBody、@ResponseEntity修饰方法输出结果的处理,应该使用@ResponseBodyAdvice”。

同时官方文档SpringWebMvc一章中1.10MvcConfig章节的1.10.7MessgeConverters小节也有类似功效。

所以写此文记录下对两者的使用。

本文提到的大写下划线转驼峰工具见 https://blog.csdn.net/qq_26950567/article/details/115102515?spm=1001.2014.3001.5501

1 使用ResponseBodyAdvice

需要用@ControllerAdvice注入

@ControllerAdvice // reponseBodyAdvice可以用@ControllerAdvice注入
public class CamelCaseResponseBodyAdvice implements ResponseBodyAdvice<BeeActionResponse> {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * 需要满足:
     * 1. 需要转换的方法被@NeedConvert(converterName="camelCaseConverter")修饰
     * 2. 返回BeeActionResponse的组合类型
     * @param returnType
     * @param converterType
     * @return
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        NeedConvert methodAnnotation = returnType.getMethodAnnotation(NeedConvert.class);
        if (methodAnnotation == null)
            return false;
        else
            return "camelCaseConverter".equals(returnType.getMethodAnnotation(NeedConvert.class).converterName())
                            && BeeActionResponse.class.isAssignableFrom(returnType.getMethod().getReturnType());
    }

    @Override
    public BeeActionResponse beforeBodyWrite(BeeActionResponse body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        Object camelParse = CamelCaseUtils.underLineToCamelCase(body.getRespData());
        BeeActionResponse beeActionResponse = new BeeActionResponse();
        beeActionResponse.setRespCode(body.getRespCode());
        beeActionResponse.setRespMsg(body.getRespMsg());
        beeActionResponse.setTraceId(body.getTraceId());
        beeActionResponse.setRespData(camelParse);
        beeActionResponse.setPager(body.getPager());
         return beeActionResponse;
        //return body;
    }
}

2 使用HttpMessageConverter

  1. 可以用@Component直接注入
  2. CanWrite表示哪种返回类型的方法,可被我这个HttpMessageConverter改写输出结果
  3. GetSupportedMediaType写空集合就行
  4. 具体的改写输出逻辑,在write方法中进行

@Component
public class CamelCaseConverter implements HttpMessageConverter<BeeActionResponse> {
    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return false;
    }
    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return BeeActionResponse.class.isAssignableFrom(clazz);
    }
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return Collections.unmodifiableList(Collections.emptyList());
    }
    @Override
    public BeeActionResponse read(Class<? extends BeeActionResponse> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }
    @Override
    public void write(BeeActionResponse response, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        Object camelParse = CamelCaseUtils.underLineToCamelCase(response.getRespData());
        outputMessage.getBody().write(JSONObject.toJSONString(camelParse).getBytes());
    }
}

3 使用ResponseBodyAdvice和RequestBodyAdvice给controller做环绕日志

/**
 *
 * @author onion.li
 */
@Slf4j
@ControllerAdvice
public class AroundAdvice implements RequestBodyAdvice,ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    /**
     * RequestBody读之前要做什么操作
     * 什么都不做
     */
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        return httpInputMessage;
    }

    /**
     * RequestBody读之后要做什么操作
     * 打印日志
     */
    @Override
    public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        Method method=methodParameter.getMethod();
        String eventName = method.getDeclaringClass().getSimpleName() + "#" + method.getName();
        log.info("req" + JSONObject.toJSONString(o));
        return o;
    }

    /**
     * 若RequestBody为空怎么办
     * 打印日志
     */
    @Override
    public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        Method method=methodParameter.getMethod();
        String eventName = method.getDeclaringClass().getSimpleName() + "#" + method.getName();
        log.info("req" + null);
        return null;
    }

    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        return true;
    }

    /**
     * reponseBody返回之前要做什么操作
     * 写日志
     */
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        Method method=methodParameter.getMethod();
        String eventName = method.getDeclaringClass().getSimpleName() + "#" + method.getName();
        log.info("resp" + JSONObject.toJSONString(o));
        return o;
    }
}

4 源码跟踪

可以看出ReponseBodyAdvice对输出的改写,同HttpMessageConverter对输出的改写,两者是挨着的,所以我觉得一般情况,使用ResponseBodyAdvice就足够了。

下图是HttpMessageConverter在springmvc的请求链路中的作用。

每个httpMessgeConverter能告诉我们

它支持什么样的协议 application/json、text/plain 还是其他。

它能够将httpMessge转为什么对象,是字符串、对象、还是别的什么。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值