@ControllerAdvice和ResponseBodyAdvice

@ControllerAdvice

@ControllerAdvice作用于@Controller修饰的类里面的所有方法。它主要有两种作用:

  1. 对Controller的入参进行预处理
  2. 对Controller中的异常进行全局统一处理

两种用法详见@ControllerAdvice 的介绍及三种用法

ResponseBodyAdvice

ResponseBodyAdvice作用于@ResponseBody注解修饰的方法,它可以对这些方法的返回值进行修改。它是一个接口,这个接口有两个函数:

/**
* @param returnType 可以得到方法和参数的相关信息(注解呀,类型呀)
* @param converterType HttpMessageConverter的实现类
* @return 是否对某个接口(被@ResponseBody修饰)的返回值进行修改。如果为true就会调用
*         beforeBodyWrite方法
*/
boolean supports(MethodParameter returnType, 
                Class<? extends HttpMessageConverter<?>> converterType);
/**
* @param body 被@ResponseBody修饰方法的返回值(区别于returnType)
* @param returnType 可以得到方法和参数的相关信息(注解呀,类型呀)
* @param selectedContentType 选中的媒体类型,即以什么格式写出数据(json、xml、text...)
* @param selectedConverterType HttpMessageConverter的实现类的具体类型
* @param request 请求对象
* @param response 响应对象
* @return
*/
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
			Class<? extends HttpMessageConverter<?>> selectedConverterType,
			ServerHttpRequest request, ServerHttpResponse response);

类上注释如下:

Allows customizing the response after the execution of an 
@ResponseBody or a ResponseEntity controller method but
 before the body is written with an HttpMessageConverter.

根据类上的注释可以得知如果满足以下两个条件,则返回值会被ResponseBodyAdvice的beforeBodyWrite方法修改,修改之后的值被HttpMessageConverter写出,返回给前端浏览器。

  1. Controller里的方法被@ResponseBody修饰或者Controller里的方法返回ResponseEntity
  2. ResponseBodyAdvice的supports方法返回true

注意两者的执行顺序

如果针对异常情况和正常情况我们都做了统一处理,要留意不要重复处理。

示例

新建一个Advice类,它被@ControllerAdvice修饰,又实现了ResponseBodyAdvice接口。我们希望在这个类里面既能实现全局异常处理,又能对后端返回的数据统一封装。

返回结果封装类

在Controller里可以返回任意类型,他们都会被封装到Result的data属性中。

public class Result {
    private int code;
    private String data;
    // getter and seter
}

全局处理异常,全局封装返回值

@ControllerAdvice
public class Advice implements ResponseBodyAdvice {
    //因为这里也加了@ResponseBody注解,所以它的返回值也会被ResponseBodyAdvice 处理一遍
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result he(Exception e) {
        Result res = new Result();
        res.setCode(500);
        res.setData(e.getMessage());
        return res;
    }

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        //返回任意类型都要封装
        return true;
    }

    ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType, Class selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {

        Result res = new Result();
        res.setCode(200);
        //如果返回值是String,直接放到Result里
        if (body instanceof String) {
            res.setData((String) body);
            return res;
        }
        //如果返回值是标准返回格式,就不需要再次封装了
        //如果不加这个判断,异常的结果会被封装两次
        else if (body instanceof Result) {
            return body;
        }
        String dataStr = null;
        try {
            dataStr = objectMapper.writeValueAsString(body);
            res.setData(dataStr);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return res;
    }
}

注意:Advice类加了@ControllerAdvice注解,并实现了ResponseBodyAdvice

接口

简单起见,直接在启动类里面写RESTful接口。一个抛出异常,一个返回一个实体类。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@Controller
public class Demo1Application {

    public static void main(String[] args) {
        SpringApplication.run(Demo1Application.class, args);
    }

    @GetMapping("ex")
    @ResponseBody
    public String ex() throws Exception {
        throw new Exception("异常信息");
    }

    @GetMapping("stu")
    @ResponseBody
    public Stu s() {
        final Stu stu = new Stu();
        stu.setName("zcx");
        stu.setAge(22);
        return stu;
    }
}


class Stu {
    private String name;
    private int age;
    //getter and setter
}

原理

因为被@ResponseBody注解注释的返回值都会被RequestResponseBodyMethodProcessor处理,它里面的
HttpMessageConverter在写出数据时,会先拿到一个RequestResponseBodyAdviceChain,先用RequestResponseBodyAdviceChain里面的responseBodyAdvice对Controller返回值进行处理,再写到浏览器。

拓展

与ResponseBodyAdvice类似的有RequestBodyAdvice,它可以对@RequestBody注释的参数进行额外处理,在使用时注意不要在beforeBodyRead里面把inputMessage的body读出来,否则会有I/O异常。可以使用afterBodyRead对参数进行修改。

参考

@ControllerAdvice 的介绍及三种用法

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值