文章目录
17.1 介绍
- 默认情况下,Spring Boot 提供
/error
处理所有错误的映射,也就是说当出现错误时,SpringBoot底层会请求转发到 /error 这个映射 - 比如使用浏览器访问不存在的接口 (路径映射) ,响应一个 “whitelabel” 错误视图,以 HTML 格式呈现给用户
-
SpringBoot 底层默认由 DefaultErrorViewResolver 处理错误
=> Debug 分析一下
- 如果请求的路径不存在,先去默认的静态资源目录下找 404.html
- 如果找不到 404.html 就去默认的静态资源目录下找 4xx.html
- 如果4xx.html 依然找不到,就创建一个 ModelAndView 返回,也就是 我们最先看到的 Whitelabel Error Page 页面
17.2 拦截器VS过滤器
- 使用范围不同
- 过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于 Tomcat等容器,Filter只能在web程序中使用
- 拦截器(Interceptor) 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application等程序中
- 过滤器和拦截器的触发时机也不同
- 过滤器 Filter 是在请求进入容器后,但在进入 servlet 之前进行预处理,请求结束是在 servlet 处理完以后
- 拦截器 Interceptor 是在请求进入 servlet 后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束
- 特别说明
- 过滤器不会处理请求转发
- 拦截器会处理请求转发
17.3 自定义异常页面
17.3.1 文档
17.3.2 自定义异常页面说明
- 如何找到这个文档位置,看下面一步步的指引
- https://docs.spring.io/spring-boot/docs/current/reference/html/index.html
- => a single HTML page => 8.web => servlet web application
- => The “Spring Web MVC Framework” => ErrorHandling
- => Custom Error Pages
17.3.3 自定义异常页面-应用实例
17.3.3.1 需求说明
- 自定义 404.html 500.html 4xx.html 5xx.html
- 当发生相应错误时,显示自定义的页面信息
17.3.3.2 代码实现
- 创建 4 个页面
- 测试 404.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
<h1>4o4 Not Found</h1>
<a href='#' th:href="@{/}">返回主页面</a>
状态码: <h1 th:text="${status}"></h1> <br/>
错误信息: <h1 th:text="${error}"></h1>
</div>
<hr/>
<!--<img src="images/logo.png"/>-->
</body>
</html>
- 浏览器请求 http://localhost:8080/xxx
- debug 可以看到 Model 的信息 (Model的数据最终是放到request域中的),所以可以在 404.html 取出显示
- 创建 D:\xjs_springboot\springboot-usersys\src\main\java\com\xjs\springboot\controller\MyErrorController.java ,用于模拟错误
package com.xjs.springboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
/**
* @Author: 谢家升
* @Version: 1.0
*/
@Controller
public class MyErrorController {
//模拟服务器内部错误 500
@GetMapping("/err")
public String err() {
int i = 10 / 0;
return "manage";
}
//这里我们配置的是 post方式请求 /err2
//一会 使用get方式请求 /err2 ,这样就会出现一个 405 的客户端错误
@PostMapping("/err2")
public String err2() {
return "manage";
}
}
- 完成测试
- 浏览器 http://localhost:8080/err
- 浏览器 http://localhost:8080/err2
17.3.3.2 底层机制分析
- 先根据对应的错误状态码找对应的 页面,比如 405.html
- 如果找不到就找 4xx.html,我们这里提供了,所以就显示 4xx.html
- 如果 4xx.html 也找不到,就显示默认错误页面
- 底层将错误信息封装到 Model中,Model 数据最终会放到request域中
- 所以我们可以在对应的错误页面中取出对应的错误信息
17.3 全局异常
17.3.1 说明
- @ControllerAdvice+@ExceptionHandler 处理全局异常
- 底层是 ExceptionHandlerExceptionResolver 支持的
17.3.2 全局异常-应用实例
- 需求:演示全局异常使用,当发生 ArithmeticException、NullPointerException 时,不使用默认异常机制匹配的 xxx.html ,而是通过全局异常机制显示指定的错误页面
- 创建 D:\xjs_springboot\springboot-usersys\src\main\java\com\xjs\springboot\exception\GlobalExceptionHandle.java
package com.xjs.springboot.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* @Author: 谢家升
* @Version: 1.0
*
* @ControllerAdvice : 使用该注解可以标识一个全局异常处理器/对象
* 该全局异常处理器 会被注入到 spring容器
*
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandle {
/**
* 1. 编写方法 处理指定的异常, 比如这里处理 算数异常和空指针异常,可以指定多个异常
* 2. 这里要处理的异常,由程序员指定即可
* 3. Exception e 表示异常发生后,传递过来的异常对象
* 4. Model model 可以将我们的异常信息,放入到 model,并传递给显示页面
*/
@ExceptionHandler({ArithmeticException.class, NullPointerException.class})
public String handleAritException(Exception e, Model model) {
log.info("异常信息={}", e.getMessage());
//这里将发生的异常信息放入到model => request域 , 然后可以在错误页面取出显示
model.addAttribute("msg", e.getMessage());
return "/error/global"; //视图地址
}
}
- 创建 templates/error/global.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>全局异常</title>
</head>
<body bgcolor="#CED3FE">
<hr/>
<div style="text-align: center">
<h1>全局异常/错误 发生了!</h1><br/>
异常/错误信息: <h1 th:text="${msg}"></h1><br/>
<a href="#" th:href="@{/}">返回主页面</a>
</div>
<hr/>
</body>
</html>
- 完成测试,浏览器 http://localhost:8080/err
17.3.2 全局异常处理过程Debug
17.3.3 获取异常发生的方法
package com.xjs.springboot.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.HandlerMethod;
/**
* @Author: 谢家升
* @Version: 1.0
*
* @ControllerAdvice : 使用该注解可以标识一个全局异常处理器/对象
* 该全局异常处理器 会被注入到 spring容器
*
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandle {
/**
* 1. 编写方法 处理指定的异常, 比如这里处理 算数异常和空指针异常,可以指定多个异常
* 2. 这里要处理的异常,由程序员指定即可
* 3. Exception e 表示异常发生后,传递过来的异常对象
* 4. Model model 可以将我们的异常信息,放入到 model,并传递给显示页面
*
* ==> 提出问题:如何获取到异常发生的方法 ?
*/
@ExceptionHandler({ArithmeticException.class, NullPointerException.class})
public String handleAritException(Exception e, Model model, HandlerMethod handlerMethod) {
log.info("异常信息={}", e.getMessage());
//这里将发生的异常信息放入到model => request域 , 然后可以在错误页面取出显示
model.addAttribute("msg", e.getMessage());
//得到异常发生的方法是哪个?
log.info("异常发生的方法是={}",handlerMethod.getMethod());
return "/error/global"; //视图地址
}
}
17.4 自定义异常
17.4.1 说明
- 如果 Spring Boot 提供的异常不能满足开发需求,程序员也可以自定义异常
@ResponseStatus
+ 自定义异常- 底层是 ResponseStatusExceptionResolver ,底层调用
response.sendError(statusCode, resolvedReason);
- 当抛出自定义异常后,仍然会根据状态码,去匹配使用 xxx.html 显示
- 当然也可以将自定义异常,放在全局异常处理器去处理
17.4.2 自定义异常-应用实例
17.4.2.1 需求说明
- 自定义一个异常 AccessException
- 当用户访问某个无权访问的路径时,抛出该异常
- 显示自定义异常状态码
17.4.2.2 代码实现
回顾一下自定义异常 ==> 文章链接
- 创建 D:\xjs_springboot\springboot-usersys\src\main\java\com\xjs\springboot\exception\AccessException.java
package com.xjs.springboot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* @Author: 谢家升
* @Version: 1.0
*
* 1. AccessException : 我们自定义的一个异常 ==> 回顾 Java基础
* 2. @ResponseStatus(value = HttpStatus.FORBIDDEN) 表示发生AccessException异常,我们通过http协议返回的状态码是 403
* 3. 这个状态码和自定义异常的对应关系是由程序员来决定[尽量合理设置]
*/
@ResponseStatus(value = HttpStatus.FORBIDDEN)
public class AccessException extends RuntimeException {
//提供一个构造器,可以指定信息
public AccessException(String message) {
super(message);
}
//显示的定义一下无参构造器
public AccessException() {
}
}
- 修改 MyErrorController.java
package com.xjs.springboot.controller;
import com.xjs.springboot.exception.AccessException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
/**
* @Author: 谢家升
* @Version: 1.0
*/
@Controller
public class MyErrorController {
//模拟服务器内部错误 500
@GetMapping("/err")
public String err() {
int i = 10 / 0; //模拟算数异常
return "manage";
}
//这里我们配置的是 post方式请求 /err2
//一会 使用get方式请求 /err2 ,这样就会出现一个 405 的客户端错误
@PostMapping("/err2")
public String err2() {
return "manage";
}
//编写方法,模拟一个 AccessException
@GetMapping("/err3")
public String err3(String name) {
//如果用户不是tom , 我们就认为 无权访问
if (!"tom".equals(name)) {
throw new AccessException();
}
return "manage";//视图地址, 请求转发
//return "redirect:/manage.html";
}
}
- 完成测试,浏览器 http://localhost:8080/err3?name=xjs
17.4.2.3 Debug自定义异常处理过程
17.4.2.3 注意事项和细节
- 如果把自定义异常类型,放在全局异常处理器,那么仍然走全局异常处理机制
- 修改全局异常处理器 GlobalExceptionHandle.java
package com.xjs.springboot.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.HandlerMethod;
/**
* @Author: 谢家升
* @Version: 1.0
*
* @ControllerAdvice : 使用该注解可以标识一个全局异常处理器/对象
* 该全局异常处理器 会被注入到 spring容器
*
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandle {
/**
* 1. 编写方法 处理指定的异常, 比如这里处理 算数异常和空指针异常,可以指定多个异常
* 2. 这里要处理的异常,由程序员指定即可
* 3. Exception e 表示异常发生后,传递过来的异常对象
* 4. Model model 可以将我们的异常信息,放入到 model,并传递给显示页面
*
* ==> 提出问题:如何获取到异常发生的方法 ?
*/
@ExceptionHandler({ArithmeticException.class, AccessException.class, NullPointerException.class})
public String handleAritException(Exception e, Model model, HandlerMethod handlerMethod) {
log.info("异常信息={}", e.getMessage());
//这里将发生的异常信息放入到model => request域 , 然后可以在错误页面取出显示
model.addAttribute("msg", e.getMessage());
//得到异常发生的方法是哪个?
log.info("异常发生的方法是={}",handlerMethod.getMethod());
return "/error/global"; //视图地址
}
}
- 修改 MyErrorController.java
- Debug