1. 表单数据验证
1.1 概述
表单数据验证主要是为了对表单中的数据项实现一个有效性的验证,防止因为某个无效参数可能导致系统产生一系列异常。
SpringBoot中采用的表单验证是使用了Hibernate-validate校验框架来实现的。
1.2 表单数据验证的实现
使用注解来引入,在需要校验的表单相关属性的持久层的对象上面使用注解标签。
public class Users {
@NotBlank //非空校验
private String name;
@NotBlank //密码非空校验
private String password;
private Integer age;
// 省略了get/set方法
}
为使得校验起作用,需要在controller层中,对于controller中传递的对象,需要加上相应标签,告诉系统,此参数的数据需要校验,在表单中校验的时候,如果此参数不符合条件,从此参数校验结果中获取到相应的响应信息然后返回给视图页面显示。
BindingResult封装了校验的结果
/**
* 完成用户添加
*@Valid 开启对 Users 对象的数据校验
*BindingResult:封装了校验的结果
*/
@RequestMapping("/save")
public String saveUser(@Valid Users users,BindingResult result){
if(result.hasErrors()){
return "add";
}
System.out.println(users);
return "ok";
}
视图页面显示出来的消息,应该在相应的输入框后面添加上校验失败信息输出的位置。
<form th:action="@{/save}" method="post">
用户姓名:<input type="text" name="name"/><font color="red"
th:errors="${users.name}"></font><br/>
用户密码:<input type="password" name="password" /><font
color="red" th:errors="${users.password}"></font><br/>
用户年龄:<input type="text" name="age" /><font color="red"
th:errors="${users.age}"></font><br/>
<input type="submit" value="OK"/>
</form>
这里需要注意,在controller中我们定义的校验对象参数名为users
,在页面校验的时候必须要是用相同名字的变量,否则参数是不能够获取到相应的校验失败的信息。
而且在校验的时候,因为上述页面是保存页面,肯定存在一个跳转到此页面的url
,在此url的controller中,我们需要提前获取到此对象,否则在我们校验失败的时候,跳转回原始页面的时候,由于原始页面没有users
对象,此时视图端获取信息,会报异常,因为没有对象绑定。所以,我们需要在跳转前的页面注入一个Users
对象。
@RequestMapping("/addUser")
public String showPage( Users users){
return "add";
}
1.3 表单验证参数名称的更改
如果我们需要对参数的名称做修改,可以使用@ModelAttribute(“需要修改的名称”)来实现校验参数的名称的更改,但是需要注意,当我们修改了此校验参数名的时候,在视图层,我们获取校验信息的时候,相应的参数名称也需要做改变。否则运行时将会报错。
@RequestMapping("/save")
public String saveUser(@ModelAttribute("abc") Users users,BindingResult result){
if(result.hasErrors()){
return "add";
}
System.out.println(users);
return "ok";
}
// ----------------------------------------------------------------
@RequestMapping("/addUser")
public String showPage(@ModelAttribute("abc") Users users){
return "add";
}
1.4 表单验证的相关标签
相应的校验规则如下:
@NotBlank: 判断字符串是否为Null或者是空串,去除前后空格的前提
@NotEmpty: 判断字符串是否为Null或者是空串,不去除空格,在此校验中,空格也是合法的
@Length: 判断字符的长度(最大或者最下)
@Min: 判断数值最小值
@Max: 判断数值最大值
@Email: 验证邮箱是否合法
2. SpringBoot的异常处理与单元测试
2.1 SpringBoot异常处理的方式
SpringBoot对于异常的处理提供了五种处理异常的方法。
2.1.1 自定义错误界面
SpringBoot对于错误产生之后会默认有一个跳转界面,当程序遇到异常的时候,SpringBoot中的BasicExceptionController会来处理/error的请求,然后跳转到默认的错误显示界面。
当需要修改错误显示界面的时候,需要在templates
文件下新建一个error.html
,注意名称必须为error
,否则SpringBoot将会按照默认的跳转。
<body>
程序运行出错,请稍后重试.....
<span th:text="${exception}"></span>
</body>
2.1.2 使用@ExceptionHandler注解处理异常
因为上面第一种处理异常的方式对于所有的异常跳转的都是这个页面,所以不益于区分错误的类型与相关页面的显示。为了使得相应的错误对应相应的页面。
可以使用@ExceptionHandler注解来实现不同异常的处理,指定跳转不同的显示页面。
对于此种处理异常的方式需要对于异常信息进行一个信息的封装,以便在显示页面可以显示出此错误中封装的错误信息。
@ExceptionHandler(value={相应错误的点class})
可以支持多个。要想实现多个错误对应的多个页面的跳转,需要编写多个此类的程序,以此来对应错误的界面的跳转。
/**
* java.lang.ArithmeticException
* 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视图的指定
* 参数 Exception e:会将产生异常对象注入到方法中
*/
@ExceptionHandler(value={java.lang.ArithmeticException.class})
public ModelAndView arithmeticExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error1");
return mv;
}
2.1.3 使用@ControllerAdvice+@ExceptionHandler注解处理异常
由于单独使用@ExceptionHandler注解会使得程序异常处理的代码成倍的增加,为了解决这个问题,可以采取@ControllerAdvice配合@ExceptionHandler来实现异常处理。
具体的实现:
创建一个全局的全局的异常处理类,然后再此类上添加注解标签@ControllerAdvice
,随后对于所有的异常都会直接跳转到此类进行相关异常的验证,然后跳转到不同的页面。
其实就是讲第二种方法拆分开来处理异常。
/*
全局异常处理类
*
*/
@ControllerAdvice
public class GlobalException {
/**
* java.lang.ArithmeticException
* 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视
图的指定
* 参数 Exception e:会将产生异常对象注入到方法中
*/
@ExceptionHandler(value={java.lang.ArithmeticException.class})
public ModelAndView arithmeticExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error1");
return mv;
}
/**
* java.lang.NullPointerException
* 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视
图的指定
* 参数 Exception e:会将产生异常对象注入到方法中
*/
@ExceptionHandler(value={java.lang.NullPointerException.class})
public ModelAndView nullPointerExceptionHandler(Exception e){
ModelAndView mv = new ModelAndView();
mv.addObject("error", e.toString());
mv.setViewName("error2");
return mv;
}
}
2.1.4 配置SimpleMappingExceptionResolver处理异常
建立一个全局异常处理类,在类上配置标签@Configuration
,这是一个配置类注解,SpringBoot在运行会提前对含有此标签的类做一个处理。在此类中编写异常处理方法,此方法必须含有返回值,且返回值类型必须为SimpleMappingExceptionResolver
/**
* 通过 SimpleMappingExceptionResolver 做全局异常处理
*
*
*/
@Configuration
public class GlobalException {
/**
* 该方法必须要有返回值。返回值类型必须是:
SimpleMappingExceptionResolver
*/
@Bean
public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
/**
* 参数一:异常的类型,注意必须是异常类型的全名
* 参数二:视图名称
*/
mappings.put("java.lang.ArithmeticException", "error1");
mappings.put("java.lang.NullPointerException","error2");
//设置异常与视图映射信息的
resolver.setExceptionMappings(mappings);
return resolver;
}
}
注意,这种异常处理方式虽然可以完成异常的处理,但是对于异常的原因,也就是错误信息,没有办法传递到页面显示,所以还是存在弊端的。
2.1.5 自定义HandlerExceptionResolver类处理异常
编写此类时需要实现HandlerExceptionResolver
接口,重写接口中的方法,实现异常的处理。
/**
* 通过实现 HandlerExceptionResolver 接口做全局异常处理
*/
@Configuration
public class GlobalException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex) {
ModelAndView mv = new ModelAndView();
//判断不同异常类型,做不同视图跳转
if(ex instanceof ArithmeticException){
mv.setViewName("error1");
}
if(ex instanceof NullPointerException){
mv.setViewName("error2");
}
mv.addObject("error", ex.toString());
return mv;
}
}
2.2 SpringBoot的单元测试
SpringBoot的单元测试与Spring的单元测试几乎一样,只是在处理单元测试类的时候,所使用的整合标签有所区别。
/**
* SpringBoot 测试类
*@RunWith:启动器
*SpringJUnit4ClassRunner.class:让 junit 与 spring 环境进行整合
*
*@SpringBootTest(classes={App.class}) 1,当前类为 springBoot 的测试类
*@SpringBootTest(classes={App.class}) 2,加载 SpringBoot 启动类。启动
springBoot
*
*junit 与 spring 整合
@Contextconfiguartion("classpath:applicationContext.xml")
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes={App.class})
public class UserServiceTest {
@Autowired
private UserServiceImpl userServiceImpl;
@Test
public void testAddUser(){
this.userServiceImpl.addUser();
}
}