SpringBoot第七集:异常处理与整合JSR303校验(2020最新最易懂)
SpringBoot第七集:异常处理与整合JSR303校验(2020最新最易懂)
一.SpringBoot全局异常
先讲下什么是全局异常处理器?
全局异常处理器就是把整个系统的异常统一自动处理,程序员可以做到不用写try... catch。SpringBoot内置有默认全局异常处理器。
Spring Boot对异常的处理有一套默认的机制,BasicErrorController处理默认异常转发的或这error请求 :当应用中产生异常时,当从浏览器地址栏中访问应用接口时,SpringBoot会获取请求头中数据,如果请求头中的accept包含text/html信息,产生异常时,Spring Boot会通过ModelAndView模型对象来装载异常信息,并以HTML的格式返回;反之,请求头中的accept不包含text/html时,Spring Boot则以JSON的格式返回异常信息。
例如:访问一个未知接口资源(或后台接口定义10/0的错误,响应的HTML结果如下)

例如:利用Postman测试工具,访问未知资源测试:(可以尝试使用其他插件工具:使用Chrome插件Restlet Client)

1.全局异常处理机制源码解析
BasicErrorController源码截取如下:
@RequestMapping("${server.error.path:${error.path:/error}}")请求的异常页面地址为/error/下面的资源
当没有自定义异常页面时,默认按下方源码执行构建HTML或JSON响应给前台。
1 @Controller
2 @RequestMapping("${server.error.path:${error.path:/error}}")
3 public class BasicErrorController extends AbstractErrorController {
4 /**
5 * 错误信息处理器方法errorHtml,设置了请求头Accpet值类型,如果包含text/html,即执行该方法
6 * @param request 请求对象
7 * @param response 响应对象
8 * @return
9 * MediaType.TEXT_HTML_VALUE的实际值就是一个字符串“text/html”
10 */
11 @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
12 public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
13 // 获取状态码
14 HttpStatus status = getStatus(request);
15 Map<String, Object> model = Collections
16 .unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
17 // 响应状态码描述
18 response.setStatus(status.value());
19 // 创建视图模型对象
20 ModelAndView modelAndView = resolveErrorView(request, response, status, model);
21 return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
22 }
23
24 /**
25 * 错误信息处理器方法error方法,设置了请求头Accpet值类型,即没有包含text/html执行该方法
26 * @param request 请求对象
27 * @param response 响应对象
28 */
29 @RequestMapping
30 public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
31 HttpStatus status = getStatus(request);
32 if (status == HttpStatus.NO_CONTENT) {
33 return new ResponseEntity<>(status);
34 }
35 Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
36 return new ResponseEntity<>(body, status);
37 }
38
39 }
我们可以自定义友好的异常页面。但必须是放在资源/error/目录下,资源目录存放默认地址可选
src/main/resources/static/,src/main/resources/resources/,src/main/resources/public/,src/main/templates/
说明:前三者是静态资源目录,页面我们使用模板引擎,因此如果需要自定义错误页面,那么需要放在src/main/templates/error目录下(当然所有的前提是,没有更改默认配置,SpringBoot默认加载其中的错误页面),且错误页面命名必须以状态码方式。SpringBoot默认错误视图解析器DefaultErrorViewResolver源码解析如下:
1 public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {
2
3 private static final Map<Series, String> SERIES_VIEWS;
4
5 // 静态初始化错误状态类型:4xx 或 5xx
6 static {
7 Map<Series, String> views = new EnumMap<>(Series.class);
8 views.put(Series.CLIENT_ERROR, "4xx");
9 views.put(Series.SERVER_ERROR, "5xx");
10 SERIES_VIEWS = Collections.unmodifiableMap(views);
11 }
12
13
14 // 解析错误视图
15 @Override
16 public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
17 // 获取错误状态码例如:404,转为字符串调用方法resolve(解析方法)
18 ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
19 if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
20 modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
21 }
22 return modelAndView;
23 }
24
25 // 解析处理方法
26 private ModelAndView resolve(String viewName, Map<String, Object> model) {
27 // 拼接错误视图访问前缀:error/500
28 String errorViewName = "error/" + viewName;
29 TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
30 this.applicationContext);
31 if (provider != null) {
32 return new ModelAndView(errorViewName, model);
33 }
34 // 调用解析资源:传入error/500
35 return resolveResource(errorViewName, model);
36 }
37
38 // 解析资源
39 private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
40 for (String location : this.resourceProperties.getStaticLocations()) {
41 try {
42 // 获取资解析
43 Resource resource = this.applicationContext.getResource(location);
44 // 创建解析文件为:error/500.html
45 resource = resource.createRelative(viewName + ".html");
46 if (resource.exists()) {
47 return new ModelAndView(new HtmlResourceView(resource), model);
48 }
49 }
50 catch (Exception ex) {
51 }
52 }
53 return null;
54 }
55 }
关于模板引擎的整合,参考第九集:整合JSP和模板引擎
2.自定义异常页面
1.在src/main/templates/error目录下新建错误页面:例如:404.html
2.测试访问。
1 <!DOCTYPE html>
2 <html xmlns:th="http://www.thymeleaf.org"> <!-- Thymeleaf模板约束 -->
3 <head>
4 <meta charset="UTF-8">
5 <title>Insert title here</title>
6 </head>
7 <body>
8 自定义404友好错误页面!<br>
9 对不起,你访问的数据被外星人盗窃了……
10 </body>
11 </html>

3.自定义异常信息
除了可以可以自定义友好异常页面(HTML)外,我们也可以自定义异常处理信息,改变默认的客户端访问接口产生的异常信息。
由于工作中都是前后端分离开发模式,所以几乎没有直接返回资源页的需求,一般上都是返回固定的响应格式JSON,如respCode、respMsg、data等,前端通过判断respCode的值进行业务判断,是弹窗还是跳转页面。
- 编写自定义异常类,封装异常信息(便于JSON转换)
1 @Data
2 @NoArgsConstructor
3 @AllArgsConstructor
4 public class ExceptionResponseResult{
5 @DateTimeFormat(pattern = "yyyy-MM-dd hh:mm:ss") // 日期格式化
6 private Date timestamp;// 时间
7 private int respCode;// 状态码
8 private String respMsg;// 给用户看的描述信息
9 private String message;// 实际错误异常信息
10 private String exceptionName;// 实际错误异常名字
11 private String path;// URI
12 private Object data;// 数据
13 } - 编写全局异常处理器
a,编写一个全局异常处理器类
b,在类上添加注解@ControllerAdvice
@ControllerAdvice:作用:对所有控制器中,被@RequestMapping注解标注的方法,进行增强(也可以直接使用@RestControllerAdvice)
c,自定义异常处理方法,并使用注解@ExceptionHandler(Throwable.class),@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR),@ResponseBody
@ExceptionHandler(Throwable.class):异常处理器注解,通常配合@ControllerAdvice注解使用。作用是:对指定或满足的异常类型实施拦截处理
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR):用于指定响应状态码的,HttpStatus是Spring内置的一个状态码枚举类,内定了详细的状态码及描。
@ResponseBody:作用:将响应的结果转为JSON信息。如果使用了@RestControllerAdvice则方法无需使用@ResponseBody注解
1 // @ControllerAdvice
2 @RestControllerAdvice // 控制器类增强:可以对Controller中所有使用@RequestMapping注解的方法增强
3 public class GlobalExceptionHandler {
4
5 // 该注解是异常处理器注解,可以对指定异常类型处理,执行注解标注的方法(只要发生指定异常都会被拦截)
6 @ExceptionHandler(Throwable.class)
7 // 该注解用于指定异常处理方法执行后响应页面的HTTP状态码,HttpStatus是Spring内置的一个状态码枚举类,内定了详细的状态码及描述,当前获取的是500
8 @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)// 响应500
9 public Object exceptonResponse(Exception ex,HttpServletRequest request) {
10 ExceptionResponseResult resultError = new ExceptionResponseResult();
11 resultError.setTimestamp(new Date());// 设置异常发生时间
12 resultError.setRespCode(0);// 可以选择自定义枚举类,定义状态码
13 resultError.setRespMsg("服务器刷新异常,请稍后。。。");// 用户看到的异常信息
14 resultError.setMessage(ex.getMessage());// 实际发生的异常信息
15 resultError.setExceptionName(ex.getClass().getName());// 实际异常的名字
16 resultError.setPath(request.getRequestURI());// 异常RUI
17 return resultError;
18 }
19
20 } - 编写控制器Controller定义后台错误
1 @Controller
2 public class HtmlController {
3
4 @RequestMapping("/indexHtml")
5 public String indexHtml(Model model) {
6 model.addAttribute("url","XSGE个人网站:http://www.xsge123.com");
7 System.out.println("测试"+(10/0));
8 return "indexHtml";
9 }
10 } - 使用Postman测试访问
页面访问测试结果:正常响应,但状态码是500

- 异常处理优化
在异常处理器方法中,判断异常类型,定义更加细节的异常响应内容。
1 // 该注解是异常处理器注解,可以对指定异常类型处理,执行注解标注的方法(只要发生指定异常都会被拦截)
2 @ExceptionHandler(Throwable.class)
3 // 该注解用于指定异常处理方法执行后响应页面的HTTP状态码,HttpStatus是Spring内置的一个状态码枚举类,内定了详细的状态码及描述,当前获取的是500
4 @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)// 响应500
5 public Object exceptonResponse(Exception ex,HttpServletRequest request) {
6 ExceptionResponseResult resultError = new ExceptionResponseResult();
7 if (ex instanceof NullPointerException) {// 如果捕获的异常为控空指针异常
8 // ****设置异常信息*****
9 } else if (ex instanceof ArithmeticException) {
10 // ****设置异常信息*****
11 }// *****
12 return resultError;
13 }
二.JSR-303数据校验
在任何时候,当你要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情。然后仅仅前端页面的校验就能保证安全了吗?小朋友还是年轻,基础的前端攻击技术网上很多,所以,仅仅页面数据校验是不够的。JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。
JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 。 Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint(约束) 的实现,除此之外还有一些附加的 constraint(约束)。(注意:此实现与 Hibernate ORM 没有任何关系)。
校验规则及使用方法:关注博主SSM整合之数据校验!!!(目前资料已备没时间写,敬请期待!)等不及的可以查阅Githup官网,查看说明文档。
SpringBoot第七集:异常处理与整合JSR303校验(2020最新最易懂)的更多相关文章
- SpringBoot第二集:注解与配置(2020最新最易懂)
2020最新SpringBoot第二集:基础注解/基础配置(2020最新最易懂) 一.Eclipse安装SpringBoot插件 Eclipse实现SpringBoot开发,为便于项目的快速构建,需要 ...
- SpringBoot第五集:整合监听器/过滤器和拦截器(2020最新最易懂)
SpringBoot第五集:整合监听器/过滤器和拦截器(2020最新最易懂) 在实际开发过程中,经常会碰见一些比如系统启动初始化信息.统计在线人数.在线用户数.过滤敏/高词汇.访问权限控制(URL级别 ...
- SpringBoot第九集:整合JSP和模板引擎Freemarker/Thymeleaf(2020最新最易懂)
SpringBoot第九集:整合JSP和模板引擎(2020最新最易懂) 当客户通过前端页面提交请求后,我们以前是怎么做的?后端接收请求数据,处理请求,把响应结果交给模板引擎JSP,最后将渲染后的JSP ...
- SpringBoot第四集:整合JdbcTemplate和JPA(2020最新最易懂)
SpringBoot第四集:整合JdbcTemplate和JPA(2020最新最易懂) 当前环境说明: Windows10_64 Maven3.x JDK1.8 MySQL5.6 SpringTool ...
- SpringBoot第五集:整合Druid和MyBatis(2020最新最易懂)
SpringBoot第五集:整合Druid和MyBatis(2020最新最易懂) 1.SpringBoot整合Druid Druid是阿里巴巴的一个开源项目,是一个数据库连接池的实现,结合了C3P0. ...
- SpringBoot第十一集:整合Swagger3.0与RESTful接口整合返回值(2020最新最易懂)
SpringBoot第十一集:整合Swagger3.0与RESTful接口整合返回值(2020最新最易懂) 一,整合Swagger3.0 随着Spring Boot.Spring Cloud等微服务的 ...
- SpringBoot第一集:入门(2020最新最易懂)
2020最新SpringBoot第一集:入门(2020最新最易懂) 学习思路: 是什么?为什么要学,有什么用?有什么特点?简单明了的总结一句话! SpringBoot推荐开发工具: Spring To ...
- SpringBoot第四集:静态资源与首页定(2020最新最易懂)
SpringBoot第四集:静态资源与首页定(2020最新最易懂) 问题 SpringBoot构建的项目结构如下:没有webapp目录,没有WEB-INF等目录,那么如果开发web项目,项目资源放在那 ...
- SpringBoot第十集:i18n与Webjars的应用(2020最新最易懂)
SpringBoot第十集:i18n与Webjars的应用(2020最新最易懂) 一,页面国际化 i18n(其来源是英文单词 internationalization的首末字符i和n,18为中间的字符 ...
随机推荐
- c#类(class)
类 类的定义是以关键字class开始的,后面跟类的名称,类的主题包含一个花括号里,下面是类定义的一般格式. <access specifier> class class_name { // ...
- 【数量技术宅|金融数据分析系列分享】为什么中证500(IC)是最适合长期做多的指数
更多精彩内容,欢迎关注公众号:数量技术宅.探讨数据分析.量化投资问题,请加技术宅微信:sljsz01 投资股票指数相比个股的优势 我们在投资股票的时候,如果持仓集中在一只或者有限几只股票上,恰好不幸遇 ...
- bash 在指定目录查找包含特定关键字的文件
比如我们要在目录/usr/local/nginx/conf/vhost/下查找baidu.com这个关键字的文件 方法1: find /usr/local/nginx/conf/vhost/ -exe ...
- 物联网wifi模块
物联网wifi模块 物联网wifi模块 是上海卓岚推出的MQTT+JSON转Modbus物联网WiFi核心模块.支持以MQTT的方式连接云端服务器,支持可以界面话配置,自主采集Modbus仪表/645 ...
- 飞翔---------双重线性dp
题目: 鹰最骄傲的就是翱翔,但是鹰们互相都很嫉妒别的鹰比自己飞的快,更嫉妒其他的鹰比自己飞行的有技巧.于是,他们决定举办一场比赛,比赛的地方将在一个迷宫之中. 这些鹰的起始点被设在一个N*M矩阵的左下 ...
- Java9系列第7篇:Java.util.Optional优化与增强
我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还是有很多的特性值得关注.期待您能关注我,我将把java 9 ...
- subprocess中命令为参数序列和字符串的区别
参数args 参数args可以是一个参数序列,也可以是一个单独的字符串.参数序列通常是首选的,因为它允许模块处理参数的转义和引号(例如,允许文件名中有空格). 如果传递参数序列,默认情况下,程序执行序 ...
- docker 启动mysql 挂载宿主机目录
在使用docker run 运行镜像获取容器时,有些容器会自动产生一些数据,为了这些数据会因为container (容器)的消失而消失,保证数据的安全,比如mysql 容器在运行中产生的一些表的数据, ...
- Python基础知识,新手入门看过来
1 下载和安装Python 在开始编程之前,你需要安装Python解析器软件(这里你可能需要找人帮忙).解析器是一个可以理解你用Python语言写的指令的程序.如果没有解析器,你的计算机不会理解这些指 ...
- 如何实现一个FormData
一.前言 最近项目中遇到一个问题,我们需要在cocos项目里去上传音频文件,而cocos原生环境和平时我们开发所在的浏览器环境和Node环境有很多差异,而cocos环境只提供了基础类,没有提供Form ...