生活打了我们一巴掌,我们,一定要想办法再打回来

上一章简单介绍了SpringBoot上传文件到远程服务器(二十九),如果没有看过,请观看上一章

一. 为什么要实现异常信息自定义展示

在 Springboot 项目开发中,包括以前的 SSM 框架开发中,我们常常会碰到 404 500 相应的错误信息.

如访问一个除以 0 的功能 (500) 错误

  @RequestMapping("/div")@ResponseBodypublic String div(){int result=10/0;return "相除后的结果是:"+result;}

或者访问一个不存在的页面 404 错误

这两个页面都是 SpringBoot 默认提供的.

用户在使用的过程中,如果没有相应的开发或者网络经验,对这些提示信息是很讨厌的。

所以常常进行一些有趣的设计,来减轻项目运行错误导致的不良感受。

如: (以下图片来源于网络)

我们可以自定义错误页面.

二. 自定义错误页面

二.一 Tomcat 服务器配置错误页面

在以前的 Tomcat 和 JSP 时代 ,我们可以这样配置错误页面.

二.一.一 自定义 404.jsp 和 505.jsp 页面

404.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isErrorPage="true"%>
<% response.setStatus(HttpServletResponse.SC_OK);%>404,地址错误

500.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isErrorPage="true"%>
<% response.setStatus(HttpServletResponse.SC_OK);%>500, 服务器错误,联系管理员

二.一.二 web.xml 配置信息

在 web.xml 配置中,进行配置

 <!--错误状态码指定-->
<error-page><error-code>404</error-code><location>/WEB-INF/jsp/404.jsp</location></error-page><!--异常类型指定,也可以细化一下--><error-page><exception-type>java.lang.Throwable</exception-type><location>/WEB-INF/jsp/500.jsp</location></error-page><!--错误状态码指定--><error-page><error-code>500</error-code><location>/WEB-INF/jsp/500.jsp</location></error-page>

二. 二SpringBoot 配置错误页面

根据上面的提示,我们知道,需要在 /error 目录下进行配置相关的页面信息.

配置错误页面,包括两种,一种是静态的异常页面,一种是动态的异常页面.

二.二.一 静态异常页面

在 static 目录下 创建 error 目录, 里面放置 404.html 和 500.html 页面

404.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>404异常</title>
</head>
<body>404异常
</body>
</html>

500.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>500 异常信息</title>
</head>
<body>500异常
</body>
</html>

重新运行服务器,并且访问

在访问 div (有异常的方法)

在访问 404 错误页面时

就变成了我们自定义的异常页面了.

除了指定 404 ,500 这样确切的错误状态码外,也可以使用 4xx, 5xx 这样的来统一进行接收.

二.二.二 动态错误页面

有时候常常需要将错误的信息展示出来,便于开发人员进行处理.

可以使用动态错误页面,通常放置在 templates 目录下

在 templates 目录下,创建动态错误页面

404.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>404状态码展示错误</title>
</head>
<body>
<h1>404</h1>
<table border="1"><tr><td>path</td><td th:text="${path}"></td></tr><tr><td>error</td><td th:text="${error}"></td></tr><tr><td>message</td><td th:text="${message}"></td></tr><tr><td>timestamp</td><td th:text="${timestamp}"></td></tr><tr><td>status</td><td th:text="${status}"></td></tr>
</table>
</body>
</html>

500.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>5xx状态码展示错误</title>
</head>
<body>
<h1>5xx</h1>
<table border="1"><tr><td>path</td><td th:text="${path}"></td></tr><tr><td>error</td><td th:text="${error}"></td></tr><tr><td>message</td><td th:text="${message}"></td></tr><tr><td>timestamp</td><td th:text="${timestamp}"></td></tr><tr><td>status</td><td th:text="${status}"></td></tr>
</table>
</body>
</html>

这五个属性信息, path, error,message,timestamp,status

是 由: org.springframework.boot.web.reactive.error.DefaultErrorAttributes 进行定义的

这个时候,再进行访问

会展示出具体的信息.

发现,动态的错误页面是生效的。

如果动态页面和静态页面同时定义了异常处理页面,

例如 classpath:/static/error/404.htmlclasspath:/templates/error/404.html 同时存在时,

默认使用动态页面。

完整的错误页面查找方式应该是这样:

发生了 404错误–>查找动态 404.html 页面–>查找静态 404.html –> 查找动态 4xx.html–>查找静态 4xx.html。

500 也一样.

发生了 500 错误–>查找动态 500.html 页面–>查找静态 500.html –> 查找动态 5xx.html–>查找静态 5xx.html。

三. 后端服务器自定义异常

三.一 提示信息和内容自定义

SpringBoot 提供的页面和属性信息,有时候不符合业务场景,需要后端开发人员进行自定义(自带的 message 是英文的,不符合国人习惯)

我们可以自定义修改

package top.yueshushu.learn.error;import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;import java.util.Map;/*** @ClassName:MyErrorAttributes* @Description 后端自定义的提示信息* @Author zk_yjl* @Date 2021/11/24 17:34* @Version 1.0* @Since 1.0**/
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {@Overridepublic Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);//也可以放置其他的属性信息,或者替换属性,如 message,或者 timestampif ((Integer)map.get("status") == 500) {map.put("message", "服务器内部错误!");}if ((Integer)map.get("status") == 404) {map.put("message", "页面找不到!");}if ((Integer)map.get("status") == 403) {map.put("message", "未授权!");}return map;}
}

提示信息发生了改变,变成了开发人员自定义的提示信息.

三.二 自定义视图解析和返回内容

将 MyErrorAttributes 去掉 @Component 组件注解

继承DefaultErrorViewResolver 解析视图类

@Component
public class MyErrorViewResolver extends DefaultErrorViewResolver {/**构造方法*/public MyErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties)   {super(applicationContext, resourceProperties);}@Overridepublic ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {// 可以根据状态  status, 自定义视图页面和 model//不能直接修改 modelMap<String, Object> newMap = new HashMap<>();for(Map.Entry<String,Object> entry:model.entrySet()){newMap.put(entry.getKey(),entry.getValue());}newMap.put("findUser","两个蝴蝶飞");// NOT_FOUND(404, "Not Found"),if(HttpStatus.NOT_FOUND.equals(status)){return new ModelAndView("/self/404.html",newMap);}// 500错误// INTERNAL_SERVER_ERROR(500, "Internal Server Error"),if(HttpStatus.INTERNAL_SERVER_ERROR.equals(status)){return new ModelAndView("/self/500.html",newMap);}//走一个默认的页面return new ModelAndView("/static/error/500.html",newMap);}
}

在 template 目录下, 创建 self , 后续创建 500.html, 404.html

500.html , 404.html 添加一个自定义的属性, 就是 findUser

四. 前后端处理自定义异常信息

现在的项目,都是前后端分离的项目,希望在出现异常时,返回的是一个 json格式的数据,并不是跳转到页面。

前端开发人员,拿到错误的信息之后,个性化进行处理.

去掉 MyErrorViewResolver 上面的 @Component 注解

同时,去掉 自定义的 静态异常和动态异常信息, 将 error 文件夹重命名为 error2

四.一 定义异常信息

四.一.一 统一返回结果 OutputResult

@Data
public class OutputResult implements Serializable {/*** @param code 响应代码* @param message 响应信息* @param data 响应的数据*/private Integer code;private String message;private Map<String,Object> data=new HashMap<String,Object>();/*** 构造方法 私有。 避免外部构造*/private OutputResult(){}/*** 成功* @return*/public static  OutputResult fail(){OutputResult outputResult=new OutputResult();outputResult.code=500;outputResult.message="失败";return outputResult;}/*** 成功* @return*/public static  OutputResult fail(String message){OutputResult outputResult=new OutputResult();outputResult.code=500;outputResult.message=message;return outputResult;}/*** 成功* @return*/public static  OutputResult success(){OutputResult outputResult=new OutputResult();outputResult.code=200;outputResult.message="成功";return outputResult;}/*** 成功* @param data  要响应的数据* @return*/public static  OutputResult success(Object data){OutputResult outputResult=new OutputResult();outputResult.code=200;outputResult.message="成功";outputResult.data.put("result",data);return outputResult;}
}

四.一.二 自定义的异常类 BusinessException

public class BusinessException extends Exception {String message="";public BusinessException(String message){super(message);this.message=message;}public void setMessage(String message) {this.message = message;}@Overridepublic String getMessage() {return message;}
}

四.一.三 Controller 层异常方法 ExceptionController

@Controller
public class ExceptionController {@RequestMapping("/")public String index(){return "index";}/*** 会出现除 0异常* @date 2021/11/9 20:54* @author zk_yjl* @param* @return java.lang.String*/@RequestMapping("/div")@ResponseBodypublic OutputResult div(){int result=10/0;return OutputResult.success(result);}/*** 会出现空指针异常* @date 2021/11/9 20:54* @author zk_yjl* @param* @return java.lang.String*/@RequestMapping("/npe")@ResponseBodypublic OutputResult npe(){String str=null;return OutputResult.success(str.length());}/*** 会出现下标越界异常* @date 2021/11/9 20:54* @author zk_yjl* @param* @return java.lang.String*/@RequestMapping("/array")@ResponseBodypublic OutputResult array(){String[] arr=new String[]{"岳泽霖","两个蝴蝶飞"};return OutputResult.success(arr[arr.length]);}/*** 会出现下业务型异常* @date 2021/11/9 20:54* @author zk_yjl* @param* @return java.lang.String*/@RequestMapping("/bus")@ResponseBodypublic OutputResult bus() throws BusinessException {try{int aa=10/0;}catch (Exception e){//去查询数据库throw new BusinessException("查询数据库失败了");}return OutputResult.success("查询数据库成功");}/*** 会出现下业务型异常* @date 2021/11/9 20:54* @author zk_yjl* @param* @return java.lang.String*/@RequestMapping("/other")@ResponseBodypublic OutputResult other() throws Exception {//去查询数据库try{int aa=10/0;}catch (Exception e){//去查询数据库throw new Exception("其他的异常信息");}return OutputResult.success("查询数据库成功");}
}

进行调用时,

这样很不好看,也不方便管理.

四.二 自定义全局异常处理器 MyExceptionHandler

通过 @ExceptionHandler 注解,接收相应的异常信息

@RestControllerAdvice
public class MyExceptionHandler {/*** 处理空指向的异常信息* @date 2021/11/10 11:52* @author zk_yjl* @param* @return top.yueshushu.learn.response.OutputResult*/@ExceptionHandler(NullPointerException.class)public OutputResult npeException(HttpServletRequest req, NullPointerException e){return OutputResult.fail(e.getMessage());}/*** 处理算术的异常信息* @date 2021/11/10 11:52* @author zk_yjl* @param* @return top.yueshushu.learn.response.OutputResult*/@ExceptionHandler(ArithmeticException.class)public OutputResult ariException(HttpServletRequest req, ArithmeticException  e){return OutputResult.fail(e.getMessage());}/*** 处理数组下标越界异常的异常信息* @date 2021/11/10 11:52* @author zk_yjl* @param* @return top.yueshushu.learn.response.OutputResult*/@ExceptionHandler(ArrayIndexOutOfBoundsException.class)public OutputResult arrException(HttpServletRequest req, ArrayIndexOutOfBoundsException  e){return OutputResult.fail(e.getMessage());}/*** 处理自定义的业务异常的异常信息* @date 2021/11/10 11:52* @author zk_yjl* @param* @return top.yueshushu.learn.response.OutputResult*/@ExceptionHandler(BusinessException.class)public OutputResult busException(HttpServletRequest req, BusinessException  e){return OutputResult.fail(e.getMessage());}/*** 处理其他的另外异常的异常信息* @date 2021/11/10 11:52* @author zk_yjl* @param* @return top.yueshushu.learn.response.OutputResult*/@ExceptionHandler(Exception.class)public OutputResult otherException(HttpServletRequest req, Exception  e){return OutputResult.fail(e.getMessage());}
}

这时候,再进行访问:

目前老蝴蝶的 状态码都是 500, 实际用的时候,会定义成不同的状态码, npe表示 300, 除 o表示 301,其它异常都有各自的状态码。

前端可以根据状态码,进行不同的展示信息

从而实现前后端分离下的全局异常处理机制.

五. 非抛出异常处理

在实际的业务中,会有 dao数据库层,servie层, controller 层,每一层都会有相应的异常抛出,也可以各自在自己的层捕获异常。

当异常被补获时,没有被抛出时,是什么情况呢? 一般不会在 dao层捕获异常.

五.一 Service 层

public interface ExceptionService {/**普通的两个异常信息,不补获*/public void div();/**普通的两个异常信息,内部进行补获*/public void tryDiv();/**往外抛出异常*/public void throwDivExcepiton() throws Exception;public void throwBusExcepiton() throws Exception;}

五.二 SerivceImpl 实现

@Service
public class ExceptionServiceImpl implements ExceptionService {@Overridepublic void div() {int a=10/0;}@Overridepublic void tryDiv() {try{int a=10/0;}catch (Exception e){e.printStackTrace();}}@Overridepublic void throwDivExcepiton() throws Exception {try{int a=10/0;}catch (Exception e){throw e;}}@Overridepublic void throwBusExcepiton() throws Exception {throw new BusinessException("数据库查询出现异常");}
}

五.三 Controller 层 ServiceExceptionController

@RestController
@RequestMapping("/exce")
public class ServiceExceptionController {@Autowiredprivate ExceptionService exceptionService;@GetMapping("/div")public OutputResult div(){exceptionService.div();return OutputResult.success();}@GetMapping("/tryDiv")public OutputResult tryDiv(){exceptionService.tryDiv();return OutputResult.success();}@GetMapping("/throwDivExcepiton")public OutputResult throwDivExcepiton() throws Exception {exceptionService.throwDivExcepiton();return OutputResult.success();}@GetMapping("/throwBusExcepiton")public OutputResult throwBusExcepiton() throws Exception {exceptionService.throwBusExcepiton();return OutputResult.success();}@GetMapping("/throwDivExcepiton2")public OutputResult throwDivExcepiton2() {try{exceptionService.throwDivExcepiton();return OutputResult.success();}catch (Exception e){e.printStackTrace();return OutputResult.success("内部补获异常");}}@GetMapping("/throwBusExcepiton2")public OutputResult throwBusExcepiton2() {try{exceptionService.throwBusExcepiton();return OutputResult.success();}catch (Exception e){e.printStackTrace();return OutputResult.success("内部补获异常");}}/** 异常处理,* 出现的异常,没有显式的捕获,都会接收到。如果被try  catch 到,即不往外抛出, 抛出到 controller 层的话, 是不会接收到这个异常的。* 从而不触发异常机制信息.* */
}

运行处理展示:

五.三.一 div 方法

service 层和 controller 层均没有 捕获异常

五.三.二 tryDiv

service 层捕获异常, controller 层没有捕获

没有接收到异常信息

五.三.三 throwDivExcepiton

service 层捕获异常,但往外抛出了, controller 层继续抛出

五.三.四 throwBusExcepiton

service 层抛出异常, controller 层继续抛出

五.三.五 throwDivExcepiton2

service 层捕获异常,但继续抛出异常, controller 捕获异常

五.三.六 throwBusExcepiton2

service 层抛出异常, controller 捕获异常

总结: 异常捕获,是在 Controller 层再往上一层进行处理的, 如果 Controller 层抛出异常,才能获取到,转换成 json的形式,如果 controller 层不抛出异常,是无法获取到的,也无法转换成 json的形式.

本章节的代码放置在 github 上:

https://github.com/yuejianli/springboot/tree/develop/SpringBoot_Exception

谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!

SpringBoot全局异常处理(三十)相关推荐

  1. springboot全局异常处理_SpringBoot:如何优雅地处理全局异常

    之前用springboot的时候,只知道捕获异常使用try{}catch,一个接口一个try{}catch,这也是大多数开发人员异常处理的常用方式,虽然屡试不爽,但会造成一个问题,就是一个Contro ...

  2. springboot 全局异常处理类

    目录标题 springboot 全局异常处理类 依赖 代码 springboot 全局异常处理类 依赖 <!-- Spring Boot Web 依赖 核心 --><dependen ...

  3. [SpringBoot]全局异常处理

    2019独角兽企业重金招聘Python工程师标准>>> 1. 全局异常处理 2. SpringBoot错误处理原理 3. SpringBoot定制错误页面 4. SpringBoot ...

  4. SpringBoot 全局异常处理

    下面首先来观察一个程序代码,例如:现在建立一个控制器,而后这个控制器自己抛出一个异常.@Controller public class MemberController extends Abstrac ...

  5. SpringBoot全局异常处理

    1.为什么要全局异常处理 在实际开发中,如果不处理统一处理异常,那么前端在调用后端提供的接口,就会处理各种的异常结构,对于前端来说那可谓是一场灾难,这对前后端的协作也不友好.比如后端路径:/api/v ...

  6. SpringBoot全局异常处理及前端请求参数校验

    SpringBoot全局异常捕获处理及参数校验 文章目录 SpringBoot全局异常捕获处理及参数校验 为什么要用全局异常处理? 如何进行全局异常捕获和处理? 统一结果封装 统一返回结果 枚举类 使 ...

  7. springBoot 全局异常处理 报错 : Could not resolve method parameter at index 0 in public .....

    在学习 springboot 的全局异常处理时   ,我写了一个处理异常类 @ControllerAdvice public class ErrorCatchController {@Exceptio ...

  8. SpringBoot实践(三十二):5分钟搭建springboot单体应用开发框架

    熟悉语言和开发工具上基础快速使用框架构建应用是个机械工作,5分钟完成开发准备工作,没有冗余动作. 目录 准备工作 开发框架搭建 spring初始化 常规依赖 其他依赖 规范化开发 目录结构 依赖适配 ...

  9. springboot 全局异常处理 自定义mvc错误页面展示

    如下:所有的异常都会进入到这里.根据个人业务按需处理 全局异常处理 @ControllerAdvice public class ErrorAdviceController {//指定捕获对象@Exc ...

  10. springboot全局异常处理详解

    对于全局异常需要认识两个注解 @RestControllerAdvice,@ExceptionHandler 1. @RestControllerAdvice 组成:@ControllerAdvice ...

最新文章

  1. R语言使用trimws函数:trimws函数去除(删除、remove)字符串头尾的空格
  2. 软件工程(2019)第一次作业
  3. 『ORACLE』 配置共享服务器(11g)
  4. java B2B2C 源码多租户电子商城系统-Spring Cloud组件详解
  5. LeetCode Mini Parser(栈操作)
  6. Load Balance Tomcat with Nginx and Store Sessions in Redis--reference
  7. Ecplise中配置Tomcat7服务器
  8. Spark Streaming(四)kafka搭建(单节点,单broker)
  9. linux下c语言tcp文件传输,C语言实现TCP通信
  10. LeetCode —— 1554. 只有一个不同字符的字符串(Python)
  11. 动态规划之图像压缩问题
  12. Qt4.7中 默认的构造函数
  13. 本地计算机上的服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止 解决办法...
  14. 【华为OJ】【算法总篇章】
  15. React开发chrome插件系列教程之插件开发环境搭建
  16. 计算机桌面性能4.4怎么办,台式CPU性能怎么看?桌面CPU天梯图2018年1月更新版 (全文)...
  17. git-remote-https.exe-无法找到入口
  18. 天津大学仁爱学院计算机科学与技术学费,天津大学仁爱学院计算机科学与技术专业2016年在山西理科高考录取最低分数线...
  19. 构造方法 梯形面积
  20. 【分治法】中位数问题,C++

热门文章

  1. WM8978移植到imx6或imx8上
  2. 很好看的source insight配色方案
  3. 计算机二级的图片尺寸,2021计算机二级照片是几寸 大小千万要记住咯
  4. GEE搭建python环境时出现WinError10060,一招解决,亲测有效!
  5. 统计字符 c语言程序,统计字符个数C语言程序.doc
  6. 计算机教室的英文音标,小学四年级英语单词(带音标).doc
  7. win10显示rpc服务器不可用,多种方法解决Win10专业版RPC服务器不可用的方法
  8. 学习Hibernate框架笔记-第1天
  9. php 统计fasta 序列长度和GC含量
  10. word论文排版和写作05:从word中导出pdf