[Spring Cloud] (2)gateway全局异常捕捉统一返回值

文章目录

    • 处理转发失败的情况
    • 全局参数
    • 同一返回格式
      • 操作消息对象AjaxResult
      • 返回值状态描述对象AjaxStatus
      • 返回值枚举接口层StatusCode
    • 全局异常处理器
    • 自定义通用异常
      • 定一个自定义异常
      • 覆盖默认的异常处理
      • 自定义异常处理工具

在上一篇章时我们有了一个简单的gateway网关
[Spring Cloud] gateway简单搭建与请求转发-CSDN博客
现在我们需要根据这个网关进行一部分的改进

现在我们需要进一步的处理一些问题,来使得网关更加完善。
本篇文章的完整代码文件已放置在gitee。
杉极简/gateway网关阶段学习

处理转发失败的情况

正常情况下,我们请求了一个接口,并得到了一个结果,如下:
image.png
但是,我们依然要考虑,如果访问到一个不存在的接口,会得到什么样的结果?
如果不做任何修改的时候,我们会得到以下的结果:
image.png
但这不是我们想要的。
我们想要如下返回结果。
image.png
因此我们需要配置一个全局异常处理器来处理。
为达到这个目的,我们开始构建一些基础的功能,描述如下。

全局参数

我们需要去创建一个全部配置,如下所示:
这里统一存放着我们需要读取的配置,主要为全局使用的参数。具体配置方式如下所示:
在网关项目中创建一个config文件夹,用于存放网关的相关配置。(当然,能想做网关的人,应该都熟练的会使用Spring Boot了,在Spring Boot项目中,这都是一些较为基础的内容)
此时,我们先配置一个全局异常捕捉-打印堆栈异常的参数,用于本文的一些功能当中。
image.png

global:
  # 全局异常捕捉-打印堆栈异常
  printStackTrace: true
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


/**
 * @author fir
 * @date 2023/7/28 17:53
 */
@Data
@Component
@ConfigurationProperties(prefix = "global")
public class GlobalConfig {

    /**
     * 全局异常捕捉-打印堆栈异常
     */
    private boolean printStackTrace;
}

image.png

同一返回格式

创建一个result文件夹,用于存放统一返回值的相关配置
该部分比较基础,暂时只说明配置方式与代码。
image.png

操作消息对象AjaxResult

import java.io.Serializable;
import java.util.HashMap;


/**
 * 操作消息-JSON
 *
 * @author fir
 */
public class AjaxResult extends HashMap<String, Object> implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 状态码
     */
    public static final String CODE_TAG = "code";

    /**
     * 返回内容
     */
    public static final String MSG_TAG = "msg";

    /**
     * 数据对象
     */
    public static final String DATA_TAG = "data";


    /**
     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
     */
    public AjaxResult() {
    }


    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param code 状态码
     * @param msg  状态描述
     */
    public AjaxResult(int code, String msg) {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
    }


    /**
     * 初始化一个新创建的 AjaxResult 对象
     *
     * @param code 状态码
     * @param msg  状态描述
     * @param data 数据对象
     */
    public AjaxResult(int code, String msg, Object data) {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
        if (data != null) {
            super.put(DATA_TAG, data);
        }
    }


    /**
     * 返回成功消息
     *
     * @return 成功消息
     */
    public static AjaxResult success() {
        AjaxStatus success = AjaxStatus.SUCCESS;
        return AjaxResult.success(success.getCode(), success.getMsg());
    }


    /**
     * 返回成功数据
     *
     * @return 成功消息
     */
    public static AjaxResult success(Object data) {
        AjaxStatus success = AjaxStatus.SUCCESS;
        return AjaxResult.success(success.getMsg(), data);
    }


    /**
     * 返回成功数据
     *
     * @return 成功消息
     */
    public static AjaxResult success(AjaxStatus success) {

        return AjaxResult.success(success.getMsg(), new HashMap<>(0));
    }


    /**
     * 返回成功消息
     *
     * @param code 状态吗
     * @param msg  状态描述
     * @return 消息体
     */
    public static AjaxResult success(Integer code, String msg) {
        return new AjaxResult(code, msg);
    }


    /**
     * 返回成功消息
     *
     * @param msg  状态描述
     * @param data 数据对象
     * @return 成功消息
     */
    public static AjaxResult success(String msg, Object data) {
        AjaxStatus success = AjaxStatus.SUCCESS;
        return new AjaxResult(success.getCode(), msg, data);
    }


    /**
     * 返回特定状态描述
     *
     * @param statusCode 特定的枚举结果
     * @param data       数据对象
     * @return 请求结果
     */
    public static AjaxResult success(StatusCode statusCode, Object data) {
        return new AjaxResult(statusCode.getCode(), statusCode.getMsg(), data);
    }


    /**
     * 返回错误消息
     *
     * @return 警告消息
     */
    public static AjaxResult error() {
        return AjaxResult.error(AjaxStatus.LOSE_OPERATION.getMsg());
    }


    /**
     * 返回错误消息
     *
     * @param msg 状态描述
     * @return 警告消息
     */
    public static AjaxResult error(String msg) {
        return AjaxResult.error(msg, new HashMap<>(0));
    }


    /**
     * 返回错误消息
     *
     * @param msg  状态描述
     * @param data 数据对象
     * @return 警告消息
     */
    public static AjaxResult error(String msg, Object data) {
        AjaxStatus loseEfficacy = AjaxStatus.LOSE_EFFICACY;
        return new AjaxResult(loseEfficacy.getCode(), msg, data);
    }


    /**
     * 返回错误消息
     *
     * @param code 状态码
     * @param msg  状态描述
     * @return 警告消息
     */
    public static AjaxResult error(int code, String msg) {
        return new AjaxResult(code, msg, new HashMap<>(0));
    }


    /**
     * 返回特定状态描述
     *
     * @param statusCode 特定的枚举结果
     * @param data       数据对象
     * @return 请求结果
     */
    public static AjaxResult error(StatusCode statusCode, Object data) {
        return new AjaxResult(statusCode.getCode(), statusCode.getMsg(), data);
    }


    /**
     * 返回特定状态描述
     *
     * @param statusCode 特定的枚举结果
     * @return 请求结果
     */
    public static AjaxResult error(StatusCode statusCode) {
        return new AjaxResult(statusCode.getCode(), statusCode.getMsg(), new HashMap<>(0));
    }
}

返回值状态描述对象AjaxStatus

import lombok.Getter;


/**
 * 返回值状态与描述
 * 
 * @author fir
 */

@Getter
public enum AjaxStatus implements StatusCode {
    /**
     * 请求成功
     */
    SUCCESS(200, "请求成功"),
    /**
     * 登录成功
     */
    SUCCESS_LOGIN(200, "登录成功"),
    /**
     * 登出成功
     */
    SUCCESS_LOGOUT(200, "登出成功"),
    /**
     * 启动成功
     */
    SUCCESS_FLOW_START(200, "启动成功"),
    /**
     * 暂无数据
     */
    NO_DATA(200, "暂无数据"),
    /**
     * 错误请求
     */
    BAD_REQUEST(400, "错误请求"),
    /**
     * 登录过期
     */
    EXPIRATION_TOKEN(401, "登录过期"),
    /**
     * 服务不存在
     */
    FAILED_SERVICE_DOES(404, "服务不存在"),
    /**
     * 账号或密码为空
     */
    NULL_LOGIN_DATA(480, "账号或密码为空"),
    /**
     * 建立通信-通信建立失败
     */
    FAILED_COMMUNICATION(481, "通信建立失败"),
    /**
     * 接口不存在
     */
    NULL_API(404, "接口不存在"),
    /**
     * 建立通信-非法请求
     */
    ILLEGAL_REQUEST(482, "非法请求"),
    /**
     * 请求失败
     */
    LOSE_EFFICACY(490, "请求失败"),
    /**
     * 操作失败
     */
    LOSE_OPERATION(491, "操作失败"),
    /**
     * 请求失败
     */
    FAILED(500, "请求失败"),
    /**
     * 服务不可用(gateway网关总定义-所有未定义处理的异常都返回该异常)
     */
    SERVICE_UNAVAILABLE(500, "服务不可用"),
    /**
     * 请求整体加密-无效会话
     */
    SESSION_INVALID(601, "无效会话"),
    /**
     * 请求整体加密-会话过期
     */
    SESSION_EXPIRE(602, "会话过期"),
    /**
     * 防重放校验失败
     */
    ANTI_REPLAY_VERIFY_FAILED(701, "防重放校验失败"),
    /**
     * 防重放校验失败
     */
    INTEGRITY_VERIFY_FAILED(801, "完整性校验失败"),
    /**
     * 预留
     */
    PASS(1000, "请求失败");

    private final int code;
    private final String msg;

    AjaxStatus(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

返回值枚举接口层StatusCode

package com.fir.gateway.config.result;

/**
 * 返回值枚举接口层
 * @author 18714
 */
public interface StatusCode {


    /**
     * 获取code信息
     *
     * @return code码
     */
     int getCode();


    /**
     * 获取msg信息
     *
     * @return msg描述
     */
     String getMsg();
}

全局异常处理器

做两步

  1. 覆盖默认的异常处理。
  2. 自定义异常处理。

自定义通用异常

通常的处理过程为抛出异常->全局异常捕捉->返回前端
通常在代码中,对于某个特定的条件,我们抛出一个自定义异常,并携带特定的状态码与状态描述

if (session == null) {
    throw new CustomException(AjaxStatus.SESSION_INVALID);
}

而此时,我们在开发环境中通常需要显示堆栈异常,但是生成环境中,大多数是不需要的,此时我们就是用了全局参数配置在自定义异常工具这个类中,我们配置了定义异常默认不打印堆栈异常

定一个自定义异常

import com.fir.gateway.config.result.AjaxStatus;
import lombok.Getter;

/**
 * 自定义通用异常
 * 抛出异常->全局异常捕捉->返回前端
 *
 * @author fir
 */
@Getter
public class CustomException extends RuntimeException {

    /**
     * code状态码
     */
    private final int code;

    /**
     * 错误状态码
     */
    private final AjaxStatus ajaxStatus;


    public CustomException(AjaxStatus ajaxStatus) {
        super(ajaxStatus.getMsg());
        this.code = ajaxStatus.getCode();
        this.ajaxStatus = ajaxStatus;
    }

}

覆盖默认的异常处理

此时我们覆盖默认的异常处理,并使用自定义的异常处理工具JsonExceptionHandler

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.result.view.ViewResolver;

import java.util.Collections;
import java.util.List;


/**
 * 覆盖默认的异常处理
 * @author fir
 */
@Configuration
@EnableConfigurationProperties({ServerProperties.class, WebProperties.class})
public class ErrorHandlerConfiguration {

    private final ServerProperties serverProperties;
    private final ApplicationContext applicationContext;
    private final WebProperties webProperties;
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public ErrorHandlerConfiguration(ServerProperties serverProperties,
                                     WebProperties webProperties,
                                     ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                     ServerCodecConfigurer serverCodecConfigurer,
                                     ApplicationContext applicationContext) {
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.webProperties = webProperties;
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
        JsonExceptionHandler exceptionHandler = new JsonExceptionHandler(
                errorAttributes,
                this.webProperties,
                this.serverProperties.getError(),
                this.applicationContext);
        exceptionHandler.setViewResolvers(this.viewResolvers);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }

}

自定义异常处理工具

解决三个问题

  1. 处理自定义异常
  2. 处理不存在的接口
  3. 对于其他的异常,统一返回一个指定的错误描述AjaxStatus.SERVICE_UNAVAILABLE
package com.fir.gateway.config.exception;


import com.fir.gateway.config.GlobalConfig;
import com.fir.gateway.config.result.AjaxResult;
import com.fir.gateway.config.result.AjaxStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;


/**
 * 自定义异常处理工具
 * 异常时用JSON代替HTML异常信息
 *
 * @author fir
 */
@Slf4j
public class JsonExceptionHandler extends DefaultErrorWebExceptionHandler {


    /**
     * 网关参数配置
     */
    @Resource
    private GlobalConfig globalConfig;


    public JsonExceptionHandler(ErrorAttributes errorAttributes, WebProperties webProperties, ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, webProperties.getResources(), errorProperties, applicationContext);
        log.info(String.valueOf(errorProperties));
        log.info(String.valueOf(errorAttributes));
    }

    /**
     * 重构方法,设置返回属性格式
     */
    @Override
    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        Throwable errorThrowable = getError(request);
        // 自定义异常默认不打印堆栈异常
        // 决定是否打印堆栈异常
        boolean printStackTrace = globalConfig.isPrintStackTrace();
        if (printStackTrace) {
            errorThrowable.printStackTrace();
        }
        // 打印全局异常
        log.error(errorThrowable.getMessage());

        Class<?> errorClass = errorThrowable.getClass();
        String simpleName = errorClass.getSimpleName();
        AjaxStatus ajaxStatus;
        switch (simpleName) {
            case "CustomException":
                // 处理自定义异常
                CustomException customException = (CustomException) errorThrowable;
                ajaxStatus = customException.getAjaxStatus();
                break;
            case "NotFoundException":
            case "ResponseStatusException":
                // 处理404
                ajaxStatus = AjaxStatus.NULL_API;
                break;
            default:
                // 统一返回一个服务错误描述
                ajaxStatus = AjaxStatus.SERVICE_UNAVAILABLE;
                break;
        }

        AjaxResult result = AjaxResult.error(ajaxStatus);
        return ServerResponse.status(200).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(result));
    }
}

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

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

相关文章

使用LangChain和GPT-4,创建Pandas DataFrame智能体

大家好&#xff0c;数据分析和数据处理是数据科学领域每天都在进行的基本任务。高效和快速的数据转换对于提取有意义的见解和基于数据做出明智决策至关重要。其中最受欢迎的工具之一是Python库Pandas&#xff0c;它提供了一个功能强大的DataFrame工具&#xff0c;使用灵活直观的…

【STL详解 —— stack和queue的介绍及使用】

STL详解 —— stack和queue的介绍及使用 stackstack的定义方式stack的使用 queuequeue的定义方式queue的使用 stack stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其只能从容器的一端进行元素的插入与提取操作。 stack的定义方式 首…

list 简化版模拟实现

1ListNode template<class T>struct ListNode{public:ListNode(const T& x T()):_next(nullptr), _prev(nullptr), _data(x){}//private://共有可访问ListNode<T>* _next;ListNode<T>* _prev;T _data;}; 实现iterator对Node*的封装 实现运算符重载 vo…

深入理解DES算法:原理、实现与应用

title: 深入理解DES算法&#xff1a;原理、实现与应用 date: 2024/4/14 21:30:21 updated: 2024/4/14 21:30:21 tags: DES加密对称加密分组密码密钥管理S盒P盒安全性分析替代算法 DES算法简介 历史 DES&#xff08;Data Encryption Standard&#xff09;算法是由IBM研发&…

整数在内存中的存储和内存操作函数

目录 整数在内存中的存储1. 整数在内存中的存储2. 大小端字节序和字节序判断2.1 什么是大小端?2.2 为什么有大小端 3. 练习3.1 请简述大端字节序和小端字节序的概念&#xff0c;设计⼀个小程序来判断当前机器的字节序。&#xff08;10分&#xff09;-百度笔试题3.2 练习23.3 练…

科研学习|科研软件——如何使用SmartPLS软件进行结构方程建模

SmartPLS是一种用于结构方程建模&#xff08;SEM&#xff09;的软件&#xff0c;它可以用于定量研究&#xff0c;尤其是在商业和社会科学领域中&#xff0c;如市场研究、管理研究、心理学研究等。 一、准备数据 在使用SmartPLS之前&#xff0c;您需要准备一个符合要求的数据集。…

Vector - CAPL - XCP介绍_02

前面我们介绍了关于使用vector XCP License后&#xff0c;通过CAPL对XCP协议进行连接、断开和获取当前XCP连接状态的函数&#xff0c;本篇文章不做过多的其他赘述&#xff0c;我们继续介绍CAPL控制XCP相关的其他函数。 目录 xcpActivate 代码示例 xcpDeactivate xcpActiva…

【周总结】

周总结 完成工单脚本的编写以及解决操作问题 完成相关 jira 问题修改 学习了 sentinel 整合项目使用流控&#xff0c;熔断&#xff0c;热点等功能的使用 2024/4/14 晴 1. 这周只办两件事&#xff0c;吃&#xff01;喝&#xff01;&#xff01;&#xff01; 2.忙忙忙&am…

开源博客项目Blog .NET Core源码学习(15:App.Hosting项目结构分析-3)

本文学习并分析App.Hosting项目中前台页面的关于本站页面和点点滴滴页面。 关于本站页面 关于本站页面相对而言布局简单&#xff0c;与后台控制器类的交互也不算复杂。整个页面主要使用了layui中的面包屑导航、选项卡、模版、流加载等样式或模块。   面包屑导航。使用layui…

Web前端 Javascript笔记3

1、垃圾回收机制 内存中的生命周期 1、内存分配 2、内存使用&#xff08;读写&#xff09; 3、内存回收&#xff0c;使用完毕之后&#xff0c;垃圾回收器完成 内存泄漏&#xff1a;该回收的&#xff0c;由于某些未知因素&#xff0c;未释放&#xff0c;叫做内存泄漏 栈&#xf…

PostgreSQL入门到实战-第二十九弹

PostgreSQL入门到实战 PostgreSQL中数据分组操作(四)官网地址PostgreSQL概述PostgreSQL中CUBE命令理论PostgreSQL中CUBE命令实战更新计划 PostgreSQL中数据分组操作(四) 如何使用PostgreSQL CUBE生成多个分组集 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不…

Linux配置程序后台运行(前后台来回切换)

Linux配置程序后台运行 在日常开发过程中&#xff0c;会遇到我们在前台运行程序&#xff0c;此时我们临时有事&#xff0c;但不能关闭终端&#xff0c;否则程序就会在电脑熄屏&#xff08;终端session断开后&#xff09;停止运行。 那么作为一个合格的开发&#xff0c;就必须要…

Matlab 2024安装教程(附免费安装包资源)

鼠标右击软件压缩包&#xff0c;选择“解压到MatlabR2024a“。 2.打开解压后的文件夹&#xff0c;鼠标右击“MATHWORKS_R2024A“选择装载。 鼠标右击“setup“选择”以管理员身份运行“。点击“是“&#xff0c;然后点击”下一步“。复制一下密钥粘贴至输入栏&#xff0c;然后…

数据结构——双向链表的实现

文章目录 一、双向链表的结构二、实现双向链表创建双向链表的项目双向链表的结构List.h申请结点初始化打印尾插头插尾删头删查找在pos位置之后插入数据删除pos结点销毁 三、所有源代码List.hList.ctest.c 一、双向链表的结构 带头双向循环链表 注意&#xff1a;这⾥的“带头…

数据交换格式

一、什么是数据交换格式 在计算机的不同程序之间&#xff0c;或者不同的编程语言之间进行交换数据&#xff0c;也需要一种大家都能听得懂得‘语言’&#xff0c;这就是数据交换格式&#xff0c;它通过文本以特定的形式来进行描述数据。 二、常用的几种数据交换格式 客户端常…

图像处理ASIC设计方法 笔记17 连通域的图像标记算法

目录 (一)主要环节图像初步标记 -等价表初始化与等价关系记录 -整理等价表 -图像标记代换 -(二)详细算法步骤:1 等价表的操作:2 整理等价表:3 图像标记代换:连通域的图像标记算法有若干步骤,除了图像初步标记,还有等价表初始化、等价关系记录、整理等价表、图像标记代…

【C++】深度解析---赋值运算符重载(小白一看就懂!!)

目录 一、前言 二、 运算符重载 &#x1f34e;运算符重载 ① 概念引入 ② 语法明细 ③ 练习巩固 ④ 代码展示 &#x1f347;赋值运算符重载 ① 语法说明及注意事项 ② 默认的赋值运算符重载 ③ 值运算符不能重载成全局函数&#xff01; 三、总结 四、共勉 一、前言…

前端console用法分享

console对于前端人员来讲肯定都不陌生&#xff0c;相信大部分开发者都会使用console来进行调试&#xff0c;但它能做的绝不仅限于调试。 最常见的控制台方法 作为开发者&#xff0c;最常用的 console 方法如下&#xff1a; 控制台打印结果&#xff1a; 今天我分享的是一些 co…

Qt快速入门(MV架构之TableView + QStandardItemModel + 自定义代理小案例)

Qt快速入门&#xff08;MV架构之TableView QStandardItemModel 自定义代理小案例&#xff09; 关于MV架构的简单介绍 在Qt框架中&#xff0c;代理&#xff08;Delegate&#xff09;、模型&#xff08;Model&#xff09;和视图&#xff08;View&#xff09;之间的关系构成了…

jvisualVM分析jvm内存使用快照dump

服务发生内存溢出&#xff0c;就需要查看服务器上Java服务的jvm堆内存使用情况&#xff0c;可以使用dump命令生成dump文件&#xff0c;然后下载到本地&#xff0c;然后使用jvisualVM工具打开&#xff0c;即可实现可视化分析。 生成dump文件常用的两种方式&#xff1a; 第一种…