Spring统一格式返回

news/2025/3/6 19:27:52/

目录

一:统一结果返回

1:统一结果返回写法

2:String类型报错问题

解决方法

二:统一异常返回

统一异常返回写法

三:总结


同志们,今天咱来讲一讲统一格式返回啊,也是好久没有讲过统一格式返回了,说实话这个统一格式返回就是一个让咱返回的数据能有一个统一的格式嘛,不为别的就为了和你一块共事的同事不会提着刀追着你满大街跑,要跟你交流交流工作经验。

前端:我这数据咋老是对不上啊?

后端:奥兄弟,这个接口我返回的是Boolean类型

前端:我这数据怎么又对不上了啊?

后端:奥好兄弟,我觉得Boolean类型太丑了我就换成String类型了,我觉得它顺眼点。

前端:

那咱肯定不能让同事追着我们交流经验啊,所以我们一定要让咱返回的数据能有一个统一的格式

一:统一结果返回

1:统一结果返回写法

定义返回模板

想要统一返回一个结果,就肯定要有一个返回结果的模板这里我们用Result类来定义

下面代码中有Result有三个属性,状态码,错误信息,返回的数据,三个方法分别是成功时返回,和失败时返回。

java">@Data
public class Result<T> {private Integer code;//后端响应状态码,成功200,失败-1,-2表示未登录private String errmsg;//后端发生错误的原因private T data;//每个接口返回的类型(BookInfo,boolean之类的,类型不固定所以要用泛型)/** 成功时设置* */public  static<T> Result<T> success(T data){//泛型方法加static需要加上<T>Result result = new Result<>();result.setData(data);result.setCode(200);return result;}/*失败时设置* */public  static<T> Result<T> fail(String errMsg){Result result = new Result<>();result.setCode(-1);result.setErrmsg(errMsg);return result;}public  static<T> Result<T> fail(T data,String errMsg){Result result = new Result<>();result.setData(data);result.setCode(-1);result.setErrmsg(errMsg);return result;}
}

实现ResponseBodyAdvice接口并重写方法,再加上@ControllerAdvice注解

@ControllerAdvice:这个注解的作用就是标记这个类为全局控制器增强类

java">@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {return Result.success(body);//返回封装后结果结果}
}

support()方法:

作用:

supports 方法用于判断是否需要对某个返回值进行处理,返回true就是处理,返回false就是不处理

参数解析:

returnType:这个参数就是表示控制器方法(Controller类)的返回类型信息,它封装的有方法的返回参数的详细信息,比如方法本身,方法所属的类,返回的类型等等...可以根据这些信息来决定是否要对返回值进行处理,返回对应的true或false

Class converterType:这个参数的意思就是用于将响应体对象,转换为,HTTP响应消息的那个消息转换器的类型,后面我们会说到,先按下不表

beforeBodyWrite()方法:

作用:

我们直译一下这个方法的名字是:写 body 之前 ,意思就是在控制器方法返回之前进行的处理

当support方法返回true之后,beforeBodyWrite()方法就需要对返回值进行一些处理

参数解析:

Object body:该参数表示控制器方法实际返回的响应体对象

MethodParameter returnType:同supports方法中的returnType

 MediaType selectedContentType:表示响应的类型,如application/json

 Class selectedConverterType:表示用于将响应体对象转换为 HTTP 响应的消息转换器的类型

 request和response:这个我们很熟悉,就是代表Http的请求和响应

我们定义几个简单的控制器类

java">@RestController
public class Controller {@RequestMapping("/t1")public int t1(){return 1;}@RequestMapping("/t2")public Boolean t2(){return true;}@RequestMapping("/t2")public String t3(){return "老皇甫";}
}

然后启动项目测试发现

t1和t2方法没问题,都对控制器的返回结果进行了封装,但是返回String类型的t3却报错了,这是为什么呢?

 

2:String类型报错问题

首先明确一点,这个报错的原因是类型不匹配问题,报的是ClassCastException

我黄色框框里面框的翻译一下就是Result类型和String类型不匹配,那么为什么呢?

还记得我在解释方法的参数的时候那个先按下不表的Class converterType 参数吗?converterType的意思是转换器类型,SpringMVC默认会注册⼀些⾃带的 HttpMessageConverter(Http消息转换器),它是以链表的形式组织的,它们的顺序是

 ByteArrayHttpMessageConverter()->StringHttpMessageConverter()
->SourceHttpMessageConverter<>()->AllEncompassingFormHttpMessageConverter()

 其中这个AllEncompassingFormHttpMessageConverter()是根据项目的依赖情况来添加对应的转换器的,如果我们添加了Jackson依赖一般会添加MappingJackson2HttpMessageConverter()转换器到消息转换器链表的末尾

Spring会根据返回的数据类型, 从 messageConverters 链(就是那个链表)选择 合适的消息转换器 .
当返回的数据是⾮字符串时, 使⽤的 MappingJackson2HttpMessageConverter 写⼊返回对象(那个在链表末尾的消息转换器)
.
当返回的数据是字符串时 StringHttpMessageConverter 会先被遍历到,这时会认为
StringHttpMessageConverter 可以使⽤,然后就会用StringHttpMessageConverter

我们下面调用的堆栈信息中也发现,最后AbstractMessageConverterMethodProcessor调用的也是StringHttpMessageConverter

这里是AbstractMessageConverterMethodProcessor中的逻辑,body在经过beforeBody方法包装过之后,就会从String类型变为Result类型,但现在匹配到的还是StringHttpMessageConverter 消息转换器

我们点进去这个 StringHttpMessageConverter 的write方法中看一下(点击那个由AbstractHttpMessageConverter实现的

)发现里面有一个addDefaultHeader方法(由 StringHttpMessageConverter实现的),再点进去这个方法,发现这个方法接收到的是String参数,但在上面的我们在beforBodyWrite方法中已经将参数转换为了Result类型,所以才会报出类型不匹配异常

解决方法

既然是因为参数不匹配导致的错误,那就只需要将参数搞成匹配的就行了,如下图所示,如果返回的是字符串类型,那么就将返回类型序列化成字符串类型,而不是Result类型
 

我知道同志们有时候看源码很懵,不知道哪个调用哪个,这里可以说一下在控制台打印的日志中,调用的顺序一般就是下面的调用上面的,一层一层的,Spring的调用链几乎都有十几层,所以看的很懵是很正常的 

二:统一异常返回

还有一个问题,不知道同志们发现没有,就是上面我们在由于String类型不匹配报错的时候哪个返回结果状态码竟然还是200,但这个200可是成功的状态码,这都报错了,那状态码肯定能是200啊,所以这个返回结果肯定是不正确的。

统一异常返回写法

这时候我们就需要通过统一异常捕获,构造出另一种返回结果失败的格式,来返回我们可以用

@ControllerAdvice + @ExceptionHandler 两个注解来实现
写法非常简单,就是加上个@ExceptionHandler后面指定要捕获哪种类型的异常,然后进行对应的封装逻辑,Exception你也可以进行自定义异常,来满足你不同的项目需求
因为我们希望给客户端返回的是数据类型,而不是一个视图,所以要加上@ResponseBody注解
java">@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {@ExceptionHandler(Exception.class)//捕获所有异常public Result handleException(Exception e) {return Result.fail(e.getMessage()+"异常");}@ExceptionHandler(Error.class)//捕获error类型public Result handleError(Error e) {return Result.fail(e.getMessage()+"错误");}@ExceptionHandler(RuntimeException.class)//捕获运行时异常public Result handleRuntimeException(RuntimeException e) {return Result.fail(e.getMessage()+"运行时异常");}@ExceptionHandler(NullPointerException.class)//捕获空指针异常public Result handleNullPointerException(NullPointerException e) {return Result.fail(e.getMessage()+"空指针异常");}}

我们来认为制造几个异常

查看测试结果

状态码为-1,返回结果符合预期

三:总结

这篇我们说的也不多,大概就是说了一下

为什么要进行统一的格式返回,

然后统一格式返回的写法

再是String类型会报错的问题以及源码级别的原因,

然后是统一异常返回问题


http://www.ppmy.cn/news/1577148.html

相关文章

java环境部署

java环境部署 一、准备工作 jrejdkeclipse jdk下载&#xff1a;21和1.8-----官网&#xff1a;Oracle&#xff1a;Java 下载 |神谕 该处选择要依据自身的系统类型选择下载 idea的下载安装&#xff1a;IntelliJ IDEA | Other Versions 二、安装 三、环境配置 四、使用 五、i…

DifyでOracle Base Database Service(23ai)を利用する設定手順

[TOC](DifyでOracle Base Database Service(23ai)を利用する設定手順) はじめに 本記事では、DifyプラットフォームとOracle Base Database Service&#xff08;23aiエディション&#xff09;を連携させる方法を解説します。クラウド環境における大規模データ処理を想定した設…

⭐算法OJ⭐跳跃游戏【贪心算法】(C++实现)Jump Game 系列 I,II

既股票买卖系列之后的第二组贪心算法题目&#xff1a;跳跃游戏系列。这一篇介绍的两个问题&#xff0c;其输入均为一个数组&#xff0c;每个元素表示在该位置可以跳跃的最大长度。 55. Jump Game You are given an integer array nums. You are initially positioned at the …

从零开始构建高效Spring Boot应用:实战案例与最佳实践

摘要 本文旨在为初学者及有一定基础的开发者提供一份详尽的指南&#xff0c;以帮助大家深入理解并掌握如何使用Spring Boot框架来快速开发企业级应用程序。通过实际案例分析、代码示例以及架构设计思路分享&#xff0c;读者不仅能够学习到理论知识&#xff0c;还能获得宝贵的实…

网络原理---HTTP/HTTPS

通过之前的网络编程&#xff0c;我们已经初步了解UDP和TCP的基本实现方法&#xff0c;接下来我们对其进一步的学习。 在网络编程中&#xff1a; 1.读和写数据通过Socket&#xff0c;通过Socket内置的InputStream和OutputStream(读写的基本单位都是字节&#xff09;。2.当在编…

游戏引擎学习第135天

仓库:https://gitee.com/mrxiao_com/2d_game_3 回顾 game_asset.cpp 的创建 在开发过程中&#xff0c;不使用任何现成的游戏引擎或第三方库&#xff0c;而是直接基于 Windows 进行开发&#xff0c;因为 Windows 目前仍然是游戏的标准平台&#xff0c;因此首先在这个环境中进行…

游戏引擎学习第137天

演示资产系统中的一个 bug 我们留下了个问题&#xff0c;你现在可以看到&#xff0c;移动时它没有选择正确的资产。我们知道问题的原因&#xff0c;就在之前我就预见到这个问题会出现。问题是我们的标签系统没有处理周期性边界的匹配问题。当处理像角度这种周期性的标签时&…

【基于手势识别的音量控制系统】

基于手势识别的音量控制系统 github 项目效果 这是一个结合了计算机视觉和系统控制的实用项目&#xff0c;通过识别手势来实现音量的无接触控制&#xff0c;同时考虑到了用户隐私&#xff0c;加入了实时人脸遮罩功能。 核心功能实现 1. 手势识别与音量映射 系统使用 Media…