【Java系列】SpringCloudAlibaba统一返回体及全局异常捕获实现

本文主要是介绍【Java系列】SpringCloudAlibaba统一返回体及全局异常捕获实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文将以实际代码展示如何实现SpringCloudAlibaba的统一返回体及全局异常捕获。

作者:后端小肥肠

1. 前言

在构建微服务应用时,统一返回体和异常捕获机制的设计对于保持代码的整洁性和提高服务的可维护性至关重要。特别是在使用 Spring Boot 和 Spring Cloud Alibaba这样的现代开发框架时,这一点显得尤为重要。本文将重点介绍如何在Spring Cloud Alibaba环境中实现统一的响应体和异常处理策略。通过这种方式,无论是在单体应用还是在复杂的微服务架构中,开发者都能保证返回信息的一致性和异常的有效管理。

2. 开发环境搭建

2.1. 所需版本依赖

依赖版本
Spring Boot2.6.3
Spring Cloud

2021.0.1

java1.8以上
Spring Cloud Alibaba2021.0.1.0

2.2. pom依赖  

    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.3</version><relativePath/></parent><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><spring-cloud.version>2021.0.1</spring-cloud.version><spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version></properties><dependencies><!-- springCloud --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.jetbrains</groupId><artifactId>annotations</artifactId><version>17.0.0</version></dependency></dependencies>

3. Spring Cloud统一返回体和异常捕获实现

3.1. Spring Cloud统一返回体实现

微服务项目一般由网关加下游微服务组成,我的习惯是分成网关模块,api模块(存放远程调用方法及公共实体类),common模块(存放一些公共Bean及工具类)及各类业务模块,通常返回体是放在common类中:

SpringCloud项目结构示例

在common中新建response包,将统一返回相关类放入其中即可。

3.1.1. 编写响应状态码枚举
public enum ResponseStatusCodeEnum implements IResponseStatusCode {//服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。SUCCESS(200, "OK"),//用户新建或修改数据成功。UPDATE_RETURN_201(201, "CREATED"),//表示一个请求已经进入后台排队(异步任务)ALL_RETURN_202(202, "Accepted"),//用户删除数据成功。DELETE_RETURN_204(204, "NO CONTENT"),//用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。UPDATE_RETURN_400(400, "INVALID REQUEST"),//用户发出的请求参数有误,服务器没有找到对应资源 这是新加的WRONG_PARAMETER_NOT_FIND_400(400,"请求参数有误,资源不存在"),//401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。ALL_RETURN_401(401, "Unauthorized"),TOKEN_PAST(1401, "身份过期,请求重新登录!"),//403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。ALL_RETURN_403(403, "Forbidden"),//404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。ALL_RETURN_404(404, "NOT FOUND"),//406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。GET_RETURN_406(406, "Not Acceptable"),//410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。GET_RETURN_410(410, "Gone"),//422 Unprocessable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。UPDATE_RETURN_422(422, "Unprocessable entity"),//500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。*/GET_RETURN_500(500, "INTERNAL SERVER ERROR"),CONFLICT_RETURN_409(409,"CONFLICT");private final Integer code;private final String message;ResponseStatusCodeEnum(Integer code, String message) {this.code = code;this.message = message;}@Overridepublic Integer getCode() {return this.code;}@Overridepublic String getMessage() {return this.message;}
}​
3.1.2. 编写响应状态码接口
public interface IResponseStatusCode {/*** 获取响应状态码** @return 响应状态码*/Integer getCode();/*** 获取响应消息** @return 响应消息*/String getMessage();
}
3.1.3. 编写统一返回结构体
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResponseStructure<T>{private Integer code;private String status;private String message;private T data;private static final String SUCCESS = "success";private static final String FAIL = "fail";public static <T> ResponseStructure<T> success(T data) {return new ResponseStructure<>(ResponseStatusCodeEnum.SUCCESS.getCode(),SUCCESS,ResponseStatusCodeEnum.SUCCESS.getMessage(),data);}public static <T> ResponseStructure<T> created(String message) {return new ResponseStructure<>(ResponseStatusCodeEnum.UPDATE_RETURN_201.getCode(),SUCCESS,message,null);}public static <T> ResponseStructure<T> unauthorized(String message) {return new ResponseStructure<>(ResponseStatusCodeEnum.ALL_RETURN_403.getCode(),FAIL,message,null);}public static <T> ResponseStructure<T> unauthenticated(String message) {return new ResponseStructure<>(ResponseStatusCodeEnum.ALL_RETURN_401.getCode(),FAIL,message,null);}public static <T> ResponseStructure<T> success(String message, T data) {return new ResponseStructure<>(ResponseStatusCodeEnum.SUCCESS.getCode(),SUCCESS,message,data);}public static <T> ResponseStructure<T> success(Integer code, String message) {return new ResponseStructure<>(code, SUCCESS, message, null);}public static <T> ResponseStructure<T> success(Integer code, String message, T data) {return new ResponseStructure<>(code, SUCCESS, message, data);}public static ResponseStructure<Object> failed() {return new ResponseStructure<>(ResponseStatusCodeEnum.GET_RETURN_500.getCode(),FAIL,ResponseStatusCodeEnum.GET_RETURN_500.getMessage(),null);}public static ResponseStructure<String> failed(String message) {return new ResponseStructure<>(ResponseStatusCodeEnum.GET_RETURN_500.getCode(),FAIL,message,null);}public static ResponseStructure<Object> failed(IResponseStatusCode errorResult) {return new ResponseStructure<>(errorResult.getCode(),FAIL,errorResult.getMessage(),null);}public static ResponseStructure<Object> conflict(String message) {return new ResponseStructure<>(ResponseStatusCodeEnum.CONFLICT_RETURN_409.getCode(),FAIL,message,null);}public static <T> ResponseStructure<T> instance(Integer code, String message, T data) {ResponseStructure<T> responseStructure = new ResponseStructure<>();responseStructure.setCode(code);responseStructure.setMessage(message);responseStructure.setData(data);if (code >= 300) {responseStructure.setStatus(FAIL);} else {responseStructure.setStatus(SUCCESS);}return responseStructure;}public static <T> ResponseStructure<T> instance(Integer code, String message) {ResponseStructure<T> responseStructure = new ResponseStructure<>();responseStructure.setCode(code);responseStructure.setMessage(message);responseStructure.setData(null);if (code >= 300) {responseStructure.setStatus(FAIL);} else {responseStructure.setStatus(SUCCESS);}return responseStructure;}public static <T> ResponseStructure<T> instance(IResponseStatusCode code) {ResponseStructure<T> responseStructure = new ResponseStructure<>();responseStructure.setCode(code.getCode());responseStructure.setMessage(code.getMessage());responseStructure.setData(null);if (code.getCode() >= 300) {responseStructure.setStatus(FAIL);} else {responseStructure.setStatus(SUCCESS);}return responseStructure;}
}
3.1.4. 编写GlobalResponseBodyAdvice
@RestControllerAdvice
public class GlobalResponseBodyAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(@NotNull MethodParameter returnType,@NotNull Class<? extends HttpMessageConverter<?>> converterType) {GlobalResponse globalResponse = returnType.getMethodAnnotation(GlobalResponse.class);return globalResponse == null || globalResponse.format();}@Overridepublic Object beforeBodyWrite(Object body,@NotNull MethodParameter returnType,@NotNull MediaType selectedContentType,@NotNull Class<? extends HttpMessageConverter<?>> selectedConverterType,@NotNull ServerHttpRequest request,@NotNull ServerHttpResponse response) {// 如果是 actuator 请求,直接返回if (isActuatorRequest(request)) {return body;}/* 如果是 Feign 请求,直接返回*/if (Objects.requireNonNull(request.getHeaders().get("user-agent")).get(0).startsWith("Java")) {return body;}// 以下代码主要解决和 Swagger 的冲突if (body instanceof ResponseStructure || body instanceof Json || body instanceof UiConfiguration ||(body instanceof ArrayList && !((ArrayList<?>) body).isEmpty() &&((ArrayList<?>) body).get(0) instanceof SwaggerResource)) {return body;}ResponseStructure<Object> responseStructure;// 如果是 POST 请求,业务状态码统一设置为 201if ("POST".equals(((ServletServerHttpRequest) request).getServletRequest().getMethod())) {responseStructure = ResponseStructure.created("OK");} else {responseStructure = ResponseStructure.success(null);}// 如果返回值是字符串类型,则用其替换 messageif (body instanceof String) {responseStructure.setData(body);return JSON.toJSONString(responseStructure);}if (body instanceof byte[]) {return body;}responseStructure.setData(body);return responseStructure;}private boolean isActuatorRequest(ServerHttpRequest request) {return ((ServletServerHttpRequest) request).getServletRequest().getRequestURI().endsWith("/actuator");}
}

这个类的主要作用是为应用提供一个统一的响应结构,帮助前端开发者和最终用户更好地理解和处理 API 的响应。通过拦截所有非特殊请求的响应体,并将它们包装成统一的格式,这个实现可以极大地增强 API 的一致性和可维护性。同时,它也处理了一些特殊情况,如避免改变对于特定工具或请求的原始响应(Swagger UI 或 Feign 客户端请求)。

3.1.5. 编写GlobalResponse
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GlobalResponse {boolean format() default true;
}
3.1.6. 效果测试 

1. controller层方法编写,只需要返回实际数据结构即可

2. 返回结构测试

3.2 Spring Cloud异常捕获实现

在common中新建expection包,将异常捕获类类放入其中即可。

3.2.1. 编写ExceptionAdvice
@Slf4j
@RestControllerAdvice
public class ExceptionAdvice {@ExceptionHandler(BindException.class)public Object bindException(BindException bindException) {bindException.printStackTrace();String message = Objects.requireNonNull(bindException.getBindingResult().getFieldError()).getDefaultMessage();return ResponseStructure.conflict(message);}@ResponseStatus(value = HttpStatus.CONFLICT)@ExceptionHandler({ValidationException.class})public ResponseStructure<Object> handleValidationException(ValidationException validationException) {validationException.printStackTrace();return (ResponseStructure<Object>) ResponseStructure.conflict(validationException.getMessage());}@ResponseStatus(value = HttpStatus.CONFLICT)@ExceptionHandler({MaxUploadSizeExceededException.class})public ResponseStructure<Object> handleMaxUploadSizeException(MaxUploadSizeExceededException maxUploadSizeExceededException) {maxUploadSizeExceededException.printStackTrace();return (ResponseStructure<Object>) ResponseStructure.conflict("当前文件大小已超过限制大小,请重新上传文件");}/*** 顶级异常捕获,当其他异常无法处理时选择使用*/@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)@ExceptionHandler({Exception.class})public ResponseStructure<String> handle(Exception exception) {exception.printStackTrace();return (ResponseStructure<String>) ResponseStructure.failed(exception.getMessage());}/*** 认证异常捕获*/@ResponseStatus(value = HttpStatus.UNAUTHORIZED)@ExceptionHandler({AuthenticationException.class})public ResponseStructure<String> handleUnAhthorized(AuthenticationException exception) {exception.printStackTrace();return ResponseStructure.instance(ALL_RETURN_401.getCode(), exception.getMessage());}
}

ExceptionAdvice 类通过定义一系列异常处理器,使得应用能够在抛出异常时提供友好的用户反馈,而不是让用户面对不友好的原始错误信息或空白页面。这样的处理机制不仅提高了应用的可用性和可维护性,还能通过日志记录帮助开发者快速定位和解决问题。此外,通过统一异常处理和响应格式,开发者可以更容易地保持前后端的一致性和同步。 

3.2.2. 效果测试

1. 编写异常测试controller层方法

2. 返回结构测试

5. 结语

本文以代码实例展示了如何在SpringCloudAlibaba中实现统一返回及全部异常捕获,如您有更好观点欢迎在评论区留言探讨~

这篇关于【Java系列】SpringCloudAlibaba统一返回体及全局异常捕获实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/903107

相关文章

springboot security快速使用示例详解

《springbootsecurity快速使用示例详解》:本文主要介绍springbootsecurity快速使用示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录创www.chinasem.cn建spring boot项目生成脚手架配置依赖接口示例代码项目结构启用s

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

springboot security之前后端分离配置方式

《springbootsecurity之前后端分离配置方式》:本文主要介绍springbootsecurity之前后端分离配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的... 目录前言自定义配置认证失败自定义处理登录相关接口匿名访问前置文章总结前言spring boot secu

Python+PyQt5实现多屏幕协同播放功能

《Python+PyQt5实现多屏幕协同播放功能》在现代会议展示、数字广告、展览展示等场景中,多屏幕协同播放已成为刚需,下面我们就来看看如何利用Python和PyQt5开发一套功能强大的跨屏播控系统吧... 目录一、项目概述:突破传统播放限制二、核心技术解析2.1 多屏管理机制2.2 播放引擎设计2.3 专

一文详解SpringBoot响应压缩功能的配置与优化

《一文详解SpringBoot响应压缩功能的配置与优化》SpringBoot的响应压缩功能基于智能协商机制,需同时满足很多条件,本文主要为大家详细介绍了SpringBoot响应压缩功能的配置与优化,需... 目录一、核心工作机制1.1 自动协商触发条件1.2 压缩处理流程二、配置方案详解2.1 基础YAML

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

java中使用POI生成Excel并导出过程

《java中使用POI生成Excel并导出过程》:本文主要介绍java中使用POI生成Excel并导出过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录需求说明及实现方式需求完成通用代码版本1版本2结果展示type参数为atype参数为b总结注:本文章中代码均为

springboot简单集成Security配置的教程

《springboot简单集成Security配置的教程》:本文主要介绍springboot简单集成Security配置的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录集成Security安全框架引入依赖编写配置类WebSecurityConfig(自定义资源权限规则

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI