后台框架-统一异常管理

news2024/9/22 14:39:42

搭建后台框架全局异常管理是一个很重要的部分,好在SpringBoot提供了很好的处理方法

使用@ControllerAdvice

@ControllerAdvice是Spring MVC中的一个全局异常处理注解,它允许在一个地方集中处理所有控制器抛出的异常。通过使用@ControllerAdvice,可以避免在每个控制器中重复编写异常处理逻辑,从而使代码更加简洁和易于维护。

基本用法

要使用@ControllerAdvice,创建一个类,并在该类上添加@ControllerAdvice注解。然后,在该类中定义多个@ExceptionHandler方法,每个方法处理一种特定的异常类型。

示例代码

package org.example.web.web;

import org.example.web.model.R;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理全局异常
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Object exceptionHandler(Exception e){
        // 运行时异常
        if(e instanceof RuntimeException){
            RuntimeException ex = (RuntimeException) e;
            return R.error(500, ex.getMessage());
        }
        return R.error(999, e.getMessage());
    }
}

继承BasicErrorController

BasicErrorController是Spring Boot中用于处理错误页面的默认控制器。当应用程序发生错误时,Spring Boot会自动调用BasicErrorController来处理错误,并返回相应的错误页面。

基本用法

BasicErrorController处理两个主要点:
/error:处理所有类型的错误。
/error/{code}:处理特定HTTP状态码的错误。
默认情况下,BasicErrorController会返回一个简单的HTML错误页面,显示错误的状态码和消息。你可以通过自定义BasicErrorController来改变这种行为。

示例代码

package org.example.web.web;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.example.web.model.R;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;

@Slf4j
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class CustomErrorController extends BasicErrorController {

    @Value("${server.error.path:${error.path:/error}}")
    private String path;

    public CustomErrorController(ServerProperties serverProperties) {
        super(new DefaultErrorAttributes(), serverProperties.getError());
    }

    /**
     * 覆盖默认的JSON响应
     */
    @Override
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = getStatus(request);
        Map<String, Object> map = new HashMap<>(16);
        Map<String, Object> originalMsgMap = getErrorAttributes(request, ErrorAttributeOptions.defaults());
        String path = (String) originalMsgMap.get("path");
        String error = (String) originalMsgMap.get("error");
        String message = (String) originalMsgMap.get("message");
        StringJoiner joiner = new StringJoiner(",", "[", "]");
        joiner.add(path).add(error).add(message);
        map.put("code", status.value());
        map.put("message", joiner.toString());
        return new ResponseEntity<Map<String, Object>>(map, status);
    }

    /**
     * 覆盖默认的HTML响应
     */
    @Override
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        // 请求的状态
        HttpStatus status = getStatus(request);
        response.setStatus(getStatus(request).value());
        Map<String, Object> model = getErrorAttributes(request, ErrorAttributeOptions.defaults());
        ModelAndView modelAndView = resolveErrorView(request, response, status, model);
        // 指定自定义的视图
        log.error("{} - {} - {}", request.getRequestURI(), status.value(), JSON.toJSONString(model));
        if (status.value() == 404) {
            return (modelAndView == null ? new ModelAndView("err/404", model) : modelAndView);
        } else if (status.value() == 500) {
            return (modelAndView == null ? new ModelAndView("err/500", model) : modelAndView);
        } else {
            return (modelAndView == null ? new ModelAndView("err/other", model) : modelAndView);
        }
    }
}

静态错误页面

/src/main/resources/templates/err/404.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="zh">
<head>
    <title th:text="${'ERROR - ' + status}"></title>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width:device-width,initial-scale=1" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <link th:href="@{/css/style.css}" rel="stylesheet" />
</head>
<body>
    ERROR-<span th:text="${status}"></span>-<span th:text="${error}"></span>
</body>
</html>

/src/main/resources/templates/err/500.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="zh">
<head>
    <title th:text="${'ERROR - ' + status}"></title>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width:device-width,initial-scale=1" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <link th:href="@{/css/style.css}" rel="stylesheet" />
</head>
<body>
    ERROR-<span th:text="${status}"></span>-<span th:text="${error}"></span>
</body>
</html>

ResultResponseAdvice

package org.example.web.web;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.web.model.R;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.Map;

/**
 * 对controller 层中 ResponseBody 注解方法,进行增强拦截
 */
@ControllerAdvice
public class ResultResponseAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 返回true表示对所有Controller的返回值进行处理
        return true;
    }

    /**
     * 如果开启,就会对返回结果进行处理
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        // 设置响应类型为json
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
        if (body instanceof R) {
            // 如果body返回的是ResultMsg类型的对象,不进行增强处理
            response.setStatusCode(HttpStatus.valueOf(((R<?>) body).getCode()));
            return body;
        }
        if (body instanceof String) {
            // 如果body返回的是String类型的对象,单独处理
            return toJson(body);
        }
        if (body instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) body;
            if (map.containsKey("code") && map.containsKey("message")) {
                int code = Integer.parseInt(map.get("code").toString());
                response.setStatusCode(HttpStatus.valueOf(code));
                if (code == 200) {
                    return R.ok(map.get("message").toString());
                } else {
                    return R.error((int)map.get("code"), map.get("message").toString());
                }
            } else {
                return R.ok(map);
            }
        }
        return R.ok(body);
    }

    private Object toJson(Object body) {
        try {
            return new ObjectMapper().writeValueAsString(R.ok(body));
        } catch (JsonProcessingException e) {
            throw new RuntimeException("无法转发json格式", e);
        }
    }
}

最后效果

访问一个不存在的页面时:
在这里插入图片描述
访问一个不存在的接口时:
在这里插入图片描述
访问一个抛错误的接口时:

    @RequestMapping("/test")
    @ResponseBody
    public User test() {
        throw new RuntimeException("User not found");
    }

在这里插入图片描述

源代码

访问后台框架-统一异常处理源码

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

资源不是问题,网盘搜索神器!极空间部署聚合搜索工具『爱盼』

资源不是问题&#xff0c;网盘搜索神器&#xff01;极空间部署聚合搜索工具『爱盼』 哈喽小伙伴们好&#xff0c;我是Stark-C~ 经常分享一些NAS的玩法和攻略&#xff0c;但是被问及最多的还是资源问题&#xff0c;我在那里下载啊&#xff1f;我在那里看啊&#xff1f;等等...…

LLM系列 | 36:Google最新开源大模型:Gemma 2介绍及其微调(下篇)

引言 环境安装 数据准备 下载 处理 模型训练 模型inference 结果 gemma-2-9b gemma-2-9b-it 引言 低头观落日&#xff0c;引手摘飞星。 小伙伴们好&#xff0c;我是微信公众号《小窗幽记机器学习》的小编&#xff1a;卖黑神话的小女孩。本文紧接前文Google最新开源大…

HarmonyOS NEXT未成年人模式无缝联动所有应用,过滤非适龄内容

背景 随着消费电子产品和移动互联网的普及&#xff0c;未成年人互联网普及率96.8%&#xff0c;超过80%的未成年人都拥有自己的上网设备&#xff0c;而如何引导孩子正确上网一直是家长们的担忧。市场上很多电子设备、系统推出了一些未成年人管控能力&#xff0c;却需要家长到各…

前端技术(六)—— AJAX详解

一、原生 AJAX 1. AJAX 简介 AJAX 全称为 Asynchronous JavaScript And XML&#xff0c;就是异步的 JS 和 XML。 通过 AJAX 可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;无刷新获取数据。 AJAX 不是新的编程语言&#xff0c;而是一种将现有的标准组…

【练习3】点击消除

地址&#xff1a;点击消除_牛客题霸_牛客网 (nowcoder.com) 分析&#xff1a; 消除的是“相邻且相等”的数&#xff0c;可以考虑用栈。这里使用StringBuilder来代替栈&#xff0c;好处是StringBuilder可以实现尾插和尾删的操作。 public class Main {public static void main(S…

Caching介绍

缓存&#xff08;Caching&#xff09;是计算机系统中一个非常关键且常用的技术&#xff0c;用于提高数据访问速度和系统性能。在Java编程中&#xff0c;缓存同样扮演着重要角色&#xff0c;特别是在处理大量数据、频繁访问数据库或远程服务时。 缓存的基本原理 缓存的基本原理是…

雅特力AT-START-F423开发板

文章目录 1、开箱2、硬件设计3、点灯初试4、gpio输入和中断5、定时器基本定时6、定时器输出PWM7、串口使用8、ADC和DAC测试9、IIC驱动OLED测试10、SPI驱动测试11、总结 1、开箱 最近官方给寄了板子&#xff0c;顺便测评一下吧&#xff0c;首先是开箱环节 板子是调试器开发板的…

栈与队列,优先队列与双端队列(C++)

在C语言阶段&#xff0c;我们学过两种数据结构&#xff0c;栈与队列&#xff0c;一种是先进后出&#xff0c;一种是先进先出。 在C阶段&#xff0c;我们有新容器来方便快捷的使用栈和队列而不需要我们手动来编写 即stack与queue 我们直接来看对应接口 stack 同时放上对应的…

9.标准化与软件知识产权基础知识

大纲 标准的分类 标准的编号 保护期限 知识产权的确定 侵权判断 补充 真题 1

全国大学生数据建模比赛——深度学习

全国大学生数学建模比赛中&#xff0c;深度学习可以成为解决复杂问题的有力手段。 一、深度学习的优势在比赛中的体现 强大的模式识别能力&#xff1a;深度学习模型&#xff0c;如卷积神经网络&#xff08;CNN&#xff09;和循环神经网络&#xff08;RNN&#xff09;&#xff0…

Linux 系统进程管理实战

今天给伙伴们分享一下Linux 系统进程管理&#xff0c;希望看了有所收获。 我是公众号「想吃西红柿」「云原生运维实战派」作者&#xff0c;对云原生运维感兴趣&#xff0c;也保持时刻学习&#xff0c;后续会分享工作中用到的运维技术&#xff0c;在运维的路上得到支持和共同进步…

系统分析师6:计算机网络

文章目录 1 OSI/RM七层模型2 TCP/IP协议族2.1 常见TCP/IP协议基础2.2 DNS 3 IP地址4 网络规划与设计4.1 网络规划与设计的阶段4.2 层次化网络设计 5 综合布线6 网络存储技术-Raid7 网络接入技术 1 OSI/RM七层模型 集线器多个端口属于同一个冲突域&#xff1b; 交换机多个端口属…

nexus 清理 docker 镜像

下载配置 nexus-cli 看网上文档都用如下地址&#xff0c;但现在已经不能下载&#xff1a; wget https://s3.eu-west-2.amazonaws.com/nexus-cli/1.0.0-beta/linux/nexus-cli chmod x nexus-cli 在 github 上下载&#xff1a; wget https://github.com/heyonggs/nexus-cli/r…

【202408最新】Anaconda+VSCode+CUDA+Pytorch安装配置保姆级教程

最近新换了电脑&#xff0c;又开始从头配置代码环境&#xff0c;到处看教程真的一个头两个大&#xff0c;干脆自己整理了一下&#xff0c;方便以后一站式重装。也提供给大家参考。 1.Anaconda下载安装 Anaconda和Python是替代品&#xff08;也不是&#xff09;&#xff0c;下…

uniapp引入最新版Animate.css及使用示例

亲测可用,不好用请移至评论区揍我 动画库官网:https://animate.style/ cdn地址:https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css(截至目前最新版为:v4.1.1) 1. 将css下载后导入项目static目录中 2. 重要!修改下载的css文件内容 文件内容如…

Audi TT MK1保险丝盒布局说明书

Audi TT MK1保险丝盒布局说明书 保险丝序号额定最大电流(A)对应功能描述110加热式洗涤器喷嘴、加热式后视镜210转向灯35车牌灯45通用电气&#xff0c;导航57.5停车辅助65中控锁系统710倒车灯85电话95防抱死制动系统/ESP1015发动机正时:汽油机115仪表盘、换档锁、自动&#xff0…

了解内网穿透以及简单应用

因特网概述 节点&#xff08;Node&#xff09;&#xff1a;网络中的节点可以是计算机&#xff08;笔记本电脑、台式电脑&#xff0c;服务器等&#xff09;、网络互联设备&#xff08;集线器、交换机&#xff0c;路由器等&#xff09;、其他具有联网功能的设备 &#xff08;网络…

TCP 之 三次握手 (面经计网篇)

这是tcp 简历连接的三次握手方式 , 其中的特殊符号 , 我解释下 , SYN 是 同步的这个单词(synchronization), ACK 是回执,承认的单词(acknowledgement), SYN-ACK 服务器收到SYN报文后&#xff0c;回复一个带有SYN和ACK标志的报文段&#xff0c;这表示服务器已经收到了客户端的SY…

【C++ Primer Plus习题】8.7

问题: 解答: #include <iostream>using namespace std;template <typename T> T SumArray(T arr[], int n) {T sum arr[0] - arr[0];for (int i 0; i < n; i){sum arr[i];}return sum; }template <typename T> T SumArray(T *arr[], int n) {T sum *…

sqli-libs第四关详解

首先判断是数字型注入还是字符型注入 正常显示&#xff0c;说明是字符型注入&#xff0c;那么尝试单引号闭合 还是正常显示&#xff0c;尝试双引号闭合 有报错信息&#xff0c;含有括号&#xff0c;这时就应该想到&#xff0c;sql代码是("$id")这样写的了。直接采取闭…