SpringMVC數據校驗


29.1 類型轉換

form表單提交的數據都是String類型,例如在Servlet中我們是通過String filedName=request.getParameter(“…”)方法來獲取相應的字段值。如果需要的是int類型,在 Servlet中我們也必須進行類型轉換,如int age=Integer.parseInt(…)。但是在SpringMVC中,我們並不需要關心類型的轉換,例如:

復制
  1. @RequestMapping(value = "/requestWithREST/{id}",
  2. method = RequestMethod.POST)
  3. public String requestWithRestAdd(@PathVariable("id") Integer id)
  4. {
  5. System.out.println("增加時需要的id:" + id);
  6. return "success";
  7. }

SpringMVC可以直接將form表單中的id字段值轉為Integer類型,並傳遞給requestWithRestAdd()方法中的參數id。這是因為SpringMVC中存在着一些內置的類型轉換器,可以自動實現大多數的類型轉換。

除此以外,我們還可以根據需求來自定義類型轉換器。例如,現在需要將form表單傳來的字符串1-張三-23解析成學號為1、姓名為張三、年齡為23,並將這些值封裝到一個學生對象之中,也就是說需要將字符串1-張三-23轉換為Student對象類型。

以下是具體的實現步驟:

①創建自定義類型轉換器

創建基於SpringMVC的自定義類型轉換器,需要新建一個類,並實現SpringMVC提供的Converter接口,如下,

自定義類型轉換器,用於將字符串轉換為Student類型 : StudentConverter.java

復制
  1. import org.springframework.core.convert.converter.Converter;
  2. public class StudentConverter
  3. implements Converter<String, Student>
  4. {
  5. @Override
  6. public Student convert(String source)
  7. {
  8. //source值就是前端form傳來的"1-張三-23"
  9. if (source != null)
  10. {
  11. //解析出source中的學號、姓名、年齡
  12. String[] vals = source.split("-");
  13. int stuNo = Integer.parseInt(vals[0]);
  14. String stuName = vals[1];
  15. int stuAge = Integer.parseInt(vals[2]);
  16. //將解析出的學號、姓名、年齡封裝到Student對象之中
  17. Student student = new Student();
  18. student.setStuNo(stuNo);
  19. student.setStuAge(stuAge);
  20. student.setStuName(stuName);
  21. return student;
  22. }
  23. return null;
  24. }
  25. }

②注冊自定義類型轉換器

springmvc.xml:將自定義的類型轉換器注冊到SpringMVC之中,共三步

復制
  1. <beans>
  2. <!-- ①將自定義的類型轉換器加入SpringIOC容器 -->
  3. <bean id="studentConverter"
  4. class="org.lanqiao.converter.StudentConverter"></bean>
  5. <!-- ②將自定義的類型轉換器注冊到 SpringMVC提供的
  6. ConversionServiceFactoryBean中-->
  7. <bean id="conversionService"
  8. class="org.springframework.context
  9. .support.ConversionServiceFactoryBean">
  10. <property name="converters">
  11. <set>
  12. <ref bean="studentConverter"/>
  13. </set>
  14. </property>
  15. </bean>
  16. <!--③ 將自定義的類型轉換器所在的ConversionServiceFactoryBean,
  17. 注冊到annotation-driven之中 -->
  18. <mvc:annotation-driven
  19. conversion-service="conversionService">
  20. </mvc:annotation-driven>
  21. </beans>

至此就完成了自定義類型轉化器的編寫及配置工作。以下,對配置完成的類型轉換器StudentConverter進行測試。

③請求處理方法

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/testConversionServiceConverer")
  6. public String testConversionServiceConverer
  7. (@RequestParam("studentInfo") Student student)
  8. {
  9. System.out.println("學號:"+student.getStuNo()
  10. +",姓名:"+student.getStuName()+",年齡:"
  11. +student.getStuAge());
  12. return "success";
  13. }
  14. }

④測試

index.jsp

復制
  1. <form action="FirstSpringDemo/testConversionServiceConverer">
  2. 學生信息: <input type="text" name="studentInfo"/>
  3. <input type="submit" value="增加"/>
  4. </form>

輸入學生信息“1-張三-23”,如圖,

圖29-01

點擊“增加”后,可在控制台得到以下結果:

圖29-02

通過自定義類型轉換器StudentConverter,成功的將前端傳來的字符串1-張三-23轉為了請求處理方法參數中的Student類型。

29.2 格式化數據

有時候需要對於日期、數字等類型進行格式化操作,例如:規定日期的格式必須為yyyy-MM-dd。

使用SpringMVC實現數據的格式化,只需要簡單的兩步操作:

在需要格式化的屬性前加上格式化注解,如@DateTimeFormat;

在springmvc.xml中加入<mvc:annotation-driven></mvc:annotation-driven>和SpringMVC提供的FormattingConversionServiceFactoryBean,如下:

springmvc.xml

復制
  1. <beans>
  2. <bean id="conversionService"
  3. class="org.springframework.format.
  4. support.FormattingConversionServiceFactoryBean">
  5. </bean>
  6. </beans>

說明:

通過類的名字可知,FormattingConversionServiceFactoryBean既提供了格式化需要的Formatting,又提供了類型轉換需要的Conversion。因此,之前配置類型轉換時使用的ConversionServiceFactoryBean,也可以使用FormattingConversionServiceFactoryBean來替代。也就是說,使用以下配置既可以實現自定義的類型轉換,也可以實現格式化數據:

復制
  1. <bean id="conversionService"
  2. class="org.springframework.
  3. format.support.FormattingConversionServiceFactoryBean">
  4. <property name="converters">
  5. <set>
  6. <ref bean="studentConverter"/>
  7. </set>
  8. </property>
  9. </bean>

例如,以下指定Date類型的birthday屬性的輸入格式必須為yyyy-MM-dd。

Student.java

復制
  1. public class Student
  2. {
  3. private int stuNo;
  4. private String stuName;
  5. @DateTimeFormat(pattern="yyyy-MM-dd")
  6. private Date birthday ;
  7. //setter、getter
  8. }

通過注解@DateTimeFormat(pattern="yyyy-MM-dd")指定birthday屬性的輸入格式必須為yyyy-MM-dd。以下是測試格式化的操作:

請求處理方法:FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/testDateTimeFormat")
  6. public String testDateTimeFormat(Student student){
  7. System.out.println("學號:"+student.getStuNo()
  8. +",姓名:"+student.getStuName()+",生日"
  9. +student.getBirthday());
  10. return "success";
  11. }
  12. }

請求頁index.jsp

復制
  1. <form action="FirstSpringDemo/testDateTimeFormat">
  2. 姓名:<input type="text" name="stuName"/><br>
  3. 年齡:<input type="text" name="stuAge"/><br>
  4. 生日:<input type="text" name="birthday"/><br>
  5. <input type="submit" value="提交"/>
  6. </form>

如果表單中輸入的日期格式符合“yyyy-MM-dd”,如“2015-05-16”,如圖,

圖29-03

就會將日期賦值給birthday屬性,並可以在控制台得到輸出結果:

圖29-04

而如果輸入的日期格式不符合“yyyy-MM-dd”格式,如輸入“2015年05月16日”,點擊“提交”后JSP頁面就會顯示HTTP Status 400,如圖,

圖29-05

但控制台並沒有任何異常信息的輸出,很不利於開發人員排查錯誤。為此,我們可以給請求處理方法加入一個BindingResult類型的參數,此參數就包含了格式化數據失敗時的異常信息,如下:

FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/testDateTimeFormat")
  6. public String testDateTimeFormat(Student student,
  7. BindingResult result)
  8. {
  9. //如果有錯誤信息
  10. if (result.getErrorCount() > 0)
  11. {
  12. //循環遍歷所有錯誤信息
  13. for (FieldError error : result.getFieldErrors())
  14. {
  15. System.out.println(error.getField() + ":"
  16. + error.getDefaultMessage());
  17. }
  18. }
  19. return "success";
  20. }
  21. }

此時再輸入不符合格式的日期“2015年05月16日”,就能既在JSP頁面顯示HTTP Status 400異常,又能在控制台得到具體的異常信息,如下:

控制台輸出:

復制
  1. birthday: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 [@javax.validation.constraints.Past @org.springframework.format.annotation.DateTimeFormat java.util.Date] for value '2015年05月16日';
  2. nested exception is java.lang.IllegalArgumentException:
  3. Unable to parse '2015年05月16日'

除了用於格式化日期的注解@DateTimeFormat以外,SpringMVC還提供了用於格式化數字的注解@NumberFormat,例如,可以使用@NumberFormat指定以下int類型的屬性count的輸入格式為“#,###”(其中#代表數字)

復制
  1. public class ClassName
  2. {
  3. @NumberFormat(pattern="#,###")
  4. private int count;
  5. //setter、getter
  6. }

通過form表單中的input字段來映射count屬性時,合法輸入:如1,234;不合法的輸入:如12,34。

29.3 數據校驗

除了使用JS、正則表達式以外,還可以使用JSR 303-Bean Validation(簡稱JSR 303)來實現數據的校驗。例如:用戶名不能為空,email必須是一個合法地址,某個日期時間必須在當前時間之前等眾多校驗,都可以使用JSR 303-Bean Validation非常方便的實現。

JSR 303通過在實體類的屬性上標注類@NotNull、@Max等注解指定校驗規則,並通過與注解相對應的驗證接口(JSR303內置提供)對屬性值進行驗證。

JSR 303提供的標准注解如下:

注解 簡介
@Null 被注釋的元素必須為 null。
@NotNull 被注釋的元素必須不為 null。
@AssertTrue 被注釋的元素必須為 true。
@AssertFalse 被注釋的元素必須為 false。
@Min(value) 被注釋的元素必須是一個數字,其值必須大於或等於value。
@Max(value) 被注釋的元素必須是一個數字,其值必須小於或等於value。
@DecimalMin(value) 被注釋的元素必須是一個數字,其值必須大於或等於value。
@DecimalMax(value) 被注釋的元素必須是一個數字,其值必須小於或等於value。
@Size(max, min) 被注釋的元素的取值范圍必須是介於min和max之間。
@Digits (integer, fraction) 被注釋的元素必須是一個數字,其值必須在可接受的范圍內。
@Past 被注釋的元素必須是一個過去的日期。
@Future 被注釋的元素必須是一個將來的日期。
@Pattern(value) 被注釋的元素必須符合指定的正則表達式。

Hibernate Validator 是JSR 303的擴展。Hibernate Validator 提供了 JSR 303中所有內置的注解,以及自身擴展的4個注解,如下:

注解 簡介
@Email 被注釋的元素值必須是合法的電子郵箱地址。
@Length 被注釋的字符串的長度必須在指定的范圍內。
@NotEmpty 被注釋的字符串的必須非空。
@Range 被注釋的元素必須在合適的范圍內。

以下是使用Spring整合Hibernate Validator實現數據校驗的步驟:

①導入JAR包:

Spring整合Hibernate Validator共需要導入以下5個JAR包:

hibernate-validator-5.0.0.CR2.jar classmate-0.8.0.jar jboss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar hibernate-validator-annotation-processor-5.0.0.CR2.jar

②加入<mvc:annotation-driven/>

Spring提供了一個LocalValidatorFactoryBean類,這個類既實現了Spring的校驗接口,也實現了JSR303的校驗接口。因此,Spring整合Hibernate Validator時,需要在Spring容器中定義了一個LocalValidatorFactoryBean。方便的是,<mvc:annotation-driven/>就會自動給Spring容器裝配一個LocalValidatorFactoryBean,因此只需要在springmvc.xml中配置上<mvc:annotation-driven/>即可。

③使用JSR303或Hibernate Validator校驗注解,標識實體類的屬性:

本次使用JSR303提供的@Past注解,以及Hibernate Validator提供的@Email注解進行輸入校驗,如下:

Student.java

復制
  1. public class Student
  2. {
  3. @Past
  4. @DateTimeFormat(pattern="yyyy-MM-dd")
  5. private Date birthday ;
  6. @Email
  7. private String email;
  8. //setter、getter
  9. }

規定birthday必須在當天之前、email必須符合郵箱格式。

④在請求處理方法對應的實體類參數前,增加@Valid注解

SpringMVC會對標有@Valid注解的實體類參數進行校驗,並且可以通過BindingResult類型的參數來存儲校驗失敗時的信息,如下:

請求處理類:FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/testValid")
  6. public String testValid(@Valid Student student,
  7. BindingResult result)
  8. {
  9. if (result.getErrorCount() > 0)
  10. {
  11. //循環遍歷所有錯誤信息
  12. for (FieldError error : result.getFieldErrors())
  13. {
  14. System.out.println(error.getField() + ":"
  15. + error.getDefaultMessage());
  16. }
  17. }
  18. return "success";
  19. }
  20. }

⑤測試

index.jsp

復制
  1. <form action="FirstSpringDemo/testValid">
  2. 用戶名:<input type="text" name="stuName"/><br>
  3. 生日:<input type="text" name="birthday"/><br>
  4. 郵箱:<input type="text" name="email"/><br>
  5. <input type="submit" value="提交"/>
  6. </form>

如果輸入的數據不符合要求,如下:

圖29-06

點擊提交后,就會在控制台得到校驗失敗的信息(錯誤信息是JSR303/Hibernate Validator框架提供的,無需開發人員編寫):

圖29-07

如果希望校驗失敗后,跳轉到錯誤提示頁面(error.jsp),可以通過以下方式實現:

請求處理類:FirstSpringDemo.java

復制
  1. @Controller
  2. @RequestMapping(value = "/FirstSpringDemo")
  3. public class FirstSpringDemo
  4. {
  5. @RequestMapping("/testValid")
  6. public String testValid(@Valid Student student,
  7. BindingResult result, Map<String, Object> map)
  8. {
  9. if (result.getErrorCount() > 0)
  10. {
  11. //將錯誤信息通過map放入request作用域之中
  12. map.put ("errors",result.getFieldErrors());
  13. return "error";
  14. }
  15. return "success";
  16. }
  17. }

錯誤提示頁:error.jsp

復制
  1. <c:forEach items="${errors }" var="error">
  2. ${error.getDefaultMessage() }、
  3. </c:forEach>

再次在index.jsp中輸入錯誤的信息(生日2021-11-11,郵箱yanqun),點擊“提交”后得到以下error.jsp頁面:

圖29-08

說明:

需要注意的是:在請求處理方法的參數中,實體類參數和存儲錯誤信息的BindingResult參數必須書寫在一起,它們之間不能摻雜任何其它參數。

例如,可以寫成:

復制
  1. public String testValid(@Valid Student student, BindingResult result, Map<String, Object> map)

但不能寫成:

復制
  1. public String testValid(@Valid Student student, Map<String, Object> map, BindingResult result)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM