数据格式化&验证以及国际化

1.数据格式化

1.1基本介绍

说明:在我们提交数据(比如表单时),SpringMVC 是怎样对提交的数据进行转换和处理的

基本数据类型可以和字符串之间自动进行转换,比如:SpringMVC 上下文中内建了很多转换器,可以完成大多数 java 类型的转换工作。[相互转换,这里只列出部分]

  1. ConversionService converters =
  2. java.lang.Boolean-> java.lang.Stringorg.springframework.core.convert.support.ObjectToStringConverter@f874ca
  3. java.lang.Character -> java.lang.Number : CharacterToNumberFactory@f004c9
  4. java.lang.Character -> java.lang.String : ObjectToStringConverter@68a961
  5. java.lang.Enum -> java.lang.String : EnumToStringConverter@12f060a
  6. java.lang.Number -> java.lang.Character : NumberToCharacterConverter@1482ac5
  7. java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory@126c6f
  8. java.lang.Number -> java.lang.String : ObjectToStringConverter@14888e8
  9. java.lang.String -> java.lang.Boolean : StringToBooleanConverter@1ca6626
  10. java.lang.String -> java.lang.Character : StringToCharacterConverter@1143800
  11. java.lang.String -> java.lang.Enum : StringToEnumConverterFactory@1bba86e
  12. java.lang.String -> java.lang.Number : StringToNumberConverterFactory@18d2c12
  13. java.lang.String -> java.util.Locale : StringToLocaleConverter@3598e1
  14. java.lang.String -> java.util.Properties : StringToPropertiesConverter@c90828
  15. java.lang.String -> java.util.UUID : StringToUUIDConverter@a42f23
  16. java.util.Locale -> java.lang.String : ObjectToStringConverter@c7e20a
  17. java.util.Properties -> java.lang.String : PropertiesToStringConverter@367a7f
  18. java.util.UUID -> java.lang.String : ObjectToStringConverter@112b07f
  19. ……

1.2基本数据类型和字符串自动转换

应用实例:基本数据类型可以和字符串之间自动地完成转换

(1)Monster.java

  1. package com.li.web.datavalid.entity;
  2. /**
  3. * @author 李
  4. * @version 1.0
  5. */
  6. public class Monster {
  7. private Integer id;
  8. private String email;
  9. private Integer age;
  10. private String name;
  11. public Monster() {}
  12. public Monster(Integer id, String email, Integer age, String name) {
  13. this.id = id;
  14. this.email = email;
  15. this.age = age;
  16. this.name = name;
  17. }
  18. public Integer getId() {
  19. return id;
  20. }
  21. public void setId(Integer id) {
  22. this.id = id;
  23. }
  24. public String getEmail() {
  25. return email;
  26. }
  27. public void setEmail(String email) {
  28. this.email = email;
  29. }
  30. public Integer getAge() {
  31. return age;
  32. }
  33. public void setAge(Integer age) {
  34. this.age = age;
  35. }
  36. public String getName() {
  37. return name;
  38. }
  39. public void setName(String name) {
  40. this.name = name;
  41. }
  42. @Override
  43. public String toString() {
  44. return "Monster{" +
  45. "id=" + id +
  46. ", email='" + email + '\'' +
  47. ", age=" + age +
  48. ", name='" + name + '\'' +
  49. '}';
  50. }
  51. }

(2)data_valid.jsp:

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <html>
  3. <head>
  4. <title>SpringMVC[数据格式/验证等]</title>
  5. </head>
  6. <body>
  7. <h1>SpringMVC[数据格式/验证等]</h1>
  8. <hr/>
  9. <a href="<%=request.getContextPath()%>/addMonsterUI">添加妖怪</a>
  10. </body>
  11. </html>

(3)MonsterHandler.java

  1. package com.li.web.datavalid;
  2. import com.li.web.datavalid.entity.Monster;
  3. import org.springframework.context.annotation.Scope;
  4. import org.springframework.stereotype.Controller;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import java.util.Map;
  7. /**
  8. * @author 李
  9. * @version 1.0
  10. * MonsterHandler 处理器响应用户提交数据
  11. * @Scope(value = "prototype")表示每次请求MonsterHandler都会生成一个新的对象
  12. */
  13. @Controller
  14. @Scope(value = "prototype")
  15. public class MonsterHandler {
  16. /**
  17. * 显示添加monster的页面
  18. * 这里的 map,当我们向 map添加数据时,会默认存放到 request域中
  19. * @param map
  20. * @return
  21. */
  22. @RequestMapping(value = "/addMonsterUI")
  23. public String addMonsterUI(Map<String, Object> map) {
  24. //如果你跳转的页面使用了SpringMVC标签,就需要准备一个对象放入到request域中,
  25. //该对象的属性名要对应SpringMVC的form标签的modelAttribute的属性名,否则报错
  26. map.put("monster", new Monster());
  27. return "datavalid/monster_addUI";
  28. }
  29. /**
  30. * SpringMVC可以将提交的数据,按照参数名和形参对象的属性名匹配,
  31. * 然后直接封装到对象中[模型数据]
  32. * @param monster
  33. * @return
  34. */
  35. @RequestMapping(value = "/save")
  36. public String save(Monster monster) {
  37. System.out.println("monster---" + monster);
  38. return "datavalid/success";
  39. }
  40. }

(4)monster_addUI.jsp:

  1. <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
  2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  3. <html>
  4. <head>
  5. <title>添加妖怪</title>
  6. </head>
  7. <body>
  8. <h3>添加妖怪</h3>
  9. <%--
  10. 1. 这里的表单,我们使用SpringMVC的标签来完成,目的是方便提示信息的回显
  11. 2. SpringMVC表单标签在显示之前,必须在request域中有一个 beanjava对象),
  12. bean的属性名和表单标签的字段要对应!
  13. 3. request 域中的 key form 标签的 modelAttribute的属性值,比如这里的 monsters
  14. 4.SpringMVC form:form 标签的 action 属性值中的 / 不代表 WEB应用的根目录。
  15. --%>
  16. <form:form action="save" method="post" modelAttribute="monster">
  17. 妖怪id:<form:input path="id"/><br/><br/>
  18. 妖怪名字:<form:input path="name"/><br/><br/>
  19. 妖怪年龄:<form:input path="age"/><br/><br/>
  20. 电子邮件:<form:input path="email"/><br/><br/>
  21. <input type="submit" value="添加妖怪"/>
  22. </form:form>
  23. </body>
  24. </html>

(5)success.jsp

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <html>
  3. <head>
  4. <title>添加成功</title>
  5. </head>
  6. <body>
  7. <h1>恭喜你,添加成功</h1>
  8. </body>
  9. </html>

(6)测试

后台成功拿到了数据,并将 String 类型的字符串转化为相应的数据类型,赋给 Monster 对象属性。说明 SpringMVC 底层可以自动进行基本数据类型转换。

1.3特殊数据类型和字符串间的转换

  1. 特殊数据类型和字符串之间的转换可以使用注解来完成(比如日期,规定格式的小数比如货币形式)
  2. 对于日期和货币可以使用 @DataTimeFormat 和 @NumberFormat 注解。

应用实例

(1)修改Monster.java,增加 birthday 和 salary 字段。修改相应的构造器,getter 和 setter 等。

(2)data_valid.jsp 不变

(3)MonsterHandler.java 不变

(4)修改 monster_addUI.jsp:

  1. <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
  2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  3. <html>
  4. <head>
  5. <title>添加妖怪</title>
  6. </head>
  7. <body>
  8. <h3>添加妖怪</h3>
  9. <%--
  10. 1. 这里的表单,我们使用SpringMVC的标签来完成,目的是方便提示信息的回显
  11. 2. SpringMVC表单标签在显示之前,必须在request域中有一个 beanjava对象),
  12. bean的属性名和表单标签的字段要对应!
  13. 3. request 域中的 key form 标签的 modelAttribute的属性值,比如这里的 monsters
  14. 4.SpringMVC form:form 标签的 action 属性值中的 / 不代表 WEB应用的根目录。
  15. --%>
  16. <form:form action="save" method="post" modelAttribute="monster">
  17. 妖怪id:<form:input path="id"/><br/><br/>
  18. 妖怪名字:<form:input path="name"/><br/><br/>
  19. 妖怪年龄:<form:input path="age"/><br/><br/>
  20. 妖怪生日:<form:input path="birthday"/><br/><br/>
  21. 妖怪工资:<form:input path="salary"/><br/><br/>
  22. 电子邮件:<form:input path="email"/><br/><br/>
  23. <input type="submit" value="添加妖怪"/>
  24. </form:form>
  25. </body>
  26. </html>

(5)success.jsp 不变

(6)redeployTomcat,进行测试

提交的数据:

后台输出:

  1. monster---Monster{id=22, email='king@sohu.com', age=99, name='king', birthday=Tue Jan 01 00:00:00 CST 1924, salary=124567.22}

如果转换失败,将会提示:

  1. Field error in object 'monster' on field 'birthday': rejected value [192401-01]; codes [typeMismatch.monster.birthday,typeMismatch.birthday,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [monster.birthday,birthday]; arguments []; default message [birthday]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'birthday'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.format.annotation.DateTimeFormat java.util.Date] for value '192401-01'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [192401-01]]]

2.验证以及国际化

2.1概述

(1)概述

  1. 对于输入的数据(比如表单数据),进行必要的验证,并给出相应的提示信息
  2. 对于验证表单数据,SpringMVC 提供了很多使用的注解,这些注解由 JSR 303验证框架提供。

(2)JSR 303 验证框架

  1. JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 中
  2. JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标注的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证
  3. JSR 303 提供的基本验证注解有:
注解 功能说明
@Null 被注释的元素必须为null
@NotNull 被注释的元素不能为null
@AssertTrue 被注释的元素必须为true
@AssertFalse 被注释的元素必须为false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max,min) 被注释的元素的大小必须在指定的范围内
@Digits(integer,fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式

(3)Hibernate Validator 扩展注解

  1. Hibernate Validator 和 Hibernate 没有关系,只是 JSR 303 实现的一个扩展

  2. Hibernate Validator 是 JSR 303的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:

注解 功能说明
@Email 被注释的元素必须是电子邮件地址
@Length 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串必须非空
@Range 被注释的元素必须在合适的范围内

2.2应用实例

需求说明

指定表单的数据格式,后端在接收到数据后,能够对数据进行校验,并给不符合格式的数据返回提示信息,显示在前端页面

2.2.1代码实现

(1)引入验证和国际化相关的jar包

(2)Monster.java,属性添加注解以验证格式

  1. package com.li.web.datavalid.entity;
  2. import org.hibernate.validator.constraints.Email;
  3. import org.hibernate.validator.constraints.NotEmpty;
  4. import org.hibernate.validator.constraints.Range;
  5. import org.springframework.format.annotation.DateTimeFormat;
  6. import org.springframework.format.annotation.NumberFormat;
  7. import java.util.Date;
  8. /**
  9. * @author 李
  10. * @version 1.0
  11. */
  12. public class Monster {
  13. @NotEmpty
  14. private Integer id;
  15. @Email
  16. private String email;
  17. //表示接收到的age的值必须在1-100之间
  18. @Range(min = 1, max = 100)
  19. private Integer age;
  20. //Asserts that the annotated string, collection,
  21. // map or array is not {@code null} or empty.
  22. @NotEmpty
  23. private String name;
  24. @DateTimeFormat(pattern = "yyyy-MM-dd")
  25. private Date birthday;
  26. @NumberFormat(pattern = "###,###.##")
  27. private Float salary;
  28. public Monster() {
  29. }
  30. public Monster(Integer id, String email, Integer age, String name, Date birthday, Float salary) {
  31. this.id = id;
  32. this.email = email;
  33. this.age = age;
  34. this.name = name;
  35. this.birthday = birthday;
  36. this.salary = salary;
  37. }
  38. public Integer getId() {
  39. return id;
  40. }
  41. public Date getBirthday() {
  42. return birthday;
  43. }
  44. public void setBirthday(Date birthday) {
  45. this.birthday = birthday;
  46. }
  47. public Float getSalary() {
  48. return salary;
  49. }
  50. public void setSalary(Float salary) {
  51. this.salary = salary;
  52. }
  53. public void setId(Integer id) {
  54. this.id = id;
  55. }
  56. public String getEmail() {
  57. return email;
  58. }
  59. public void setEmail(String email) {
  60. this.email = email;
  61. }
  62. public Integer getAge() {
  63. return age;
  64. }
  65. public void setAge(Integer age) {
  66. this.age = age;
  67. }
  68. public String getName() {
  69. return name;
  70. }
  71. public void setName(String name) {
  72. this.name = name;
  73. }
  74. @Override
  75. public String toString() {
  76. return "Monster{" +
  77. "id=" + id +
  78. ", email='" + email + '\'' +
  79. ", age=" + age +
  80. ", name='" + name + '\'' +
  81. ", birthday=" + birthday +
  82. ", salary=" + salary +
  83. '}';
  84. }
  85. }

(3)MonsterHandler.java

  1. package com.li.web.datavalid;
  2. import com.li.web.datavalid.entity.Monster;
  3. import org.springframework.context.annotation.Scope;
  4. import org.springframework.stereotype.Controller;
  5. import org.springframework.validation.Errors;
  6. import org.springframework.validation.ObjectError;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.RequestMethod;
  9. import javax.validation.Valid;
  10. import java.util.Map;
  11. /**
  12. * @author 李
  13. * @version 1.0
  14. */
  15. @Controller
  16. @Scope(value = "prototype")
  17. public class MonsterHandler {
  18. /**
  19. * 1.SpringMVC可以将提交的数据,按照参数名和形参对象的属性名匹配,
  20. * 然后直接封装到对象中[模型数据]
  21. * 2.@Valid Monster monster 表示对monster接收的数据进行校验
  22. * 3.校验的发生的时机:在SpringMVC底层反射调用目标方法时,会接收到http请求接收到的数据,
  23. * 然后根据注解来进行验证。在验证过程中,如果出现了错误,就把错误信息填充到errors和 map中
  24. * @param monster
  25. * @param errors 表示如果校验出现了错误,会将校验的错误信息保存到errors中
  26. * @param map map不但会保存monster对象,如果校验出现错误,也会将校验的错误信息放到map中
  27. * @return
  28. */
  29. @RequestMapping(value = "/save", method = RequestMethod.POST)
  30. public String save(@Valid Monster monster, Errors errors, Map<String, Object> map) {
  31. System.out.println("----monster----" + monster);
  32. //为了查看验证的情况,输出map和errors
  33. System.out.println("=======map=======");
  34. for (Map.Entry<String, Object> entry : map.entrySet()) {
  35. System.out.println("key=" + entry.getKey() +
  36. " value=" + entry.getValue());
  37. System.out.println("--------");
  38. }
  39. System.out.println("=======errors=======");
  40. for (ObjectError error : errors.getAllErrors()) {
  41. System.out.println("error="+error);
  42. }
  43. return "datavalid/success";
  44. }
  45. }

(4)monster_addUI.jsp:

  1. <%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
  2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  3. <html>
  4. <head>
  5. <title>添加妖怪</title>
  6. </head>
  7. <body>
  8. <h3>添加妖怪</h3>
  9. <form:form action="save" method="post" modelAttribute="monster">
  10. 妖怪id:<form:input path="id"/><br/><br/>
  11. 妖怪名字:<form:input path="name"/><br/><br/>
  12. 妖怪年龄:<form:input path="age"/><br/><br/>
  13. 妖怪生日:<form:input path="birthday"/> 要求以"yyyy-MM-dd"的格式<br/><br/>
  14. 妖怪工资:<form:input path="salary"/> 要求以"###,###.##"的格式<br/><br/>
  15. 电子邮件:<form:input path="email"/><br/><br/>
  16. <input type="submit" value="添加妖怪"/>
  17. </form:form>
  18. </body>
  19. </html>

(5)测试

提交的数据:年龄这里故意填写不符合格式的数据(1-100)

后台输出了默认的错误信息:

  1. ----monster----Monster{id=1, email='king@sohu.com', age=999, name='king', birthday=Tue Jan 01 00:00:00 CST 1924, salary=1267.22}
  2. =======map=======
  3. key=monster value=Monster{id=1, email='king@sohu.com', age=999, name='king', birthday=Tue Jan 01 00:00:00 CST 1924, salary=1267.22}
  4. --------
  5. key=org.springframework.validation.BindingResult.monster value=org.springframework.validation.BeanPropertyBindingResult: 1 errors
  6. Field error in object 'monster' on field 'age': rejected value [999]; codes [Range.monster.age,Range.age,Range.java.lang.Integer,Range]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [monster.age,age]; arguments []; default message [age],100,1]; default message [需要在1100之间]
  7. --------
  8. =======errors=======
  9. error=Field error in object 'monster' on field 'age': rejected value [999]; codes [Range.monster.age,Range.age,Range.java.lang.Integer,Range]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [monster.age,age]; arguments []; default message [age],100,1]; default message [需要在1100之间]

(6)自定义错误信息:配置 springDispatcherServlet-servlet.xml

  1. <!--配置国际化错误信息的资源处理 bean-->
  2. <bean class="org.springframework.context.support.ResourceBundleMessageSource"
  3. id="messageSource">
  4. <!--
  5. 配置国际化文件名字
  6. 如果下面这样配置,表示 messageSource对象会到src/i18nXXX.properties 去读取错误信息
  7. -->
  8. <property name="basename" value="i18n"/>
  9. </bean>

(7)在src 目录下创建国际化文件 i18n.properties

中文要使用 unicode 编码处理

  1. NotEmpty.monster.name=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a
  2. typeMismatch.monster.age=\u5e74\u9f84\u8981\u6c42\u5728\u0031\u002d\u0031\u0035\u0030\u4e4b\u95f4
  3. typeMismatch.monster.birthday=\u751f\u65e5\u683c\u5f0f\u4e0d\u6b63\u786e
  4. typeMismatch.monster.salary=\u85aa\u6c34\u683c\u5f0f\u4e0d\u6b63\u786e

(8)修改 monster_addUI.jsp 的 form ,回显错误信息

  1. <form:form action="save" method="post" modelAttribute="monster">
  2. 妖怪id:<form:input path="id"/><form:errors path="id"/><br/><br/>
  3. 妖怪名字:<form:input path="name"/><form:errors path="name"/><br/><br/>
  4. 妖怪年龄:<form:input path="age"/><form:errors path="age"/><br/><br/>
  5. 妖怪生日:<form:input path="birthday"/><form:errors path="birthday"/>
  6. 要求以"yyyy-MM-dd"的格式<br/><br/>
  7. 妖怪工资:<form:input path="salary"/><form:errors path="salary"/>
  8. 要求以"###,###.##"的格式<br/><br/>
  9. 电子邮件:<form:input path="email"/><form:errors path="email"/><br/><br/>
  10. <input type="submit" value="添加妖怪"/>
  11. </form:form>

(9)再次进行测试

没有在properties文件中配置的提示,将会按照默认的错误信息回显

2.2.2细节说明和注意事项

  1. 在需要验证的 Javabean/POJO 的字段上添加相应的验证注解

  2. 目标方法上,在 Javabean/POJO 类型的参数前,添加 @Valid 注解以告知 SpringMVC 该 Bean 是需要验证的

  3. 在 @Valid 注解之后,添加一个 Errors 或 BindingResult 类型的参数,可以获取到验证的错误信息

    校验的发生的时机:SpringMVC 底层反射调用目标方法前,会接收到 http 请求接收到的数据,然后根据验证注解来进行验证。在验证过程中,如果出现了错误,就把错误信息填充到 errors,map 等参数中

  4. 需要使用 <form:errors path="xxx"></form:errors> 标签来显示错误信息,该标签需要写在 <form:form> 标签内生效

  5. 自定义错误消息的国际化文件 i18n.properties,如果是中文需要使用 unicode 编码处理。

    格式为:验证规则.表单modelAttribute值.属性名=错误提示信息

  6. 注意@NotNull 和 @NotEmpty 的区别

    @NotEmpty:

    1. Asserts that the annotated string, collection, map or array is not {@code null} or empty.

    @NotNull:

    1. The annotated element must not be {@code null}. Accepts any type.

    如果是字符串验证空,建议使用 @NotEmpty

  7. SpringMVC 验证时,同一个属性,会根据不同的验证错误,返回不同的错误信息

2.3注解的结合使用

2.3.1问题提出

在之前的例子中,表单中的数据虽然不符验证格式,但是仍然可以提交:

这显然是不合理的,我们想要在提交的时候,如果age的字段为空,也应该基于一个提示信息,并且不应该提交表单,这个问题应该如何解决?

答案是注解组合使用。

上面的例子中,表单字段所对应的 age 属性只添加了@Range 注解,程序只会检查提交的 age 的范围,其他的错误无法检出,因此会出现即使提交的数据为空,也能提交成功的情况。

2.3.2解决问题

根据上面的分析,可以使用 @NotNull + @Range 组合来解决问题

(1)修改 Monster.java:

可以直接在验证注解中添加 message 属性,作为错误提示,这样就不必在 properties 文件中配置错误信息

(2)测试如下:

注意:这里不要使用 @NotEmpty 注解,否则会出错。

因为 @NotEmpty 注解修饰的是 String,Collection,Map 和 Array 类型,而 @NotNull 任何类型都可以使用。其他的属性同理,如果是 String 等类型,使用 @NotEmpty 注解,如果是其他类型,应使用 @NotNull 注解。

2.4数据类型转换校验核心类-DataBinder

  • DateBinder 工作机制

图例,SpringMVC 通过反射机制对目标方法进行解析,将请求信息绑定到处理方法(目标方法)的入参中。

数据绑定的核心部件是 DataBinder,运行机制如下:

  1. 首先 url 请求发送到服务器 Handler 的某个方法,然后 tomcat 给我们传入一个 ServletRequest 对象

  2. 在 SpringMVC 底层反射调用目标方法前,DataBinder 对象处理目标方法的入参对象(得到目标方法的形参的一些信息,将请求信息绑定到处理方法(目标方法)的入参中),在这个过程中:

    • 通过调用 ConversionService 接口的实现类的方法,进行数据类型的转换/格式化。

    • 接着进行数据的校验,数据校验会调用校验器 Validator(SpringMVC 的数据校验器也有很多种)

    • 在校验的过程中一旦出现错误,会将错误信息封装到 BindingResult(BindingResult 其实是一个接口,下面有很多实现类)

      之前的例子中,Errors 是 BindingResult 的父类接口


debug 一下 Validator 得到验证 errors 信息

(1)在 DataBinder 的 validate 方法中打上断点,点击debug

(2)在前端表单提交错误格式的信息

(3)提交表单后光标跳转到断点处,可以看到此时的 target 就是目标方法中要进行校验的 Monster 对象,因为我们在目标方法的 Monster 形参前添加了 @Valid 注解。

此时前端提交的数据已经提填充进去了:

(4)点击 step over,可以看到方法中创建了BindingResult对象,该对象用于存储数据类型转换和数据校验中可能出现的错误信息。

(5)之后遍历校验器,对 target 对象进行校验,如果出现错误信息就将其放到 BindingResult 中

  1. public void validate(Object... validationHints) {
  2. Object target = getTarget();
  3. Assert.state(target != null, "No target to validate");
  4. BindingResult bindingResult = getBindingResult();
  5. // Call each validator with the same binding result
  6. for (Validator validator : getValidators()) {
  7. if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
  8. ((SmartValidator) validator).validate(target, bindingResult, validationHints);
  9. }
  10. else if (validator != null) {
  11. validator.validate(target, bindingResult);
  12. }
  13. }
  14. }

(6)在目标方法中打上断点,点击 resume

2.5取消某个属性的绑定

  • 说明

在默认情况下,表单提交的数据都会和 pojo/entity 类型的 Javabean 的属性绑定。如果在开发中,希望取消某个JavaBean 的属性的绑定,也就是说不希望接收到某个表单对应的属性的值(比如说有一个Javabean,我们已经赋予其初始值,不希望被接收表单对应的属性值给覆盖),可以通过 @InitBinder 注解取消绑定。

  1. 编写一个方法(方法名随意),使用 @InitBinder 标识该方法,可以对 WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的子类,用于完成表单字段到 JavaBean 属性的绑定。
  2. @InitBinder 标识的方法不能有返回值,它必须声明为 void
  3. @InitBinder 标识的方法的参数通常是 WebDataBinder

2.5.1使用实例

案例:不希望接收Monster的 name 和 age 属性

(1)修改Monster.java:

(2)修改MonsterHandler.java,增加方法

  1. package com.li.web.datavalid;
  2. import com.li.web.datavalid.entity.Monster;
  3. import org.springframework.context.annotation.Scope;
  4. import org.springframework.stereotype.Controller;
  5. import org.springframework.validation.BeanPropertyBindingResult;
  6. import org.springframework.validation.Errors;
  7. import org.springframework.validation.ObjectError;
  8. import org.springframework.web.bind.WebDataBinder;
  9. import org.springframework.web.bind.annotation.InitBinder;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11. import org.springframework.web.bind.annotation.RequestMethod;
  12. import javax.validation.Valid;
  13. import java.util.List;
  14. import java.util.Map;
  15. /**
  16. * @author 李
  17. * @version 1.0
  18. */
  19. @Controller
  20. @Scope(value = "prototype")
  21. public class MonsterHandler {
  22. @RequestMapping(value = "/save", method = RequestMethod.POST)
  23. public String save(@Valid Monster monster, Errors errors, Map<String, Object> map) {
  24. System.out.println("----monster----" + monster);
  25. //为了查看验证的情况,输出map和errors
  26. System.out.println("=======map=======");
  27. for (Map.Entry<String, Object> entry : map.entrySet()) {
  28. System.out.println("key=" + entry.getKey() +
  29. " value=" + entry.getValue());
  30. System.out.println("--------");
  31. }
  32. System.out.println("=======errors=======");
  33. if (errors.hasErrors()) {//判断是否有错误
  34. List<ObjectError> allErrors = errors.getAllErrors();
  35. for (ObjectError error : allErrors) {
  36. System.out.println("error=" + error);
  37. }
  38. return "datavalid/monster_addUI";
  39. }
  40. return "datavalid/success";
  41. }
  42. //取消绑定 monster的name表单提交值给 monster.name属性
  43. @InitBinder
  44. public void initBinderExample(WebDataBinder webDataBinder) {
  45. /**
  46. * 1.方法上需要标注 @InitBinder 注解,SpringMVC 底层会初始化 WebDataBinder
  47. * 2.调用webDataBinder.setDisallowedFields("xxx");
  48. * 表示取消xxx属性的绑定
  49. * 3.即当表单提交字段有name时,不再将该参数填充到model(monster)的name属性中
  50. * 4.机制:SpringMVC 在底层通过反射调用目标方法前,在接收到http请求的参数和值,
  51. * 会使用反射+注解,取消对指定属性的填充。
  52. * 5.setDisallowedFields 支持可变参数,可以填写多个字段
  53. * 6.如果取消了某个属性绑定,验证就没有意义了,应当把javabean中验证的注解去掉,否则可能会出错
  54. * //@NotEmpty
  55. * private String name;
  56. *
  57. * //@NotNull(message = "年龄age不能为空111")
  58. * //@Range(min = 1, max = 100)
  59. * private Integer age;
  60. * 如上,name和age属性将会使用默认值(这里为null)
  61. */
  62. webDataBinder.setDisallowedFields("name", "age");
  63. }
  64. }

(3)测试,提交的数据如下:

后台接收的数据:

  1. ----monster----Monster{id=12, email='jack@sohu.com', age=null, name='null', birthday=Tue Jan 01 00:00:00 CST 1924, salary=9999.0}

可以看到 monster的 age 和 name 属性仍然使用的是默认值 null ,这说明取消属性绑定的操作成功了。

注意:这里 age 和 name 的值为 'null',之所以有俩个单引号,是因为重写了 toString 方法,其实这两个属性的值就是 null

2.5.2注意事项和使用说明

  1. setDisallowedFields() 是可变形参,可以指定多个字段/属性
  2. 当将一个字段/属性设置为 disallowed,就不会再接收表单提交的对应值。这个字段的值就会使用默认的值
  3. 一般来说,如果不接收表单某个字段的提交数据,则 Javabean 对象中的对应的属性的验证就没有意义,可以将该属性的验证注解去掉

day09-数据格式化&验证以及国际化的更多相关文章

  1. SpringMVC框架下数据的增删改查,数据类型转换,数据格式化,数据校验,错误输入的消息回显

    在eclipse中javaEE环境下: 这儿并没有连接数据库,而是将数据存放在map集合中: 将各种架包导入lib下... web.xml文件配置为 <?xml version="1. ...

  2. SpringMVC 数据转换 & 数据格式化 & 数据校验

    数据绑定流程 1. Spring MVC 主框架将 ServletRequest 对象及目标方法的入参实例传递给 WebDataBinderFactory 实例,以创建 DataBinder 实例对象 ...

  3. SpringMVC听课笔记(九:数据转换 & 数据格式化 & 数据校验)

    1.数据绑定流程 --1). Spring MVC主框架将ServletRequest对象及目标方法入参实例传递给WebDataBinderFactory实例,以创建DataBinder实例对象. - ...

  4. Spring MVC @InitBinder 数据绑定 & 数据格式化 & 数据校验

    1 数据绑定 2 数据格式化 修改绑定的字段等等操作 日期 - 接收表单日期字符串格式内容.,在实体类加入@DateTimeFormat 数值 原理: DefautFormattingConversi ...

  5. SpringMVC——数据转换 & 数据格式化 & 数据校验

    一.数据绑定流程 1. Spring MVC 主框架将 ServletRequest 对象及目标方 法的入参实例传递给 WebDataBinderFactory 实例,以创 建 DataBinder ...

  6. SpringMVC的数据转换&&数据格式化&&数据校验

    1 SpringMVC的数据绑定流程 SpringMVC将ServletRequest对象及目标方法的入参实例传递给WebDataBinderFactory实例,以创建DataBinder实例对象. ...

  7. Spring MVC—数据绑定机制,数据转换,数据格式化配置,数据校验

    Spring MVC数据绑定机制 数据转换 Spring MVC处理JSON 数据格式化配置使用 数据校验 数据校验 Spring MVC数据绑定机制 Spring MVC解析JSON格式的数据: 步 ...

  8. 解决Yii2 启用_csrf验证后POST数据仍提示“您提交的数据无法验证”

    一 CSRF 概念 CSRF(Cross-site request forgery跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XS ...

  9. ECharts图表中级入门之formatter:夜谈关于ECharts图表内的数据格式化方法

    来源于:http://www.ithao123.cn/content-3751220.html 格式化之所以存在,主要是因为我们想把一些不够人性化的内容通过某种处理让其变得人性化,便于用户更好地理解内 ...

  10. PHP json数据格式化方法

    php 的json_encode能把数组转换为json格式的字符串.字符串没有缩进,中文会转为unicode编码,例如\u975a\u4ed4.人阅读比较困难.现在这个方法在json_encode的基 ...

随机推荐

  1. 6、后端学习规划:Java学习 - 学习规划系列文章

    Java语言在现在的编程语言排行榜上也是前5的存在.经过这么些年的发展,Java的发展没有C#这么大,但是在编程领域,因为有MVC架构,以及Spring框架的支持,以及微服务架构等等,Java这些年也 ...

  2. Git企业开发控制理论和实操-从入门到深入(三)|分支管理

    前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量博客汇总 然后就是博主最近最花时间的一 ...

  3. XmlDocument 解决 Clone、CloneNode、ImportNode 等节点克隆后的标签自闭合问题

    前言: 这两天在对 Taurus.Mvc 做 html 加载性能优化时,发现存在这个问题. 具体优化的是 CYQ.Data 组件的 XHtmlAction 相关类. 问题过程: 之前 XmlDocum ...

  4. (C语言)每日代码||2023.12.27||关于(++i)+(++i)以及(++i)+(i = 100)

    #include <stdio.h> int main() { int i = 1; int a = (++i) + (++i); printf("a = %d,i = %d\n ...

  5. DecisionTreeClassifier&DecisionTreeClassRegression

    DecisionTreeClassifier from sklearn.datasets import load_wine # 红酒数据集 from sklearn.tree import Decis ...

  6. DP的各种优化小结

    动态规划算法(简称动规,DP),是IO中最为常见的,也是最为重要的算法之一.这也就意味着,在各种题目与比赛中它会有很多稀奇古怪的算法和优化,时不时地在你的面前出现一个TLE,MLE和RE来搞你的心态. ...

  7. Python Split 函数用法

    一.split函数简介Python中split()函数,具体作用如下: 拆分字符串.通过指定分隔符对字符串进行切片,并返回分割后的字符串列表(list):二.语法split() 方法语法: str.s ...

  8. NVME(学习笔记_杂谈)

    NVME 协议中一些概念的理解: 1.Namespace :可以将Namespace 理解为一片Logic Block的区域,一个Controller可以支持多个Namespace,每个Namespa ...

  9. Kubernetes上使用Ingress Nginx将服务发布到外部IP

    Kubernetes的网络结构 K8s的网络相对比较复杂, 包含了如下几类IP地址: Host Network 运行K8s集群的宿主服务器的内网IP, 其网段在配置宿主机时设置. 这些服务器可能是物理 ...

  10. 《深入理解Java虚拟机》(八) 记录一次OOM问题分析实战

    目录 一.问题分析思路 二.主要问题概述以及分析 1.相关操作 2.主要问题现象 3.初步分析问题 三.相关工具介绍 四.实际问题快照分析 1.通过Memory查看老年代内存占用情况 2.选择Live ...