前言
在上一章节的内容中,壹哥 给大家讲解了@InitBinder的原理及使用教程,我们利用InitBinder实现了对日期类型参数的格式转换。
本章节我会利用另一种技术WebDataBinder,继续处理SpringBoot中的参数传递及数据绑定,并在本文中带大家实现自定义的属性编辑器,以及自定义参数校验器。
一. WebDataBinder简介
在进行代码实现之前,我们先来看看WebDataBinder是个什么东东!
1. WebDataBinder的由来
在Servlet中,有一个方法:request.getParameter("paramName")
,它会根据key返回一个String类型的数据,从而获取到前端传递过来的请求参数。
但是如果我们这样一个一个地去取出Web请求中的所有参数,就会很麻烦。我们知道Java中有对象的概念,那有没有办法将request中的请求参数都自动封装到一个Java对象中呢?为了解决这个问题,SpringMVC中就引入了WebDataBinder的概念。
2. WebDataBinder类关系
我们再来看看WebDataBinder的类关系图:
3. WebDataBinder的作用
WebDataBinder的作用是从Web 请求中,把请求里的参数
都绑定到对应的JavaBean
上!
在Controller方法中的参数类型可以是基本类型,也可以是封装后的普通Java类型。若这个普通的Java类型没有声明任何注解,则意味着它的每一个属性都需要到Request中去查找对应的请求参数,而WebDataBinder则可以帮助我们实现从Request中取出请求参数并绑定到JavaBean中。
4. WebDataBinder工作机制
WebDataBinder用于绑定请求参数到指定的属性编辑器里。由于前台传到Controller里的值是String类型,当往Java Bean里设置这个值的时候,如果设置的这个属性是对象,SpringMVC就会去找到对应的编辑器editor进行转换,然后再设置进去!
SpringMVC自己提供了大量的editor编辑器实现类,诸如CustomDateEditor,CustomBooleanEditor,CustomNumberEditor等,基本上够我们使用了。
但在平时使用SpringMVC时,可能会碰到Java Bean中有Date类型的参数,表单中传来代表日期的字符串转化为日期类型,SpringMVC在默认时,是不支持这种类型转换的。此时我们就需要手动设置时间格式并在WebDateBinder上注册这个编辑器!
5. 示例代码
利用CustomDateEditor实现日期格式转换的示例代码,如下所示:
publicvoidbindingPreparation(WebDataBinderbinder) { DateFormatdateFormat1=newSimpleDateFormat("dd-MM-yyyy"); CustomDateEditororderDateEditor=newCustomDateEditor(dateFormat1, true); DateFormatdateFormat2=newSimpleDateFormat("MMM d, YYYY"); CustomDateEditorshipDateEditor=newCustomDateEditor(dateFormat2, true); binder.registerCustomEditor(Date.class, "orderDate", orderDateEditor); binder.registerCustomEditor(Date.class, "shipDate", shipDateEditor); }
二. WebDataBinder实现过程
了解了上面的这些理论内容,接下来我就利用WebDataBinder实现参数的转换。
1. 创建Web项目
首先我们要创建一个Web项目,或者也可以直接在上一个项目的基础之上编写本案例代码,具体请参考之前的章节。
2. 创建User类
我们在项目中创建一个domain包,在该包下创建一个User实体类,里面有个日期类型的birthday字段。
packagecom.yyg.boot.domain; importlombok.Data; importjava.util.Date; /*** @Description Description* @Author 一一哥Sun* @Date Created in 2020/3/23*/publicclassUser { privateStringname; privateStringpassword; privateStringemail; privateDatebirthday; }
3. Validator接口介绍
在使用Validator之前,我先介绍一下Validator接口。
- Validator 是一个有着两个方法的接口;
- boolean supports(Class<?> clazz) : 检验参数是否验证成功的实例类;
- void validate(Object target, Errors errors) : 如果 supports() 方法返回真, target object 合法. Errors.rejectValue() 方法用一个字段名注册错误信息。
4. 创建UserValidator验证类
我们创建一个validator包,在该包下面创建一个UserValidator验证类,在这里负责对前端传递过来的User类型参数进行必要的校验,判断用户名和密码是否符合规范。
packagecom.yyg.boot.validator; importcom.yyg.boot.domain.User; importorg.springframework.stereotype.Component; importorg.springframework.validation.Errors; importorg.springframework.validation.ValidationUtils; importorg.springframework.validation.Validator; /*** @Description Description* @Author 一一哥Sun* @Date Created in 2020/3/23*/publicclassUserValidatorimplementsValidator { /*** 检验参数是否验证成功的实例类*/publicbooleansupports(Class<?>clazz) { //isAssignableFrom()方法是判断是否为某个类的父类,instanceof关键字是判断是否某个类的子类。returnUser.class.isAssignableFrom(clazz); } /*** 如果 supports() 方法返回真, target object 合法. Errors.rejectValue() 方法会用一个字段名注册错误信息;*/publicvoidvalidate(Objecttarget, Errorserrors) { Useruser= (User)target; //配置字段验证信息ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "","Username is empty"); ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "", "Password is empty"); //用户名长度不能小于5个字符if (user.getName().length()<5) { errors.rejectValue("name","", "Username length is less than 5"); } } }
5. 创建EmailValidator验证类
我们在validator包中,再创建一个EmailValidator验证类,判断用户的邮箱信息是否符合要求。
packagecom.yyg.boot.validator; importcom.yyg.boot.domain.User; importorg.springframework.stereotype.Component; importorg.springframework.validation.Errors; importorg.springframework.validation.ValidationUtils; importorg.springframework.validation.Validator; /*** @Description Description* @Author 一一哥Sun* @Date Created in 2020/3/23*/publicclassEmailValidatorimplementsValidator { publicbooleansupports(Class<?>clazz) { returnUser.class.isAssignableFrom(clazz); } publicvoidvalidate(Objecttarget, Errorserrors) { Useruser= (User)target; ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "","Email is empty"); if (!user.getEmail().contains("@")) { errors.rejectValue("email","", "Email is not valid."); } } }
6. 创建ValidatorController测试接口
接下来我们创建一个web包,在该包下面创建一个ValidatorController类,并且编写一个接口,接受前端传递过来的User参数。而且该类中还要实现InitBinder的参数格式化校验。
packagecom.yyg.boot.web; importcom.yyg.boot.domain.User; importcom.yyg.boot.validator.EmailValidator; importcom.yyg.boot.validator.UserValidator; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.beans.propertyeditors.CustomDateEditor; importorg.springframework.stereotype.Controller; importorg.springframework.validation.BindingResult; importorg.springframework.web.bind.WebDataBinder; importorg.springframework.web.bind.annotation.*; importjavax.validation.Valid; importjava.text.SimpleDateFormat; importjava.util.Date; /*** @Description Description* @Author 一一哥Sun* @Date Created in 2020/3/23*/publicclassValidatorController { privateUserValidatoruserValidator; privateEmailValidatoremailValidator; /*** 接收参数,自定义编辑器*/publicvoiddataBinding(WebDataBinderbinder) { //将UserValidator和EmailValidator添加到WebDataBinder中binder.addValidators(userValidator, emailValidator); SimpleDateFormatdateFormat=newSimpleDateFormat("yyyy-MM-dd"); //setLenient用于设置Calendar是否宽松解析字符串,如果为false,则严格解析;默认为true,宽松解析dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, "birthday", newCustomDateEditor(dateFormat, true)); } value="/user", method=RequestMethod.POST) (publicStringcreateUser(Useruser, BindingResultresult) { if (result.hasErrors()) { returnresult.getFieldError().getDefaultMessage(); } System.out.println("Name:"+user.getName()); System.out.println("Email:"+user.getEmail()); System.out.println("Date of Birth:"+user.getBirthday()); return"success"; } }
7. 项目结构
完整的项目结构如下图所示,各位可以参考创建。
8. 启动程序进行测试
最后我们启动项目,在Postman中进行测试,输入地址:
http://localhost:8080/user,
8.1 验证用户名长度
这里我们要使用post请求,传递一个json格式的参数:
{ "name":"一一哥", "password":"123", "email":"sss@33", "birthday": "2020-03-20"}
执行完毕后可以看到返回的验证数据:
8.2 验证邮箱格式
当传入的参数中,email不合格时,有如下返回值。
至此,我们就实现了数据绑定和自定义的属性编辑器,以及自定义的参数校验器效果了!
结语
利用今天的内容,我们就可以对前端传递过来的参数进行统一的合法性校验了,这样就避免了很多非法请求。你学会了吗?
今日小作业:
思考今天的效果,还能不能进一步改进?