1、JSR303数据校验

1) 如何校验

①使用JSR 303验证标准

②加入hibernate validator验证框架

③在SpringMVC配置文件中增加< mvc:annotation-driven />

④需要在bean的属性上增加对应验证的注解

⑤在目标方法bean类型的前面增加@Valid注解,说明该参数需要做数据校验

2) 验证出错后,跳转到哪个页面

3) 错误消息,如何显示,如何国际化

4) JSR 303

是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 中 .

JSR 303 (Java Specification Requests意思是Java 规范提案)通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证
image.png

5) Hibernate Validator 扩展注解

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

6) Spring MVC 数据校验

  • Spring 4.0 拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。

  • Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在 Spring MVC 中,可直接通过注解驱动的方式进行数据校验

  • Spring 的 LocalValidatorFactroyBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在 Spring 容器中定义了一个 LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean 中。

  • Spring 本身并没有提供 JSR303 的实现,所以必须将 JSR303 的实现者的 jar 包放到类路径下

  • < mvc:annotation-driven /> 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @Valid 注解即可让 Spring MVC 在完成数据绑定后执行数据校验的工作

  • 在已经标注了 JSR303 注解的表单/命令对象前标注一个 @Valid,Spring MVC 框架在将请求参数绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验

[

](https://blog.csdn.net/qq_43284469/article/details/111172764)

7) 实验代码

① 添加jar包:

hibernate-validator-5.0.0.CR2\dist

hibernate-validator-5.0.0.CR2.jar

hibernate-validator-annotation-processor-5.0.0.CR2.jar

hibernate-validator-5.0.0.CR2\dist\lib\required (EL就不需要加了)

classmate-0.8.0.jar

jboss-logging-3.1.1.GA.jar

validation-api-1.1.0.CR1.jar

② 在验证属性上增加验证注解

  1. public class Employee {
  2. private Integer id;
  3. @NotEmpty
  4. private String lastName;
  5. @Email
  6. private String email;
  7. //1 male, 0 female
  8. private Integer gender;
  9. private Department department;
  10. //关于类型转换
  11. @Past //被标注的日期必须是一个过去的日期
  12. @DateTimeFormat(pattern="yyyy-MM-dd")
  13. private Date birthDay ;
  14. @NumberFormat(pattern="#,###,###.#")
  15. private double salary ;
  16. }

③ 增加

  1. //添加员工
  2. /** 增加@Valid注解,验证失败会报错,说明该参数需要做数据校验。
  3. * 严重: Servlet.service() for servlet springDispatcherServlet threw exception
  4. java.lang.NoSuchMethodError: javax.el.ExpressionFactory.newInstance()Ljavax/el/ExpressionFactory;
  5. */
  6. //BindingResult保存了前一个数据校验参数的错误信息,必须跟在数据校验参数的后面
  7. @RequestMapping(value="/empAdd",method=RequestMethod.POST)
  8. public String empAdd(@Valid Employee employee,BindingResult bindingResult){
  9. System.out.println("empAdd - employee="+employee);
  10. if(bindingResult.getErrorCount() > 0 ){
  11. System.out.println("类型转换出错误了");
  12. List<FieldError> fieldErrors = bindingResult.getFieldErrors();
  13. for(FieldError fieldError : fieldErrors){
  14. System.out.println(fieldError.getField() + " - " + fieldError.getDefaultMessage());
  15. }
  16. }
  17. employeeDao.save(employee);
  18. return "redirect:/empList";
  19. }

④ 后台打印错误消息
image.png
⑤ 前台打印错误消息
image.png
⑥ 测试验证,解决EL表达式错误

拷贝hibernate-validator-5.0.0.CR2\dist\lib\required目录下的

el-api-2.2.jar、javax.el-2.2.4.jar、javax.el-api-2.2.4.jar

三个包到Tomcat/lib目录下,将原来的el-api.jar删除。重启tomcat6

⑦ 如果希望验证失败,回到添加页面

  1. @RequestMapping(value="/empAdd",method=RequestMethod.POST)
  2. public String empAdd(@Valid Employee employee,BindingResult bindingResult){
  3. System.out.println("empAdd - employee="+employee);
  4. if(bindingResult.getErrorCount() > 0 ){
  5. System.out.println("类型转换出错误了");
  6. List<FieldError> fieldErrors = bindingResult.getFieldErrors();
  7. for(FieldError fieldError : fieldErrors){
  8. System.out.println(fieldError.getField() + " - " + fieldError.getDefaultMessage());
  9. }
  10. map.put("deptList",departmentDao.getDepartments());
  11. return "add"; // /WEB-INF/views/add.jsp
  12. }
  13. employeeDao.save(employee);
  14. return "redirect:/empList";
  15. }

⑧ public interface BindingResult extends Errors

Spring MVC 是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是 BindingResult 或 Errors 类型,这两个类都位于 org.springframework.validation 包中

需校验的 Bean 对象和其绑定结果对象或错误对象是成对出现的,它们之间不允许声明其他的入参

Errors 接口提供了获取错误信息的方法,如 getErrorCount() 或 getFieldErrors(String field)

BindingResult 扩展了 Errors 接口
image.png

2、错误消息的显示及国际化

1) 在页面上显示错误

Spring MVC 除了会将表单/命令对象的校验结果保存到对应的 BindingResult 或 Errors 对象中外,还会将所有校验结果保存到 “隐含模型”

即使处理方法的签名中没有对应于表单/命令对象的结果入参,校验结果也会保存在 “隐含对象” 中。

隐含模型中的所有数据最终将通过 HttpServletRequest 的属性列表暴露给 JSP 视图对象,因此在 JSP 中可以获取错误信息

在 JSP 页面上可通过 显示错误消息

2) 示例:

① 在表单上页面上显示所有的错误消息

  1. <!-- 显示所有的错误消息 -->
  2. <form:errors path="*"/>

② 显示某一个表单域的错误消息

<form:errors  path="lastName"/>

③ 有错,回到add.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="java.util.*"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<!-- 
1.为什么使用SpringMVC的form标签
① 快速开发
② 表单回显
2.可以通过modelAttribute指定绑定的模型属性,
若没有指定该属性,则默认从request域中查找command的表单的bean
如果该属性也不存在,那么,则会发生错误。
 -->
 <form:form action="empAdd" method="POST" modelAttribute="employee">
         <!-- 显示所有的错误消息 --><form:errors path="*"/><br><br>

         LastName : <form:input path="lastName" /> <form:errors path="lastName"/> <br><br>

         Email : <form:input path="email" /><form:errors path="email"/><br><br>
         <%
                 Map<String,String> map = new HashMap<String,String>();
                 map.put("1", "Male");
                 map.put("0","Female");
                 request.setAttribute("genders", map);
         %>
         Gender : <form:radiobuttons path="gender" items="${genders}" delimiter="<br>"/>
         DeptName : 
                 <form:select path="department.id" 
                                                 items="${deptList }" 
                                                 itemLabel="departmentName" 
                                                 itemValue="id"></form:select><br><br>
         <!-- 解决问题:
                 1.数据类型转换
                 2.数据格式
                 3.数据校验
                         1)-如何校验
                                 验证格式,需要个注解就可以了
                                 ①使用JSR 303验证标准
                                 ②加入hibernate validator验证框架
                                 ③在SpringMVC配置文件中增加<mvc:annotation-driven/>
                                 ④需要在bean的属性上增加对应验证的注解
                                 ⑤在目标方法bean类型的前面增加@Valid注解
                         2)-验证出错后,跳转到哪个页面
                         3)错误消息,如何显示,如何国际化                 

                 自定义类型转换器:
                         将字符串转换为Employee对象,完成添加功能 
         -->
BirthDay :<%-- <input type="text" name="birthDay"/> --%>        
<form:input path="birthDay"/><form:errors path="birthDay"/><br><br>
Salary : <form:input path="salary"/><br><br>
            <input type="submit" value="Submit"><br><br>
 </form:form> 
</body>
</html>

3、 提示消息的国际化

  • 每个属性在数据绑定和数据校验发生错误时,都会生成一个对应的 FieldError 对象。

  • 当一个属性校验失败后,校验框架会为该属性生成 4 个消息代码,这些代码以校验注解类名为前缀,结合 modleAttribute、属性名及属性类型名生成多个对应的消息代码:例如 User 类中的 password 属性标注了一个 @Pattern 注解,当该属性值不满足 @Pattern 所定义的规则时, 就会产生以下 4 个错误代码:

Pattern.user.password
Pattern.password
Pattern.java.lang.String
Pattern

  • 当使用 Spring MVC 标签显示错误消息时, Spring MVC 会查看 WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认的错误消息,否则使用国际化消息。

  • 若数据类型转换或数据格式转换时发生错误,或该有的参数不存在,或调用处理方法时发生错误,都会在隐含模型中创建错误消息。其错误代码前缀说明如下:

required:必要的参数不存在。如 @RequiredParam(“param1”) 标注了一个入参,但是该参数不存在
typeMismatch:在数据绑定时,发生数据类型不匹配的问题
methodInvocation:Spring MVC 在调用处理方法时发生了错误

  • 注册国际化资源文件

image.png

4、提示消息的国际化实验

① 定义国际化资源文件:i18n.propertie

NotEmpty.employee.lastName=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
Email.employee.email=\u7535\u5B50\u90AE\u4EF6\u5730\u5740\u4E0D\u5408\u6CD5
Past.employee.birthDay=\u65E5\u671F\u5FC5\u987B\u662F\u4E00\u4E2A\u8FC7\u53BB\u7684\u65F6\u95F4
typeMismatch.employee.birthDay=\u4E0D\u662F\u4E00\u4E2A\u65E5\u671F\u6709\u6548\u683C\u5F0F

② 声明国际化资源配置

<!-- 声明国际化资源文件 -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="i18n"></property>
</bean>

5、返回JSON

1) 处理 JSON
① 加入 jar 包:
http://wiki.fasterxml.com/JacksonDownload/ 下载地址
jackson-annotations-2.1.5.jar
jackson-core-2.1.5.jar
jackson-databind-2.1.5.jar

② 编写目标方法,使其返回 JSON 对应的对象或集合

@ResponseBody  //SpringMVC对JSON的支持
@RequestMapping("/testJSON")
public Collection<Employee> testJSON(){
return employeeDao.getAll();
}

③ 增加页面代码:index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(function(){ 
$("#testJSON").click(function(){

var url = this.href ;
var args = {};
$.post(url,args,function(data){
for(var i=0; i<data.length; i++){
var id = data[i].id;
var lastName = data[i].lastName ;
alert(id+" - " + lastName);
}
});

return false ;
});                
});
</script>

</head>
<body>

<a href="empList">To Employee List</a>
<br><br>

<a id="testJSON" href="testJSON">testJSON</a>

</body>
</html>

④ 测试
image.png

6、HttpMessageConverter原理

① HttpMessageConverter< T > 是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为 T),将对象(类型为 T)输出为响应信息

HttpMessageConverter< T >接口定义的方法:

Boolean canRead(Class<?> clazz,MediaType mediaType): 指定转换器可以读取的对象类型,即转换器是否可将请求信息转换为 clazz 类型的对象,同时指定支持 MIME 类型(text/html,applaiction/json等)

Boolean canWrite(Class<?> clazz,MediaType mediaType):指定转换器是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型在MediaType 中定义。

List getSupportMediaTypes():该转换器支持的媒体类型。

Tread(Class<? extends T> clazz,HttpInputMessage inputMessage):将请求信息流转换为 T 类型的对象。

void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage):将T类型的对象写到响应流中,同时指定相应的媒体类型为 contentType。
[

](https://blog.csdn.net/qq_43284469/article/details/111172764)
image.png
image.png
image.png
③ DispatcherServlet 默认装配 RequestMappingHandlerAdapter ,
而 RequestMappingHandlerAdapter 默认装配如下 HttpMessageConverter:

image.png
④ 加入 jackson jar 包后, RequestMappingHandlerAdapter
装配的 HttpMessageConverter 如下:

image.png
默认情况下数组长度是6个;增加了jackson的包,后多个一个

MappingJackson2HttpMessageConverter

7、使用HttpMessageConverter

  • 使用 HttpMessageConverter 将请求信息转化并绑定到处理方法的入参中或将响应结果转为对应类型的响应信息,Spring 提供了两种途径:

  • 使用 @RequestBody / @ResponseBody 对处理方法进行标注

  • 使用 HttpEntity / ResponseEntity 作为处理方法的入参或返回值

  • 当控制器处理方法使用到 @RequestBody/@ResponseBody 或 HttpEntity/ResponseEntity 时, Spring 首先根据请求头或响应头的 Accept 属性选择匹配的 HttpMessageConverter, 进而根据参数类型或泛型类型的过滤得到匹配的 HttpMessageConverter, 若找不到可用的 HttpMessageConverter 将报错

  • @RequestBody 和 @ResponseBody 不需要成对出现

  • Content-Disposition:attachment; filename=abc.pdf

image.png
image.png
① 实验代码

<form action="testHttpMessageConverter" method="post" enctype="multipart/form-data">
文件: <input type="file" name="file"/><br><br>
描述: <input type="text" name="desc"/><br><br>
<input type="submit" value="提交"/>
</form>
@ResponseBody  //@ResponseBody:是将内容或对象作为Http响应正文返回
@RequestMapping("/testHttpMessageConverter")
//@RequestBody:是将Http请求正文插入方法中,修饰目标方法的入参
public String testHttpMessageConverter(@RequestBody String body){
System.out.println("body="+body);
return "Hello," + new Date();  //不再查找跳转的页面}

image.png
② 实验代码
/files/abc.txt 准备一个下载的文件

<a href="testResponseEntity">abc.txt</a>
@RequestMapping("testResponseEntity")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException{

ServletContext servletContext = session.getServletContext();
InputStream resourceAsStream = servletContext.getResourceAsStream("/files/abc.txt");
byte[] body = new byte[resourceAsStream.available()] ;
resourceAsStream.read(body);

MultiValueMap<String, String> headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment;filename=abc.txt");

HttpStatus statusCode = HttpStatus.OK;

ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(body, headers, statusCode);

return responseEntity ;
}

image.png
③ 源码参考

  • HttpHeaders
  • HttpStatus