SpringBoot之统一数据返回格式

目录

统一数据返回格式

使用

定义测试接口

运行结果

问题解答(结合源码)

解决方法:对返回类型为String类型的字符串进行序列化

优点


之前我们实现的图书管理系统,函数的返回值格式都不统一,如果将这些函数的返回值格式都进行统一,将方便后续的返回和接收,但是如果我们挨个修改这些函数的返回值格式,有些麻烦,那么有没有别的好的办法呢?----有,就是下面我们要讲解的"SpringBoot之统一数据返回格式"。

统一数据返回格式

使用

统⼀的数据返回格式使⽤ @ControllerAdvice 和 ResponseBodyAdvice 的⽅式实现

@ControllerAdvice 表⽰控制器通知类。

定义类 ResponseAdvice , 实现 ResponseBodyAdvice 接⼝, 并在类上添加

@ControllerAdvice 注解。

java 复制代码
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof Result){
            return body;
        }

        return Result.success(body);
    }
}

运行结果:

定义测试接口
java 复制代码
@RequestMapping("/test")
@RestController
public class TestController {
    @RequestMapping("/t1")
    public String t1(){
        return "string";
    }

    @RequestMapping("/t2")
    public Integer t2(){
        return 1;
    }

    @RequestMapping("/t3")
    public Boolean t3(){
        return true;
    }

}
运行结果

t2返回结果:

t3返回结果:

t1返回结果:

为什么其他方法都能够正常返回,而返回类型为String时却不能正常返回,而且还抛出了ClassCastException异常,这是为什么呢?

问题解答(结合源码)

根据IDEA控制台打印的堆栈信息,我们首先看看StringHttpMessageConverter和AbstractHttpMessageConverter这两个类,特别是StringHttpMessageConverter的addDefaultHeaders方法和AbstractHttpMessageConverter的write方法,其中StringHttpMessageConverter是继承了AbstractHttpMessageConverter并重写了几个方法,特别是addDefaultHeaders方法,下面通过Debug方式运行:

再继续运行,会捕获到异常

由此我们知道,是因为返回结果为String类型的"string"被转换成了Result类型,但是在执行AbstractHttpMessageConverter的write方法时,因为AbstractHttpMessageConverter的派生类StringHttpMessageConverter重写了addDefaultHeaders方法,此时会调用StringHttpMessageConverter的addDefaultHeaders方法,但是StringHttpMessageConverter的addDefaultHeaders方法要求 t 的类型是String类型,但是无法从自定义的Result类型转换成String类型,因此抛出了ClassCastException异常。

那么问题来了,上面的三个测试接口,走的都是Result.success()方法,为什么却有不一样的结果,通过调试:

对于返回类型为String类型,在AbstractMessageConverterMethodProcessor里面的writeWithMessageConverters()方法时,在经过下图所示过程中,String类型会被转换成Result类型的结果,而且后面走的是StringHttpMessageConverter的addDefaultHeaders方法,其中StringHttpMessageConverter重写了AbstractHttpMessageConverter的addDefaultHeaders方法;

而返回值类型不是String类型时,经过和刚刚同样的步骤时,直接走的是AbstractHttpMessageConverter的addDefaultHeaders方法,而没有走StringHttpMessageConverter的addDefaultHeaders方法。

解决方法:对返回类型为String类型的字符串进行序列化
java 复制代码
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import springbook.model.Result;

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof Result){
            return body;
        }
        if (body instanceof String){
            return objectMapper.writeValueAsString(Result.success(body));
        }
        return Result.success(body);
    }
}

运行结果:

优点
  1. ⽅便前端程序员更好的接收和解析后端数据接⼝返回的数据

  2. 降低前端程序员和后端程序员的沟通成本, 按照某个格式实现就可以了, 因为所有接⼝都是这样返回的.

  3. 有利于项⽬统⼀数据的维护和修改.

  4. 有利于后端技术部⻔的统⼀规范的标准制定, 不会出现稀奇古怪的返回内容.

相关推荐
大圣编程15 小时前
面向对象深度理解
java·开发语言·算法
影寂ldy15 小时前
C# const 常量 / readonly 只读 / static readonly
java·开发语言·c#
摇滚侠16 小时前
Maven 入门+高深 体系外 jar 包导入 172
java·maven·jar
做个文艺程序员16 小时前
第02篇:K8s 存储与配置管理:ConfigMap、Secret、PV/PVC 实战——Java SaaS 多租户配置最佳实践
java·容器·kubernetes
爱吃牛肉的大老虎16 小时前
Spring中用到的设计模式
java·spring·设计模式
Refrain_zc16 小时前
Android TV 语音消息实战:遥控器 PCM 录音失真修复与扬声器强制播放方案
java
Stick_ZYZ16 小时前
从“能调用工具”到“能稳定执行任务”:Agent 工程化的下一步
java·人工智能·后端·spring·ai
代码中介商16 小时前
C++四大设计模式:单例、工厂、观察者、策略
java·c++·设计模式
宋志宗16 小时前
从三层架构到清晰边界:一套更适合复杂 Java 服务的分层方法
java
lulu121654407816 小时前
Codex Computer Use 深度分析:AI桌面自动化的技术突破与行业影响
java·运维·人工智能·自动化·ai编程