SpringBoot 多种方式配置错误页面

news2024/12/25 21:18:16

参考资料

  1. SpringBoot异常处理机制-BasicErrorController与@ControllerAdvice
  2. Java开发从工作到原理–BasicErrorController统一异常处理
  3. 【spring boot】spring boot 处理异常
  4. SpringBoot一个请求的处理全过程
  5. @ControllerAdvice和ErrorPageRegistrar接口配置错误页面的问题
  6. SpringBoot全局异常处理
  7. Springboot异常处理只会@ControllerAdvice+@ExceptionHandler?还远远不够!

目录

  • 前期准备
    • 1.1 后台
    • 1.2 错误页面跳转Controller
  • 一. 静态资源文件的方式
    • 1.1 在`/static/error/`下配置错误页面
    • 1.2 在`/templates/error/`下配置错误页面
  • 二. 实现ErrorController接口
  • 三. 实现ErrorPageRegistrar接口
  • 四. @ControllerAdvice + @ExceptionHandler注解
    • 4.1 前台
    • 4.2 全局异常捕获
    • 4.3 Ajax请求异常效果
    • 4.4 普通请求异常效果
  • 五. @ControllerAdvice + @ExceptionHandler注解与其他方式共同使用


当系统出现异常时候,如404或500异常的时候,默认返回的错误页面通常非常简陋。用户也看不懂,这时候我们想通过一些手段,提示用户访问的资源不存在,或者告知系统异常。


前期准备

1.1 后台

  • 通过运行时异常来触发错误页面跳转
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/test22")
public class Test22Controller {

    @GetMapping("/init")
    public ModelAndView init() {
        
        // 🤪运行时异常
        int a = 1 / 0;

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("test22");
        return modelAndView;
    }

    @PostMapping("/test")
    public ResponseEntity<Void> test() {

        // 🤪运行时异常
        int a = 1 / 0;

        return ResponseEntity.noContent().build();
    }
}

1.2 错误页面跳转Controller

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/error")
public class ExceptionPageController {
	
	// 404异常
    @GetMapping("/404error")
    public ModelAndView init404Page() {

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("error/500");
        return modelAndView;
    }
	
	// 500异常
    @GetMapping("/500error")
    public ModelAndView init500Page() {

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("error/500");
        return modelAndView;
    }
}

一. 静态资源文件的方式

当SpringBoot工程启动的时候,默认会去如下所示路径初始化存放静态异常页面

  • /META-INF/resources/error/ 👉优先顺最高
  • /resources/error/
  • /static/error/
  • /public/error/ 👉优先顺最低

❗注意,默认配置下,静态页面的名称要和状态码相同。
即404异常的静态页面需命名为404.html

1.1 在/static/error/下配置错误页面

可以看到只需创建error文件夹和对应的状态码.html页面,错误页面就配置成功

在这里插入图片描述

⏹ 效果

在这里插入图片描述

1.2 在/templates/error/下配置错误页面

❗注意,此时/static/error/下面还存在error页面

在这里插入图片描述

⏹ 效果

👉可以看到/templates/error/下配置的错误页面覆盖了/static/error/下的错误页面

在这里插入图片描述


二. 实现ErrorController接口

  • ErrorController接口的优先级 > 静态资源文件的方式
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@Controller
public class CustomErrorController implements ErrorController {

    private final static String ERROR_PATH = "/error";

    // 状态码和对应页面的map映射
    private final static Map<Integer, String> codePageMap = new HashMap<>(){
        {
            put(HttpStatus.NOT_FOUND.value(), "/common/404_page");
            put(HttpStatus.INTERNAL_SERVER_ERROR.value(), "/common/500_page");
        }
    };

    @RequestMapping(value = ERROR_PATH, produces = MediaType.TEXT_HTML_VALUE)
    public String errorView(HttpServletRequest request) {

        // 从request对象中获取状态码
        Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        request.setAttribute("statusCode", "错误状态码:" + status);

        if (ObjectUtils.isEmpty(status)) {
            return this.getDefaultView();
        }

        // 若状态码包含在map映射中,就返回到指定的错误页面
        Integer statusCode = Integer.valueOf(status.toString());
        if (codePageMap.containsKey(statusCode)) {
            return codePageMap.get(statusCode);
        }

        return this.getDefaultView();
    }

    // 默认的页面
    private String getDefaultView() {
        return codePageMap.get(HttpStatus.INTERNAL_SERVER_ERROR.value());
    }
}

在这里插入图片描述

⏹ 效果

❗可以看到,断点经过我们自定义的CustomErrorController类之后,跳转到了
templates/common/中,我们自定义的404_page.html页面中。

由此可以证明: ErrorController接口的实现类的优先顺 > /templates/error/

在这里插入图片描述


三. 实现ErrorPageRegistrar接口

  • ErrorPageRegistrar接口的优先级 > ErrorController接口
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

@Component
public class ErrorPageConfig implements ErrorPageRegistrar {

    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {

        ErrorPage[] errorPages = {
                // 若指定了静态的html页面,则该页面需要存储在 static/ 路径下
                new ErrorPage(HttpStatus.NOT_FOUND, "/error/404.html"),
                // 如果需要指定 templates/ 路径下的页面,则需要配合get请求的方式,通过ModelAndView进行跳转
                new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/common/500error"),
        };

        registry.addErrorPages(errorPages);
    }
}
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/common")
public class ExceptionPageController1 {

    @GetMapping("/500error")
    public ModelAndView init500Page() {

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("common/500_page");
        return modelAndView;
    }
}

⏹ 效果

在这里插入图片描述


四. @ControllerAdvice + @ExceptionHandler注解

  • 工作中大多使用这种方式
  • Exception异常可能由普通的get请求触发,大多数情况下会由Ajax请求的get和post方法触发

4.1 前台

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <a th:href="@{/test22/init}">点击跳转页面</a>
    <hr>
    <button id="sendRequest">发送jQuery-Ajax请求</button>
</div>
<script th:src="@{/js/public/jquery-3.6.0.min.js}"></script>
<script th:inline="javascript">
    $("#sendRequest").click(function () {

        $.ajax({
            url: `/test22/test`,
            type: 'POST',
            data: JSON.stringify(null),
            // 向服务器发送的数据类型
            contentType: 'application/json;charset=utf-8',
            success: function (data, status, xhr) {
                console.log(data);
            },
            error(xhr, textStatus, errorMessage) {
                // 若状态码为500,则跳转到系统异常页面
                if (xhr.status == 500) {
                    location.href = `${location.origin}/error/500Page`;
                }
            }
        });
    });
</script>
</body>
</html>

4.2 全局异常捕获

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@ControllerAdvice
public final class GlobalExceptionHandler {

    @Resource
    private HttpServletRequest request;

    @Resource
    private HttpServletResponse response;

    @ExceptionHandler(Exception.class)
    public ModelAndView handleException(Exception ex) {

        /*
        * 如果是Ajax引发的Exception异常的话,就将错误的信息返回给前台
        * 然后在前台的error回调方法中访问后台的error接口,从而跳转到systemError页面
        * */
        String requestedWithHeader = request.getHeader("X-Requested-With");
        if ("XMLHttpRequest".equals(requestedWithHeader)) {

            // HttpServletResponse对象是java中的对象
            // response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

            // HttpStatus对象是Spring中的对象,指定错误的状态码,保证jquery的ajax走error方法
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());

            MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
            jsonView.setExtractValueFromSingleKeyModel(true);
            jsonView.setModelKey("jsonKey");

            ModelAndView mv = new ModelAndView(jsonView);
            mv.addObject("jsonKey", Map.of("exception", ex.getMessage()));
            return mv;
        }

        /*
        * 重定向到指定的Controller,然后在Controller中,跳转到systemError页面
        * */
        String redirectPath = request.getContextPath() + "/error/500error";
        RedirectView redirectView = new RedirectView(redirectPath);
        return new ModelAndView(redirectView, Map.of("exception", ex.getMessage()));
    }
}

4.3 Ajax请求异常效果

在这里插入图片描述

4.4 普通请求异常效果

在这里插入图片描述

五. @ControllerAdvice + @ExceptionHandler注解与其他方式共同使用

@ControllerAdvice
public final class GlobalExceptionHandler {
	
	// 省略注入......

    @ExceptionHandler(Exception.class)
    public ModelAndView handleException(Exception ex) {

        String requestedWithHeader = request.getHeader("X-Requested-With");
        if ("XMLHttpRequest".equals(requestedWithHeader)) {
			// 省略Ajax处理的情况......
        }
		
		// 仅返回ModelAndView对象,不在其中指定View视图的名称
        ModelAndView modelView = new ModelAndView();
        modelView.addObject("message", ex.getMessage());
        return modelView;
    }
}

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

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

相关文章

【Linux操作系统】自动化编译make和Makefile

文章目录一.make/makefile简介1.什么是make,makefile?2.为什么要有make/makefile?二.makefile文件规则1.基本规则2.举一个例子3.伪目标4.其他规则三.文件三个时间问题-make程序1.三个时间何时更新2.touch的两个作用3.make程序如何知道依赖文件是否更新?一.make/makefile简介…

手写Srping11(实现AOP切面)

文章目录目标设计项目结构一、代理方法的案例二、代理方法案例拆解实现1、切点表达式——Pointcut2、类匹配器——ClassFilter3、方法匹配器——MethodMatcher4、实现切点表达式类——AspectJExpressionPointcut4.1、匹配验证5、包装切面信息——AdvisedSupport5.1、被代理的目…

Zynq PS之MIO、EMIO调试

目录 原理框图 Vivado中添加&配置Zynq UltraScale MPSoc IP UART设置&#xff08;仅用于调试&#xff0c;非必需&#xff09; MIO、EMIO设置 DDR配置 执行Generate Output Products 执行Create HDL Wrapper 执行File -> Export ->Export Hardware 执行Launch S…

Springboot中配置文件application.yaml的位置

文章目录位置一&#xff1a;整个项目的config包下位置二&#xff1a;整个项目的根目录下位置三&#xff1a;resources文件夹下config包中位置四&#xff1a;resources文件夹下四个位置的优先级位置一&#xff1a;整个项目的config包下 前些天发现了一个巨牛的人工智能学习网站&…

SQL优化实战-0002:select查询不建议使用星号(select *),最好指定具体查询字段

文章目录1.查询时的普遍写法2.问题分析2.1 计算负担2.2 IO负担2.3 覆盖索引失效2.4 缓存压力3.总结1.查询时的普遍写法 select * from the_table_name where ...2.问题分析 2.1 计算负担 数据库需要去解析更多的对象字段、权限、属性&#xff0c;查询数据字典将"*"…

dp刷题(二)分割回文串(详细推导+O(N^3)=>O(N^2)优化)

目录 分割回文串-ii_牛客题霸_牛客网 ​编辑 描述 示例1 思路 状态F(i)&#xff1a;即为第i个字符时所需要切割的最小次数 状态转移方程&#xff1a;F(i) min(F(i), F(j)1) 优化&#xff1a; 注意点 分割回文串-ii_牛客题霸_牛客网 描述 给出一个字符串s&#xff0…

Android---Bottom Sheet

目录​​​​​​​ Bottom Sheet BottomSheetBehavior BottomSheetDialog 完整 Demo Bottom Sheet Bottom Sheet 是 Design Support Library 23.2 版本引入的一个类似于对话框的控件&#xff0c;可以暂且叫做底部弹出框。Bottom Sheet 中的内容默认是隐藏起来的&#xff0…

Python爬虫登录后token处理

今天继续给大家介绍Python爬虫相关知识&#xff0c;本文主要内容是Python爬虫登录后token处理。 一、网页token及token作用 在上文Python爬虫登录后cookie处理中&#xff0c;我们介绍过使用使用Python爬虫解决cookie及网页登录访问问题。 然而&#xff0c;有的网站&#xff0…

一文读懂Linux内核进程及调度时机原理

前言0.1 进程概要进程是对物理世界的建模抽象&#xff0c;每个进程对应一个 task_struct 数据结构&#xff0c;这个数据结构包含了进程的所有的信息。在 Linux 内核中&#xff0c;不会区分线程和进程的概念&#xff0c;线程也是通过进程来实现的&#xff0c;线程和进程的唯一区…

Leetcode:257. 二叉树的所有路径(C++)

目录 问题描述&#xff1a; 实现代码与解析&#xff1a; 递归&#xff1a; 原理思路&#xff1a; 迭代&#xff08;前序&#xff09;&#xff1a; 思路原理&#xff1a; 问题描述&#xff1a; 给你一个二叉树的根节点 root &#xff0c;按 任意顺序 &#xff0c;返回所有…

计算机网络第一章

目录 1.概念 2.标准化工作及其相关组织 3.速率相关的性能指标 4.分层的基本原则&#xff1a; 5.参考模型 1.OSI七层参考模型 2.TCP/IP参考模型 3.五层参考模型 1.概念 计算机网络是网络中的一个分支&#xff0c;组成包括了计算机系统&#xff0c;通信设备&#xff0c;线路…

app逆向 || xx合伙人登陆参数

声明 本文仅供学习参考&#xff0c;如有侵权可私信本人删除&#xff0c;请勿用于其他途径&#xff0c;违者后果自负&#xff01; 如果觉得文章对你有所帮助&#xff0c;可以给博主点击关注和收藏哦&#xff01; 本文适用于对安卓开发和Java有了解的同学 前言 本人最近一直在…

学习笔记5:关于操作符与表达式的求值

目录​​​​​​​ 一.移位操作符 1.左移操作符 2.右移操作符 二.位操作符 1.位运算基本知识 2.位运算的巧妙运用 三.其他操作符 1.算术操作符 2.单目操作符 3.关于逻辑操作符 四.表达式求值 隐式类型转换 (1)整形提升(短整型家族数据的二进制序列补位转换) (2).算…

【最新】SpringBoot集成Dubbo3

最近在学习dubbo&#xff0c;构建一个简单的springboot集成dubbo&#xff0c;中间也是出了好多问题&#xff0c;在这记录下整体的过程。 1. 构建SpringBoot环境 一个简单的聚合工程 dubbo-consumer&#xff1a;是服务消费方dubbo-provider&#xff1a;是服务提供方dubbo-inte…

机器学习笔记之前馈神经网络(二)非线性问题

机器学习笔记之前馈神经网络——非线性问题引言回顾&#xff1a;关于非线性问题解决非线性问题的三种方式引言 上一节介绍了从机器学习到深度学习的过渡&#xff0c;并介绍了深度学习的发展过程。本节将主要介绍如何使用神经网络处理非线性问题 回顾&#xff1a;关于非线性问…

决策树生成、决策树可视化、决策树算法api、泰坦尼克号乘客生存预测案例代码

一、决策树算法api class sklearn.tree.DecisionTreeClassifier(criterion’gini’,max_depthNone,random_stateNone) criterion&#xff1a;特征选择标准&#xff0c;"gini"或者"entropy"&#xff0c;前者代表基尼系数&#xff0c;后者代表信息增益&…

来自 GitHub 2022 的趋势和见解

《Github 2022 发展趋势和见解》发布了这件事小伙伴们知道了吧&#xff1f;这是每个程序员不能错过的年度报告&#xff0c;因为里面详细介绍了语言的发展趋势和热门领域的介绍。那就让我们来看看吧 目录 编程语言 地理分布 贡献时间分配 技术发展趋势 最受欢迎的存储库 …

GoogLeNet详解

入门小菜鸟&#xff0c;希望像做笔记记录自己学的东西&#xff0c;也希望能帮助到同样入门的人&#xff0c;更希望大佬们帮忙纠错啦~侵权立删。 ✨完整代码在我的github上&#xff0c;有需要的朋友可以康康✨ https://github.com/tt-s-t/Deep-Learning.git 目录 一、GoogLeNet…

C++入门——auto、范围for、nullptr

下一篇就要类和对象了&#xff0c;剩了点零碎的知识点就浅浅水一篇把 一. auto关键字 在早期C/C中auto的含义是&#xff1a;使用auto修饰的变量&#xff0c;是具有自动存储器的局部变量&#xff0c;但遗憾的 是一直没有人去使用它&#xff0c;这是由于变量本身就具备生命周期…

算法及时间、空间复杂度

算法 算法是对问题求解过程的一种描述&#xff0c;是为解决一个或一类问题给出的一个确定的、有限长的操作序列。严格说来&#xff0c;一个算法必须满足以下5个重要特性&#xff1a; &#xff08;1&#xff09;有穷性&#xff1a;对于任意一组合法的输入值&#xff0c;在执行有…