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

    @Override
    public Integer getCode() {
        return this.code;
    }

    @Override
    public 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> {
    @Override
    public boolean supports(@NotNull MethodParameter returnType,
                            @NotNull Class<? extends HttpMessageConverter<?>> converterType) {
        GlobalResponse globalResponse = returnType.getMethodAnnotation(GlobalResponse.class);
        return globalResponse == null || globalResponse.format();
    }

    @Override
    public 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 请求,业务状态码统一设置为 201
        if ("POST".equals(((ServletServerHttpRequest) request).getServletRequest().getMethod())) {
            responseStructure = ResponseStructure.created("OK");
        } else {
            responseStructure = ResponseStructure.success(null);
        }

        // 如果返回值是字符串类型,则用其替换 message
        if (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中实现统一返回及全部异常捕获,如您有更好观点欢迎在评论区留言探讨~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/542737.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构初阶:二叉树(一)

树概念及结构 树的概念 树是一种 非线性 的数据结构&#xff0c;它是由 n &#xff08; n>0 &#xff09;个有限结点组成一个具有层次关系的集合。 把它叫做树是因 为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的 。 有一个特殊的结点&a…

1688商品详情接口技术深探:解锁电商数据新纪元,实现业务自动化飞跃

1688商品详情接口技术解析 一、引言 随着电子商务的快速发展&#xff0c;越来越多的企业开始关注如何利用API接口获取商品详情信息&#xff0c;以实现数据的自动化处理和业务的快速拓展。1688作为国内知名的B2B电商平台&#xff0c;其商品详情接口成为了众多企业关注的焦点。…

HarmonyOS鸿蒙端云一体化开发--适合小白体制

端云一体化 什么是“端”&#xff0c;什么是“云”&#xff1f; 答&#xff1a;“端“&#xff1a;手机APP端 “云”:后端服务端 什么是端云一体化&#xff1f; 端云一体化开发支持开发者在 DevEco Studio 内使用一种语言同时完成 HarmonyOS 应用的端侧与云侧开发。 …

AI预测体彩排3第3弹【2024年4月14日预测--第1套算法开始计算第3次测试】

今天咱们继续测试第1套算法和模型&#xff0c;今天是第3次测试&#xff0c;目前的测试只是为了记录和验证&#xff0c;不建议大家盲目跟买。我的目标仍旧是10次命中3-4次!~废话不多说了&#xff0c;直接上结果&#xff01; 2024年4月14日排3的七码预测结果如下 第一套&…

LLM 推理优化探微 (4) :模型性能瓶颈分类及优化策略

编者按&#xff1a; 在人工智能浪潮袭卷全球的大背景下&#xff0c;进一步提升人工智能模型性能&#xff0c;满足更多应用需求已经刻不容缓。如何优化模型延迟和吞吐量&#xff0c;成为了业界亟待解决的重要问题。 我们今天为大家带来的这篇文章&#xff0c;其观点为&#xff1…

C语言中的文件操作

C语言中的文件操作 1、文件的打开 创建文件指针变量 File* pf;定义一个指向FILE类型数据的指针变量&#xff0c;可以使pf指向某个文件的文件信息区&#xff0c;通过文件指针变量就能够找到与它关联的文件 &#xff08;1&#xff09;文件的打开 使用fopen函数打开文件&#…

基于Springboot的餐厅点餐系统

基于SpringbootVue的餐厅点餐系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 首页展示 菜品详情页 菜品信息 个人中心 后台管理 菜品信息管理 用户管理 菜…

less+rem+媒体查询布局(主流)

rem适配布局 一.rem基础二.媒体查询1.概念2.语法&#xff08;1&#xff09;.mediatype查询类型&#xff08;2&#xff09;.关键字&#xff08;3&#xff09;.媒体特性&#xff08;4&#xff09;.应用 3.媒体查询rem实现元素动态大小变化4.引入资源&#xff08;针对不同媒体查询…

【系统分析师】需求工程☆

文章目录 0、需求工程概述1、需求的分类2、需求获取3、需求分析3.1 结构化需求分析-SA3.1.1DFD- 数据流图3.1.2 STD-状态转换图3.1.3 ER图-实体联系图 3.2 面向对象需求分析-OOA3.2.1 工具-UML图3.2.2 UML分类3.2.3 用例图 ☆3.2.4 类图 / 对象图 ☆3.2.5 顺序图3.2.6 活动图3.…

斐尔玫瑰荣获《中国3.15诚信企业》证书,诚信经营赢得社会认可

2024年&#xff0c;斐尔玫瑰&#xff0c;荣获了备受瞩目的《中国3.15诚信企业》证书。这一荣誉的获得&#xff0c;不仅是对斐尔玫瑰长期以来坚持诚信经营、提供优质产品和服务的肯定&#xff0c;更是对其在消费者心目中建立起的良好信誉和口碑的认可。 斐尔玫瑰作为女性私密护…

自动化测试之httprunner框架hook函数实操

本篇介绍httprunner中hook函数的使用&#xff0c;以及通过编程能力实现建设自动化测试更全面的场景覆盖 前置&#xff1a; 互联网时代让我们更快的学习到什么是Httprunner 正文&#xff1a; 经过上文了解到这个框架怎么使用之后&#xff0c;我们开始来探讨一下我们为什么要用…

MySQL分区表(14/16)

分区表 基本概述 分区表是数据库中一种用于优化大型表数据管理和查询性能的技术。它将一个表的数据根据特定的规则或条件分割成多个部分&#xff0c;每个部分称为一个分区。每个分区可以独立于其他分区进行存储、管理和查询&#xff0c;这样可以提高数据处理的效率&#xff0…

mybatis(9)-逆向工程+PageHelper+注解方式开发

最后一篇&#xff01;&#xff01; 1、逆向工程1.1、普通版1.2、增强版 2、PageHelper2.1 limit2.2 插件 3、注解开发3.1 Insert3.2Delete3.3 Update3.4 Select Results 1、逆向工程 1.1、普通版 所谓的逆向工程是&#xff1a;根据数据库表逆向生成Java的pojo类&#xff0c;S…

智过网:注册安全工程师注册有效期与周期解析

在职业领域&#xff0c;各种专业资格认证不仅是对从业者专业能力的认可&#xff0c;也是保障行业安全、规范发展的重要手段。其中&#xff0c;注册安全工程师证书在安全生产领域具有举足轻重的地位。那么&#xff0c;注册安全工程师的注册有效期是多久呢&#xff1f;又是几年一…

伺服系统中滤波器算法的工程实现方案

此文章主要致力于描述如何将伺服驱动系统中的数字滤波器用编程语言来实现。

【动态规划 区间dp 位运算】100259. 划分数组得到最小的值之和

本文涉及知识点 动态规划 区间dp 位运算 LeetCode100259. 划分数组得到最小的值之和 给你两个数组 nums 和 andValues&#xff0c;长度分别为 n 和 m。 数组的 值 等于该数组的 最后一个 元素。 你需要将 nums 划分为 m 个 不相交的连续 子数组&#xff0c;对于第 ith 个子数…

银行渠道整合平台应用架构

渠道整合平台将 功能微服务化&#xff0c;将服务流程标准化。微服务 化的功能能够进行各种组合使用。而标准化的流程可同时作用于所有渠道&#xff0c;保证体验一致。未来在进行流程变更的时候可有效避免各渠道的重复开发。 • 渠道整合平台避免了各个渠道对于同一个业务的差异…

C# dynamic 数据类型

在C#中&#xff0c;dynamic是一种数据类型&#xff0c;它允许在运行时推迟类型检查和绑定。使用dynamic类型&#xff0c;可以编写更具灵活性的代码&#xff0c;因为它允许在编译时不指定变量的类型&#xff0c;而是在运行时根据实际情况进行解析。 dynamic类型的变量可以存储任…

你真的会处理python代码异常吗?

Python 使用称为异常(exception&#xff09;的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时&#xff0c;它都会创建一个异常对象。如果你编写了处理该异常的代码&#xff0c;程序将继续运行&#xff1b;如果你未对异常进行处理&#xff0c;程序将停…

什么是面向对象思想?

面向对象不是一种技术&#xff0c;而是一种思想。它指导我们以什么形式组织代码&#xff0c;以什么思路解决问题。 面向对象编程&#xff0c;是一种通过对象方式&#xff0c;把现实世界映射到计算机世界的编程方法。 面向对象解决问题的思路&#xff1a;把构成问题的事物分解成…