【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健康检查监控全过程

《springboot健康检查监控全过程》文章介绍了SpringBoot如何使用Actuator和Micrometer进行健康检查和监控,通过配置和自定义健康指示器,开发者可以实时监控应用组件的状态,... 目录1. 引言重要性2. 配置Spring Boot ActuatorSpring Boot Act

使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)

《使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)》在现代软件开发中,处理JSON数据是一项非常常见的任务,无论是从API接口获取数据,还是将数据存储为JSON格式,解析... 目录1. 背景介绍1.1 jsON简介1.2 实际案例2. 准备工作2.1 环境搭建2.1.1 添加

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

java如何分布式锁实现和选型

《java如何分布式锁实现和选型》文章介绍了分布式锁的重要性以及在分布式系统中常见的问题和需求,它详细阐述了如何使用分布式锁来确保数据的一致性和系统的高可用性,文章还提供了基于数据库、Redis和Zo... 目录引言:分布式锁的重要性与分布式系统中的常见问题和需求分布式锁的重要性分布式系统中常见的问题和需求

SpringBoot基于MyBatis-Plus实现Lambda Query查询的示例代码

《SpringBoot基于MyBatis-Plus实现LambdaQuery查询的示例代码》MyBatis-Plus是MyBatis的增强工具,简化了数据库操作,并提高了开发效率,它提供了多种查询方... 目录引言基础环境配置依赖配置(Maven)application.yml 配置表结构设计demo_st

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

python使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧