统一处理controller层接口返回的数据

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


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

  1. 理论知识

 

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

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

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

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

  1. 举例说明

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;
}

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

2.3统一返回对象
@Data
@Slf4j
public class CommonResult {

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是自己自定的一些枚举类 例如部分:

  1. 常见错误分析

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

@PostMapping("/test")
public String addBaseInfo(){

return "我返回的是一个字符串";

}
复制代码

控制台报错:

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

解决

分析: 正常数据返回:

字符串数据返回 打断点:

原因: 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 "我返回的是一个字符串";

}
复制代码
返回结果:

  1. 总结

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

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

相关文章
|
监控 Kubernetes 负载均衡
spring boot应用优化,6s内启动,内存减半
taptap-developer是一个spring boot框架驱动的纯Grpc服务,所以,只用了四步,移除了web和spring cloud相关的模块后,启动速度就稳稳的保持在了6s内。除了启动速度提升外,在服务待机状态下,内存锐减了50%左右,从500M左右的内存占用,缩减到了250M不到。
10354 113
spring boot应用优化,6s内启动,内存减半
|
存储 SQL 搜索推荐
业务系统架构实践总结
作者从2015年起至2022年,在业务平台(结算、订购、资金)、集团财务平台(应收应付、账务核算、财资、财务分析、预算)、本地生活财务平台(发票、结算、预算、核算、稽核)所经历的业务系统研发实践的一个总结。1.核心是面向复杂性业务支撑的实践经验(个人概念里的“复杂业务“,大概至少面向5类行业若干业务线且业态差异很大),文章不涉及性能、稳定性、资损防控、大数据离线研发,聚焦在线业务系统架构对多态业务的包容性、开放性、灵活性、可读性。2.文章较多强调”个人”两字,因为仅是我个人在实践上归纳总结的一些方式方法。3.实践经验主要来自两类,一类是接手旧系统,得以见识不一样的设计,文中“见过”特指。
3339 32
|
存储 负载均衡 应用服务中间件
分布式文件系统FastDFS
 FastDFS是一个开源的轻量级分布式文件系统,由阿里巴巴余庆大神开发,底层采用C语言,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
516 0
|
1月前
|
人工智能 运维 IDE
阿里云Qoder CN完整介绍:全系列产品、核心能力、计费标准与落地场景说明
2026年阿里云原通义灵码完成品牌升级,正式命名Qoder CN,产品定位从单一代码辅助工具升级为覆盖软件开发、日常办公、云端自动化、数字员工的全栈国产AI智能体产品家族。整套产品依托国内云服务部署,兼容GLM、DeepSeek、Kimi、MiniMax等多款本土大模型,满足金融、政务等高合规行业的数据安全需求。产品线包含编程专用Qoder CN、桌面办公QoderWork CN、终端CLI、云端Cloud Agents、7×24数字员工QoderWake CN等多款子产品,同时推出个人、企业多层级订阅体系,采用统一Credits积分计量模式,下文从产品矩阵、核心能力、落地场景、计费规则、优惠
553 0
|
前端开发 小程序 Java
【规范】SpringBoot接口返回结果及异常统一处理,这样封装才优雅
本文详细介绍了如何在SpringBoot项目中统一处理接口返回结果及全局异常。首先,通过封装`ResponseResult`类,实现了接口返回结果的规范化,包括状态码、状态信息、返回信息和数据等字段,提供了多种成功和失败的返回方法。其次,利用`@RestControllerAdvice`和`@ExceptionHandler`注解配置全局异常处理,捕获并友好地处理各种异常信息。
8441 1
【规范】SpringBoot接口返回结果及异常统一处理,这样封装才优雅
|
应用服务中间件 网络安全 nginx
配置Nginx以支持Websocket连接的方法。
通过上述配置,Nginx将能够理解WebSocket协议的特殊要求,代理Websocket流量到合适的后端服务器。注意,Websocket并不是HTTP,尽管它最初是通过HTTP请求启动的连接升级,因此保证Nginx了解并能够妥善处理这种升级流程是关键。
2485 10
|
缓存 前端开发 Java
【Spring】——SpringBoot项目创建
SpringBoot项目创建,SpringBootApplication启动类,target文件,web服务器,tomcat,访问服务器
|
SQL Java 数据库连接
mybatisplus QueryWrapper or写法
# MyBatis-Plus QueryWrapper的OR写法详解 MyBatis-Plus是一款基于MyBatis的增强工具,提供了丰富的简化操作,使开发者能更高效地进行数据库操作。`QueryWrapper`是MyBatis-Plus中用于构建查询条件的一个核心类,支持多种条件组合,包括AND和OR条件。本文将详细介绍如何使用 `QueryWrapper`实现OR条件的查询。 ## QueryWrapper简介 `QueryWrapper`用于构建动态SQL查询条件,它封装了各种条件构造方法,使得查询条件的构建更加简洁和直观。`QueryWrapper`中提供了丰富的方法来支持多
3252 0
|
JSON 编解码 安全
【HTTP】方法(method)以及 GET 和 POST 的区别
【HTTP】方法(method)以及 GET 和 POST 的区别
664 1
|
Python
Python 解析 yaml 配置文件
Python 解析 yaml 配置文件
779 0