声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅。

1、概念:

SpringBoot 错误处理

2、具体内容

在之前的程序里面如果一旦出现了错误之后就会出现一堆的大白板,这个白板会有一些错误信息(虽然这些错误信息你可能 看不懂,但是这些错误信息依然要告诉给用户)。在 SpringBoot 里面针对于错误的处理一共提供有三种方式:数据验证错误、错误 页指派以及全局异常的处理。

2.1、数据验证

现在假设说要进行表单信息提交,肯定需要有一个表单,而后这个表单要将数据提交到 VO 类中,所以现在的基本实现如下:

1、 建立一个 Member.java 的 VO 类:

package cn.study.microboot.vo;

import java.io.Serializable;
import java.util.Date; @SuppressWarnings("serial")
public class Member implements Serializable {
private String mid ;
private Integer age ;
private Double salary ;
private Date birthday ;
public String getMid() {
return mid;
}
public void setMid(String mid) {
this.mid = mid;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Member [mid=" + mid + ", age=" + age + ", salary=" + salary
+ ", birthday=" + birthday + "]";
}
}

2、 由于此时的程序之中需要进行日期的转换处理操作,那么就需要为其做一个转换处理的格式配置,修改 AbstractBaseController 类,追加如下的转换操作方法绑定:

@InitBinder
public void initBinder(WebDataBinder binder) { // 在本程序里面需要针对于日期格式进行处理
// 首先建立一个可以将字符串转换为日期的工具程序类
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
// 明确的描述此时需要注册一个日期格式的转化处理程序类
binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(sdf, true));
}

3、 建立一个 MemberController 程序类,负责实现 Member 的控制层处理操作。

package cn.study.microboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody; import cn.study.microboot.util.controller.AbstractBaseController;
import cn.study.microboot.vo.Member; @Controller
public class MemberController extends AbstractBaseController {
@RequestMapping(value = "/addPre", method = RequestMethod.GET)
public String addPre() { // 增加前的准备操作路径
return "member_add" ;
}
@RequestMapping(value = "/add", method = RequestMethod.POST)
@ResponseBody
public Object add(Member vo) { // 增加前的准备操作路径
return vo ;
}
}

4、 编写一个页面进行用户的表单填写(在 src/main/view/templates 下建立):member_add.html;

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>SpringBoot模版渲染</title>
<link rel="icon" type="image/x-icon" href="/images/study.ico"/>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
</head>
<body>
<form action="add" method="post">
用户邮箱:<input type="text" name="mid" value="studyjava@163.com"/><br/>
用户年龄:<input type="text" name="age" value="18"/><br/>
用户工资:<input type="text" name="salary" value="1000"/><br/>
用户生日:<input type="text" name="birthday" value="2010-10-10"/><br/>
<input type="submit" value="提交"/>
<input type="reset" value="重置"/>
</form>
</body>
</html>

5、 此时的代码只是一个最为普通的处理操作,但是这个时候对于该程序也是存在有如下问题的:

· 如果某些数据没有输入,则内容是 null,如果要进行严格控制,这些 null 不应该存在;

· 某些数据需要进行格式验证,例如:用户名应该是邮箱,这个的信息应该进行邮箱验证;

所以现在如果要想进行这些的验证,SpringBoot 里面有默认的支持,只不过这种支持未必是最好的,在 SpringBoot 里面为了 方便用户编写验证专门提供有一个 hibernate-validation.jar 工具包,这个工具包是由 hibernate 开发框架提供的。

6、 如果要想进行验证,那么首先要解决的问题就必须是错误的提示信息问题,而在 SpringBoot 里面对于错误信息的保存,都要 求其保存在 ValidationMessages.properties 文件,在“src/main/resources”目录中建立此文件;

member.mid.notnull.error=用户名不允许为空!
member.mid.email.error=用户名的注册必须输入正确的邮箱!
member.mid.length.error=用户名的格式错误!
member.age.notnull.error=年龄不允许为空
member.age.digits.error=年龄必须是合法数字!
member.salary.notnull.error=工资不允许为空!
member.salary.digits.error=工资必须是合法数字!
member.birthday.notnull.error=生日不允许为空!

 提示:你一个表单就需要编写这么多的配置项,那么如果要有几百个表单呢?这样的配置项太可怕了,所以最好的数据检测还是利 用拦截器处理最合适。

7、 修改 Member.java 程序类追加验证的处理方式:

package cn.study.microboot.vo;

import java.io.Serializable;
import java.util.Date; import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull; import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length; @SuppressWarnings("serial")
public class Member implements Serializable {
@NotNull(message="{member.mid.notnull.error}")
@Email(message="{member.mid.email.error}")
@Length(min=6,message="{member.mid.length.error}")
private String mid ;
@NotNull(message="{member.age.notnull.error}")
@Digits(integer=3,fraction=0,message="{member.age.digits.error}")
private Integer age ;
@NotNull(message="{member.salary.notnull.error}")
@Digits(integer=20,fraction=2,message="{member.salary.digits.error}")
private Double salary ;
@NotNull(message="{member.birthday.notnull.error}")
private Date birthday ;
public String getMid() {
return mid;
}
public void setMid(String mid) {
this.mid = mid;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Member [mid=" + mid + ", age=" + age + ", salary=" + salary
+ ", birthday=" + birthday + "]";
}
}

8、 修改 MemberController 类中的 add()方法来观察错误信息的显示:

package cn.study.microboot.controller;

import java.util.Iterator;

import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody; import cn.study.microboot.util.controller.AbstractBaseController;
import cn.study.microboot.vo.Member; @Controller
public class MemberController extends AbstractBaseController {
@RequestMapping(value = "/add", method = RequestMethod.POST)
@ResponseBody
public Object add(@Valid Member vo, BindingResult result) { // 增加前的准备操作路径
if (result.hasErrors()) { // 现在表示执行的验证出现错误
Iterator<ObjectError> iterator = result.getAllErrors().iterator(); // 获取全部错误信息
while (iterator.hasNext()) {
ObjectError error = iterator.next() ; // 取出每一个错误
System.out.println("【错误信息】code = " + error.getCode() + ",message = " + error.getDefaultMessage());
}
return result.getAllErrors() ;
} else {
return vo;
}
}
@RequestMapping(value = "/addPre", method = RequestMethod.GET)
public String addPre() { // 增加前的准备操作路径
return "member_add";
}
}

对于此类的验证大家理解即可,不需要将其作为重点,但是需要清楚,默认情况下 SpringBoot 提供的数据验证需要通过注解 以及一系列的资源文件进行定义后才可以使用,而且所有的错误都必须用户自己来处理,这一点的设计不如直接编写具体的反射拦 截器方便。

2.2、处理错误页

错误页绝对是所有的 WEB 项目之中必须具有的一项信息显示处理,但是在传统的 WEB 项目开发过程之中,错误页都是在 web.xml 文件之中进行配置的,不过遗憾的是 SpringBoot 之中并不存在有 web.xml 配置文件这一项,那么如果要想进行错误页的处 理,最好的做法是需要根据每一个错误代码创建一个属于自己的错误显示页。

1、 所有的错误页都是普通的静态文件,那么建议在“src/main/view/static”目录下创建几个常见的错误页(常见的错误的 HTTP 返回编码:404、500、400)

2、 添加一个错误页的配置类,在启动类中编写一个错误页的配置项;

package cn.study.microboot.config;

import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.servlet.ErrorPage;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus; @Configuration
public class ErrorPageConfig {
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(
ConfigurableEmbeddedServletContainer container) {
ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST,
"/error-400.html");
ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND,
"/error-404.html");
ErrorPage errorPage500 = new ErrorPage(
HttpStatus.INTERNAL_SERVER_ERROR, "/error-500.html");
container.addErrorPages(errorPage400, errorPage404,
errorPage500);
}
};
}
}

那么此时只要出现了错误,就会找到相应的 http 状态码,而后跳转到指定的错误路径上进行显示。

 2.3、全局异常

下面首先来观察一个程序代码,例如:现在建立一个控制器,而后这个控制器自己抛出一个异常。

@RequestMapping(value="/get")
@ResponseBody
public String get() {
System.out.println("除法计算:" + (10 / 0));
return "hello world" ;
}

如果此时配置有错误页,那么这个时候错误会统一跳转到 500 所在的路径上进行错误的显示,但是如果说现在希望能够显示 出错误更加详细的内容呢?

所以这个时候可以单独定义一个页面进行错误的信息显示处理,而这个页面,可以定义在“src/main/view/templates/error.html”, 这个页面里面要求可以输出一些信息;

1、 定义一个全局的异常处理类:

import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView; @ControllerAdvice // 作为一个控制层的切面处理
public class GlobalExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error"; // 定义错误显示页,error.html @ExceptionHandler(Exception.class) // 所有的异常都是Exception子类
public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) { // 出现异常之后会跳转到此方法
ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW); // 设置跳转路径
mav.addObject("exception", e); // 将异常对象传递过去
mav.addObject("url", request.getRequestURL()); // 获得请求的路径
return mav;
}
}

2、 定义 error.html 页面:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>SpringBoot模版渲染</title>
<link rel="icon" type="image/x-icon" href="/images/study.ico"/>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
</head>
<body>
<p th:text="${url}"/>
<p th:text="${exception.message}"/>
</body>
</html>

对于全局异常信息显示除了采用以上的跳转处理方式之外,也可以做的简单一些,使用 Rest 进行显示。

范例:修改全局异常处理类

package cn.study.microboot.advice;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; //@ControllerAdvice// 作为一个控制层的切面处理
@RestControllerAdvice
public class GlobalExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error"; // 定义错误显示页,error.html
@ExceptionHandler(Exception.class) // 所有的异常都是Exception子类
public Object defaultErrorHandler(HttpServletRequest request,Exception e) {
class ErrorInfo {
private Integer code ;
private String message ;
private String url ;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
ErrorInfo info = new ErrorInfo() ;
info.setCode(100); // 标记一个错误信息类型
info.setMessage(e.getMessage());
info.setUrl(request.getRequestURL().toString());
return info ;
}
// public ModelAndView defaultErrorHandler(HttpServletRequest request,
// Exception e) { // 出现异常之后会跳转到此方法
// ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW); // 设置跳转路径
// mav.addObject("exception", e); // 将异常对象传递过去
// mav.addObject("url", request.getRequestURL()); // 获得请求的路径
// return mav;
// }
}

如果现在要想把异常的信息显示的更加华丽一些(不是面对所有用户),那么最好的做法就是使用全局异常处理的方式完成。

SpringBoot系列五:SpringBoot错误处理(数据验证、处理错误页、全局异常)的更多相关文章

  1. SpringBoot系列教程之Bean加载顺序之错误使用姿势辟谣

    在网上查询 Bean 的加载顺序时,看到了大量的文章中使用@Order注解的方式来控制 bean 的加载顺序,不知道写这些的博文的同学自己有没有实际的验证过,本文希望通过指出这些错误的使用姿势,让观文 ...

  2. SpringMVC学习系列(6) 之 数据验证

    在系列(4).(5)中我们展示了如何绑定数据,绑定完数据之后如何确保我们得到的数据的正确性?这就是我们本篇要说的内容 —> 数据验证. 这里我们采用Hibernate-validator来进行验 ...

  3. [oldboy-django][2深入django]form表单clean_xx, clean完成数据验证+ form错误信息

    form后台生成form里面的Input标签,以及设置Input的属性 # 需求 后台生成form里面的input标签,并设置input标签的属性, class RegisterForm(Form): ...

  4. springboot系列五、springboot常用注解使用说明

    一.controller相关注解 1.@Controller 控制器,处理http请求. 2.@RespController Spring4之后新加的注解,原来返回json需要@ResponseBod ...

  5. springboot系列五:springboot整合mybatisplus jsp

    一.用IDEA创建项目 1.添加pom.xml <?xml version="1.0" encoding="UTF-8"?> <project ...

  6. Springboot系列:Springboot与Thymeleaf模板引擎整合基础教程(附源码)

    前言 由于在开发My Blog项目时使用了大量的技术整合,针对于部分框架的使用和整合的流程没有做详细的介绍和记录,导致有些朋友用起来有些吃力,因此打算在接下来的时间里做一些基础整合的介绍,当然,可能也 ...

  7. SPRING-BOOT系列之SpringBoot快速入门

    今天 , 正式来介绍SpringBoot快速入门 : 可以去如类似 https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/refer ...

  8. SPRING-BOOT系列之SpringBoot的诞生及其和微服务的关系

    转载自 : https://www.cnblogs.com/ityouknow/p/9034377.html 微服务架构 微服务的诞生并非偶然,它是在互联网高速发展,技术日新月异的变化以及传统架构无法 ...

  9. SpringBoot学习(五)-->SpringBoot的核心

    SpringBoot的核心 1.入口类和@SpringBootApplication Spring Boot的项目一般都会有*Application的入口类,入口类中会有main方法,这是一个标准的J ...

随机推荐

  1. 每天一个linux命令(5):in命令

    ln是linux中又一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接.当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在 ...

  2. Mathematica .nb程序运行不下去的原因

    Mathematica是个不错的工具,尤其是其支持交互式参数调整的plot功能,灰常实用.但一直有个烦人的carveat,这里提一下. 在evaluate notebook(.nb)时,一旦碰到了使用 ...

  3. Spark(Hive) SQL中UDF的使用(Python)【转】

    相对于使用MapReduce或者Spark Application的方式进行数据分析,使用Hive SQL或Spark SQL能为我们省去不少的代码工作量,而Hive SQL或Spark SQL本身内 ...

  4. Lintcode:Longest Common Subsequence 解题报告

    Longest Common Subsequence 原题链接:http://lintcode.com/zh-cn/problem/longest-common-subsequence/ Given ...

  5. 本质矩阵E求解及运动状态恢复

    为了获取本质矩阵,首先计算基础矩阵F.根据本质矩阵E,即可恢复得到运动的状态R和T. 由可以根据匹配点得到F,然后根据和相机内参,即可得到本质矩阵E.进而根据: 注意:根据摄像机模型t=-RT,恢复运 ...

  6. 基于jQuery滑动分步式进度导航条代码

    分享一款基于jQuery滑动分步式进度导航条代码.这是一款基于jquery实现的网站注册动态步骤导航条代码.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div id=& ...

  7. java基础篇---枚举详解

    在JDK1.5之前,JAVA可以有两种方式定义新类型:类和接口,对于大部分面向对象编程,有这两种似乎就足够了,但是在一些特殊情况就不合适.例如:想要定义一个Color类,它只能有Red,Green,B ...

  8. [转]基于BootStrap 的城市三级联动

    原文地址:https://blog.csdn.net/peiyuanxin/article/details/51992384 HTML代码部分    <div class="form- ...

  9. SpringBoot使用AutoConfiguration自定义Starter

    https://segmentfault.com/a/1190000011433487

  10. Thinkphp在Tpl中调用common.php中的公共函数

    Common/common.php //产生随机验证码 function random($length, $chars = '0123456789') { //随机生成的逻辑 return $hash ...