springboot入门05 – 包装SpringBoot Controller返回值

一个项目使用了SpringBoot,需要对Controller的返回值进行二次包装。包装类结构大致如下:

通过查找资料,找到了两种封装方式。

方法一

第一种方式是替换掉RequestResponseBodyMethodProcessor,这需要使用一个MethodReturnValueHandler的装饰类:

在装饰类中使用一个Result类的实例替换了returnValue。而后在InitializingBean中基于原来的RequestResponseBodyMethodProcessor的实例创建一个ResponseBodyWrapHandler的实例来完成替换:

使用ResponseBodyWrapFactoryBean,完成afterProperties方法的调用,只需要创建一个ResponseBodyWrapFactoryBean的实例即可:

这行代码可以放在启动类中。

方法二

第二种方式基于ControllerAdvice和HttpMessageConverter实现。

首先用一个ResponseBodyAdvice类的实现包装Controller的返回值:

如果Controller类的返回值没有String类型的,仅有上面这个类就够了。如果有String类型的返回值,就有可能遇到类型不匹配的问题。HttpMessageConverter是根据Controller的原始返回值类型进行处理的,而我们在ResponseAdvisor中改变了返回值的类型。如果HttpMessageConverter处理的目标类型是Object还好说,如果是其它类型就会出现问题,其中最容易出现问题的就是String类型,因为在所有的HttpMessageConverter实例集合中,StringHttpMessageConverter要比其它的Converter排得靠前一些。我们需要尝试将处理Object类型的HttpMessageConverter放得靠前一些,这可以在一个Configuration类中完成:

在这个方案中,如需要对异常做些特别处理,还可以创建一个ExceptionAdvisor类来完成:

这样还可以根据异常类型来设置返回时的HttpStatus。

就这样。

有朋友在评论区指出问题了,做了些调整,也写了一份示例程序上传到CSDN。有兴趣的可以下载来看看。下载地址如下:点击此处下载

这是许久之前刚用springboot时写的,现在适应springboot的最新版本存在一些问题。所以稍稍重新调整了下,并将之加入到了最近正在尝试进行的一个系列里面。

新版本的代码可以在GitHub / zhyea上看到。

##########

已有15条评论 发表评论

  1. response /

    第一种方法,测试了发现不可以啊,是不是需要添加注解啊,还有一种利用@around来处理返回结果,和楼主的两种处理方式相比有什么优劣

    1. robin / 本文作者

      第一种方法我反复测试过了,是没有问题的。如有报错,可以把错误信息贴一下。
      @Around是Spring标准的AOP处理方案。也考虑过类似的思路,不过不太理想。

    2. wangyupeng /

      返回的类的数据必须要有getter,(可能需要有setter)

  2. syystx /

    用第一种方式,一直报错,类型不匹配
    java.lang.IllegalArgumentException: Unknown return value type

    1. robin / 本文作者

      现在提供了示例代码。

  3. luoqiz /

    No converter found for return value of type: class com.Test.Result
    没有Result类的转换器

    1. robin / 本文作者

      恩,偷懒来着。没有为Result设置get&set方法。现在已作了修正。

  4. chenchen /

    第二种方法lz试了吗

    1. robin / 本文作者

      现在附上了示例代码,见文末

  5. finalriod /

    不清楚作者的版本,目前springboot2.0,两种方法均无法实现,最后spring均会对方法声明的返回值类型和实际返回值类型进行对比,不一致直接抛出异常

    1. robin / 本文作者

      版本为spring-boot-starter-web:1.5.6.RELEASE。详情可以参考提供的示例代码。2.0没有试过,不妄言。

    2. 赵海堂 /

      2.0 第二种可以用。

  6. XY /

    感谢,找了很久Stack Overflow 都没有结果,博主的解决最详细,原理描述清晰,感谢。

  7. 陈先生er /

    纠正一下:
    使用 第 2 种方式的时候,其实是可以的。 并不需要 将 FastJsonMessageConvert 提前。 因为 FastJsonMessageConvert 一直都是在 String 转换器前面的。

    如果你是用浏览器请求, content-type 会是 text/plain。 因此 spring 会认为你是要请求 一个 页面。 从而会找不到任何可以处理该返回值的处理。

    如果你是使用接口,或者用 postMan 测试。 content-type 会是 application/json。那么这个时候 FastJson 是可以处理的。

  8. yxe /

    第二种方法返回值为null的时候,楼主有没有测试过,根本就走不到beforeBodyWrite方法中

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据