IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 统一处理controller层接口返回的数据 -> 正文阅读

[Java知识库]统一处理controller层接口返回的数据

@RestControllerAdvice和ResponseBodyAdvice统一处理controller层接口返回

1. 理论知识

要对controller层的内容进行统一返回,需要用到 @ControllerAdvice ResponseBodyAdvice

  • @RestControllerAdvice

一个方便的注释,它本身带有@ControllerAdvice和@ResponseBody注释,携带此注释的类型被视为控制器通知 总之,它就是对controller层的方法加强

  • ResponseBodyAdvice

允许在@ResponseBody或ResponseEntity控制器方法执行之后,但在使用HttpMessageConverter编写body之前定制响应

简单理解:ResponseBodyAdvice接口是在controller层方法执行之后,在response返回给前端数据之前对reponse的数据进行处理,可以对数据进行统一的处理,从而可以使返回数据格式一致。

2. 举例说明

  • 2.1 编写统一返回数据格式代码
@RestControllerAdvice
@Slf4j
public class UnifiedAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        //若加了@ResponseNotIntercept 则该方法不用做统一的拦截
        AnnotatedElement annotatedElement = returnType.getParameterType();
        ResponseNotIntercept annotation = AnnotationUtils.findAnnotation(annotatedElement, ResponseNotIntercept.class);
        return annotation == null;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof CommonResult) return body;
        CommonResult<Object> objectCommonResult = new CommonResult<>(ResultCode.SUCCESS, body);
        //若未封装 则对其进行封装
        return objectCommonResult;
    }
}
复制代码

说明:实现ResponseBodyAdvice接口 需要重写supports,beforeBodyWrite方法;

supports:是否支持给定的控制器方法返回类型和选定的HttpMessageConverter类型; 若不支持 则就不会对数据进行做统一处理,就像上面代码,若加了@ResponseNotIntercept注解,则不会进行拦截(@ResponseNotIntercept是自己自定义的一个注解)

参数:returnType:返回类型; converterType:选择的转换器类型

返回:若返回结果为true,则调用beforeBodyWrite

beforeBodyWrite:在选择HttpMessageConverter之后以及在调用其write方法之前调用。

参数:body:你传入的数据;returnType:controller层方法返回的类型;selectedContentType :通过内容协商选择的内容类型;selectedConverterType:选择要写入响应的转换器类型;request/reponse:当前请求和响应

返回:传入的数据或修改的(可能是新的)实例

  • 2.2编写contrller层方法
@RestController
@Slf4j
@RequestMapping("/baseInfo")
public class BaseInfoController {
    @Autowired
    private BaseInfoService baseInfoService;
    /**
     * 添加基本信息
     * @param info
     * @return
     */
    @PostMapping("/addBaseInfo")
    public BaseInfo addBaseInfo(@RequestBody BaseInfo info){
        BaseInfo baseInfo = baseInfoService.addBaseInfo(info);
        return baseInfo;
    }
}
复制代码

控制层返回的是一个对象,业务层 数据层方法省略 对只想执行后返回的结果做了统一的处理:

image.png

  • 2.3统一返回对象
@Data
@Slf4j
public class CommonResult<T> {
    private String code;
    private String message;
    private T Data;

    public CommonResult() {
    }

    public CommonResult(T data) {
        Data = data;
    }

    /**
     * 若只传入code码 默认传入的数据为null
     * @param rc
     */
    public CommonResult(ResultCode rc) {
        this(rc, null);
    }
    public CommonResult(ResultCode rc, T data) {
        this.code  = rc.getCode();
        this.message = rc.getMsg();
        this.Data = data;
    }


    public static<T> CommonResult<T> errorResult(ResultCode rc,T data){
        CommonResult<T> commonResult = new CommonResult<>();
        commonResult.code = rc.getCode();
        commonResult.message = rc.getMsg();
        commonResult.Data = data == null?(T) "" :data;
        log.error("{}",commonResult);
        return commonResult;
    }
 }
复制代码

ResultCode是自己自定的一些枚举类 例如部分:

image.png

3. 常见错误分析

但是就如上面这些就完全正确了吗?其实不然 若在controller层方法返回字符串会出现什么情况?请看:


@PostMapping("/test")
public String addBaseInfo(){
    return "我返回的是一个字符串";
}
复制代码

image.png

控制台报错:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.ClassCastException: com.flscode.publicApi.Response.CommonResult cannot be cast to java.lang.String

Caused by: java.lang.ClassCastException: com.flscode.publicApi.Response.CommonResult cannot be cast to java.lang.String

解决

分析: 正常数据返回:

image.png

字符串数据返回 打断点:

image.png

image.png

原因: SpringMVC 默认会注册一些自带的HttpMessageConvertor(从先后顺序排列分别为ByteArrayHttpMessageConverter、StringHttpMessageConverter、ResourceHttpMessageConverter,SourceHttpMessageConverter、AllEncompassingFormHttpMessageConverter),后端服务使用Restful API的形式,前后端得规范一般是json格式,SpringMVC 自带MappingJackson2HttpMessageConverter,在依赖中引入 jackson 包后,容器会把MappingJackson2HttpMessageConverter自动注册到 converter 链的末尾 (这端话来自:blog.csdn.net/weixin_4433…

因此 从上面两张图对比可以看出,此处得方法是要去循环遍历HttpMessageConverter集合,如果对应得转换器能够使用 则会使用该转换器,当你返回得数据是字符串时,因为StringHttpMessageConverter会先被遍历到,这时会认为StringHttpMessageConverter可以使用,因此在ResponseBodyAdvice封装数据时就会报错

修改后:

@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    if (body instanceof CommonResult) return body;
    CommonResult<Object> objectCommonResult = new CommonResult<>(ResultCode.SUCCESS, body);
    if (body instanceof String){
        String s = JSON.toJSONString(new CommonResult<>(ResultCode.SUCCESS, body));
        return s;
    }
    //若未封装 则对其进行封装
    return objectCommonResult;
}
复制代码

controller层:

@PostMapping(value = "/test",produces = "application/json; charset=UTF-8")
public String test(){
    return "我返回的是一个字符串";
}
复制代码

返回结果:

image.png

4. 总结

我们在前后端分离开发中,后端一般会返回一个统一格式给前端,若每在一个controller层写一个方法,就要封装一下CommonResult,这样就写了许多不必要得代码,因此,就在controller层每写一个方法,就让他返回它相应得数据即可,不用每次都去封装CommonResult对象,我们将封装CommonResult对象做统一拦截:利用 @RestControllerAdvice和 ResponseBodyAdvice做统一处理,在这个过程中要注意方法返回字符串要做相应得处理,原因可以参考第3点。


作者:又菜又想玩的XXX
链接:https://juejin.cn/post/6991635395758784520
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-13 11:49:15  更:2021-08-13 11:51:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年3日历 -2025/3/4 6:43:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码