springMvc-07 类型转换/格式化/数据校验
前端给后端传递数据进行数据绑定时,会经过类型转换,数据格式化,以及数据校验的过程。
1、类型转换
SpringMVC框架提供了一个通用的类型转换模块,该类型转换模块位于包org.springframework.core.convert中,可以在处理方法的参数绑定中使用他们进行数据转换。
SpringMVC框架中内置了很多的类型转换。
3)自定义类型转换
默认类型转换器并不是可以将用户提交的内容,转换为用户需要的所有类型。此时 ,就需要自定义类型转换器了
例如:我们文本框里边要输入2021-01-01之后要让其转换为日期类型,则需要自定义类型转换器。
自定义类型转换器的方式有3三种:
1)Converter<S,T>:是SpringMVC最简单的一个转换器接口,可以实现任意类型间的相互转换,负责把S转为T
2)ConverterFactory<S,R>:将一种类型的对象转换成另一个类型及其子类型对象。其中S为转换的源类型,R为目标类型的基类,T为R的子类。
3)GenericConverter,转换方法参数中包含TypeDescriptor类型的参数,这个参数包含了上下文信息,能够根据上下文信息进行类型转换。
举例:
1)转换器实现
package rui.tool; import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class StringToDateConverter implements Converter<String,Date> { //日期的格式,定义一个变量,允许配置Bean的时候动态的指定 private String datePattern; public String getDatePattern() { return datePattern; } public void setDatePattern(String datePattern) { this.datePattern = datePattern; } @Override public Date convert(String s) { if(s==null || "".equals(s.trim())) { return null; } SimpleDateFormat sdf = new SimpleDateFormat(datePattern); Date result = null; try { result = sdf.parse(s); } catch (ParseException e) { e.printStackTrace(); System.out.println("转换失败"); } return result; } }
2)SpringMVC配置
<!--开启自动注册处理器映射器和处理器适配器--> <!--开启Spring的valid功能 validator="validator" --> <mvc:annotation-driven validator="validator" conversion-service="formatService" /> <!--配置类型转换和格式化--> <bean id="formatService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <list> <bean class="rui.tool.StringToDateConverter"> <property name="datePattern" value="yyyy-MM-dd" /> </bean> </list> </property> </bean>
2、数据格式化
从Spring3.0开始,引入了格式化转换框架,该框架位于Spring-context.jar包中。
Spring在格式化模块中定义了一个实现ConversionService接口的FormattingConversionService实现类,该类既具有类型转换功能,有具有格式化功能。
Formatter和Converter基本功能一样, 是将一种类型转换成另一种类型, 但是,Formatter的源类型必须是一个String, 目标类型是java类型.
通过FormattingConversionServiceFactoryBean即可以实现注册定义的转换器,也可以注册自定义的格式化器。
Spring内置了很多的格式化转换器,也允许我们自定义,例如定义了一个坐标类Point,内部包含x,y属性,如果表单上输入2,3之后,能够将其转换为Point对象,则需要自己编写数据格式化器。
1) 内置的格式化
名称 | 功能 |
NumberFormatter | 实现Number与String之间的解析与格式化 |
CurrencyFormatter | 实现Number与String之间的解析与格式化(带货币符号) |
PercentFormatter | 实现Number与String之间的解析与格式化(带百分数符号) |
DateFormatter | 实现Date与String之间的解析与格式化 |
NumberFormatAnnotationFormatterFactory | @NumberFormat注解,实现Number与String之间的解析与格式化,可以通过指定style来指示要转换的格式(Style.Number/Style.Currency/Style.Percent),当然也可以指定pattern(如pattern=“#.##”(保留2位小数) ),这样pattern指定的格式会覆盖掉Style指定的格式 |
JodaDateTimeFormatAnnotationFormatterFactory | @DateTimeFormat注解,实现日期类型与String之间的解析与格式化这里的日期类型包括Date、Calendar、Long以及Joda的日期类型。必须在项目中添加Joda-Time包 |
2)自定义格式化
举例:
1)格式化器实现
package rui.tool; import org.springframework.format.Formatter; import rui.db.Model.Point; import java.text.ParseException; import java.util.Locale; public class StringToPointFormatter implements Formatter<Point> { @Override public Point parse(String s, Locale locale) throws ParseException { if(s==null || "".equals(s.trim())) { return null; } String[] array = s.split(","); if(array.length==2) { Point p = new Point(); p.setX(Integer.parseInt(array[0])); p.setY(Integer.parseInt(array[1])); return p; } return null; } @Override public String print(Point point, Locale locale) { if(point!= null) { return point.getX()+","+point.getY(); } return ""; } }
2)SpringMvc配置
类型转换和格式化可以放在一个Bean中配置。
<!--开启自动注册处理器映射器和处理器适配器--> <!--开启Spring的valid功能 validator="validator" --> <mvc:annotation-driven validator="validator" conversion-service="formatService" /> <!--配置类型转换和格式化--> <bean id="formatService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="formatters"> <list> <bean class="rui.tool.StringToPointFormatter" /> </list> </property> </bean>
3)基于注解的格式化
@DateTimeFormat可以对Date和Calendar等时间类型的属性进行标准。
DateTimeFormat.ISO.DATE
: 格式yyyy-MM-dd
。DateTimeFormat.ISO.DATE_TIME
: 格式yyyy-MM-dd HH:mm:ss .SSSZ
。DateTimeFormat.ISO.TIME
: 格式HH:mm:ss .SSSZ
。DateTimeFormat.ISO.NONE
: 表示不使用ISO格式的时间。
该标准拥有两个属性:
- pattern。类型为String,使用自定义的时间格式化字符串。
- style。类型为String,通过样式指定日期时间的格式,由两位字符组成,第1位表示日期的样式,第2位表示时间的格式:
- S: 短日期/时间的样式;
- M: 中日期/时间的样式;
- L: 长日期/时间的样式;
- F: 完整日期/时间的样式;
- -: 忽略日期/时间的样式;
@NumberFormat可对类似数字类型的属性进行标准,它拥有两个互斥的属性。
- pattern。类型为String,使用自定义的数字格式化字符串,"##,###.##"。
- style。类型为
NumberFormat.Style
,常用值:Style.NUMBER
正常数字类型Style.PERCENT
百分数类型Style.CURRENCY
货币类型
例子代码:
// 域对象,实现序列化接口 public class User implements Serializable{ // 日期类型 @DateTimeFormat(pattern="yyyy-MM-dd") private Date birthday; // 正常数字类型 @NumberFormat(style=Style.NUMBER, pattern="#,###") private int total; // 百分数类型 @NumberFormat(style=Style.PERCENT) private double discount; // 货币类型 @NumberFormat(style=Style.CURRENCY) private double money; }
填写数据和展示效果:
3、数据校验
SpringMVC提供了强大的数据验证功能,其中有两种方法可以验证输入
1)利用Spring自定的validation校验框架。
2)利用JSR303(Java验证规范)实现验证功能
这里极少JSR303的使用方法。
3.1、导入需要的包
<!--JSR3.0校验--> <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator --> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>7.0.0.Final</version> </dependency> <!-- https://mvnrepository.com/artifact/javax.validation/validation-api --> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> <!-- https://mvnrepository.com/artifact/org.jboss.logging/jboss-logging --> <dependency> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging</artifactId> <version>3.3.2.Final</version> </dependency> <!-- https://mvnrepository.com/artifact/com.fasterxml/classmate --> <dependency> <groupId>com.fasterxml</groupId> <artifactId>classmate</artifactId> <version>1.5.1</version> </dependency>
3.2、springmvc.xml注册校验器
<!--开启自动注册处理器映射器和处理器适配器--> <!--开启Spring的valid功能 validator="validator" --> <mvc:annotation-driven validator="validator" conversion-service="formatService" /> <!--配置验证器--> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator"></property> </bean>
3.3、常用验证特性
注解 | 功能 |
---|---|
@Null | 对象必须为null |
@NotNull | 对象必须不为null,无法检查长度为0的字符串 |
@NotBlank | 字符串必须不为Null,且去掉前后空格长度必须大于0 |
@AssertTrue | 对象必须为true |
@AssertFalse | 对象必须为false |
@Max(Value) | 必须为数字,且小于或等于Value |
@Min(Value) | 必须为数字,且大于或等于Value |
@DecimalMax(Value) | 必须为数字( BigDecimal ),且小于或等于Value。小数存在精度 |
@DecimalMin(Value) | 必须为数字( BigDecimal ),且大于或等于Value。小数存在精度 |
@Digits(integer,fraction) | 必须为数字( BigDecimal ),integer整数精度,fraction小数精度 |
@Size(min,max) | 对象(Array、Collection、Map、String)长度必须在给定范围 |
@Range(min,max) | 验证数字是否介入min和max之间 |
字符串必须是合法邮件地址 | |
@Past | Date和Calendar对象必须在当前时间之前 |
@Future | Date和Calendar对象必须在当前时间之后 |
@Pattern(regexp=“正则”) | String对象必须符合正则表达式 |
@Url | 验证是否合法的Url地址 |
@CreditCardNumber | 信用卡校验 |
3.4、例子
1)在模型类上增加标准
2)控制器内判断验证是否有问题
- 给模型类前边增加@Valid标准,代表User对象绑定时会检查验证规则。
- BindResult参数必须紧跟在有@Valid参数的后边,用来获取绑定之后的错误信息
- 如果有验证错误,则重新返回到填写界面,需要把界面需要的数据都携带上。
//用户注册提交 @RequestMapping(value = "register", method = RequestMethod.POST) public String register(Model model, @Valid User user, BindingResult bindResult, List<MultipartFile> headImgUpload, HttpServletRequest request ) { System.out.println("执行TestController==2"); System.out.println(bindResult.toString()); //验证不通过 if (bindResult.getFieldErrorCount() > 0) { //获取所有的错误 List<ObjectError> errList = bindResult.getAllErrors(); for(ObjectError err:errList) { System.out.println(err.toString()); } model.addAttribute("user", user); model.addAttribute("sexList", rui.tool.listHelper.getSex()); model.addAttribute("deptList", rui.tool.listHelper.getSex()); model.addAttribute("loveList", rui.tool.listHelper.getLove()); return "/test/register"; } model.addAttribute("user", user); return "forward:/test/showInfo"; }
3)前端JSP页面显示错误
<div>用户账号:<f:input path="loginId" data-id="123" /><f:errors path="loginId"/> </div>
如果是前后端分离的,需要自己获取错误描述信息,并通过JSON返回。