第九章 数据绑定流程分析2ajax
InitBinder注解
@InitBinder
- 由 @InitBinder 标识的方法,可以对 WebDataBinder 对象进行初始化。WebDataBinder 是 DataBinder 的子类,用于完成由表单字段到 JavaBean 属性的绑定
- @InitBinder方法不能有返回值,它必须声明为void。
- @InitBinder方法的参数通常是 WebDataBinder
/** 由 @InitBinder 标识的方法,可以对 WebDataBinder 对象进行初始化。 WebDataBinder 是 DataBinder 的子类,用于完成由表单字段到 JavaBean 属性的绑定 @InitBinder方法不能有返回值,它必须声明为void。 @InitBinder方法的参数通常是 WebDataBinder */ @InitBinder public void initBinder(WebDataBinder dataBinder){ dataBinder.setDisallowedFields("lastName"); }
数据的格式化
数据格式化概述
- 对属性对象的输入/输出进行格式化,从其本质上讲依然属于 “类型转换” 的范畴。
- Spring 在格式化模块中定义了一个实现 ConversionService 接口的
FormattingConversionService 实现类,该实现类扩展了 GenericConversionService,因此它既具有类型转换的功能,又具有格式化的功能 - FormattingConversionService 拥有一个FormattingConversionServiceFactroyBean 工厂类,后者用于在 Spring 上下文中构造前者,FormattingConversionServiceFactroyBean 内部已经注册了 :
- NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性使用
@NumberFormat 注解 - JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型的属性使用 @DateTimeFormat 注解
- 装配了 FormattingConversionServiceFactroyBean 后,就可以在 Spring MVC 入参绑定及模型数据输出时使用注解驱动了。
- 《mvc:annotation-driven/> 默认创建的 ConversionService 实例即为
DefaultFormattingConversionService
日期格式化概述
- @DateTimeFormat 注解可对 java.util.Date、java.util.Calendar、java.long.Long 时间类型进行标注:
- pattern 属性:类型为字符串。指定解析/格式化字段数据的模式,如:”yyyy-MM-dd hh:mm:ss”
- iso 属性:类型为 DateTimeFormat.ISO。指定解析/格式化字段数据的ISO模式,包括四种:ISO.NONE(不使用) -- 默认、ISO.DATE(yyyy-MM-dd) 、ISO.TIME(hh:mm:ss.SSSZ)、 ISO.DATE_TIME(yyyy-MM-dd hh:mm:ss.SSSZ)
- style 属性:字符串类型。通过样式指定日期时间的格式,由两位字符组成,第一位表示日期的格式,第二位表示时间的格式:S:短日期/时间格式、M:中日期/时间格式、L:长日期/时间格式、F:完整日期/时间格式、-:忽略日期或时间格式
我们新添加了时间的字段
public class Book { private String bookName; private String id; private Date birth; } 《td>${book.id}《/td> 《td>${book.bookName}《/td> 《td>${book.birth}《/td> 提交 《form action="book" method="post"> 书名:《input name="bookName"/> 书id:《input name="id"/> 书出版日期:《input name="birth"/> 《input type="submit" value="提交"/> 《/form>
修改了springmvc配置文件的类
FormattingConversionServiceFactoryBean该类不但有转化器集合,也添加了格式集合 默认是没有了【国际标准/:2017/02/11】 既有类型转换 也有格式化 <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.project.component.MyConverterFunc"/> </set> </property> </bean> 格式通过注解自动赋值到容器中 @DateTimeFormat(pattern = "yyyy-MM-dd") private Date birth;
效果
数值格式化概述
- @NumberFormat 可对类似数字类型的属性进行标注,它拥有两个互斥的属性:
- style:类型为 NumberFormat.Style。用于指定样式类型,包括三种:Style.NUMBER(正常数字类型)、 Style.CURRENCY(货币类型)、 Style.PERCENT(百分数类型)
- pattern:类型为 String,自定义样式,如pattern="#,###";
@NumberFormat(pattern = "¥#,###.###") private Double money;
如果类型转换失败,如何获取错误消息
(后台获取错误消息,并打印)
//添加员工 @RequestMapping(value="/empAdd",method=RequestMethod.POST) public String empAdd(Employee employee,BindingResult bindingResult){ System.out.println("empAdd - employee="+employee); if(bindingResult.getErrorCount() > 0 ){ System.out.println("类型转换处错误了"); List<FieldError> fieldErrors = bindingResult.getFieldErrors(); for(FieldError fieldError : fieldErrors){ System.out.println(fieldError.getField() + " - " + fieldError.getDefaultMessage()); } } employeeDao.save(employee); return "redirect:/empList"; }
校验
有一万种方法绕过前端严重 copy绝对路径自己写一个页面 发起请求
前端验证是不安全的 在重要数据要增加后端验证 校验失败直接重写
- 可以写程序将我们每一个数据取出进行校验,如果失败直接来到添加页面,提示其重新填写 pass
- springmvc:可以jsr303【标准体验】 来做数据校验
如何校验
- 使用JSR 303验证标准
- 加入hibernate validator验证框架
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.1.0.Final</version> </dependency> tomcat里面有包了 <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.el</artifactId> <version>3.0.1-b11</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator-cdi</artifactId> <version>6.1.0.Final</version> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency>
- tomcat 有自己的el包 不用自己额外导入【tomcat版本>=7】
- 在SpringMVC配置文件中增加《mvc:annotation-driven/>
- 需要在bean的属性上增加对应验证的注解
- 在目标方法bean类型的前面增加@Valid注解
JSR 303
是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 中 .
JSR 303 (Java Specification Requests意思是Java 规范提案)通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证
Hibernate Validator 扩展注解
Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解
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 框架在将请求参数绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验
数据有误
- Spring MVC 是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是 BindingResult 或 Errors 类型,这两个类都位于 org.springframework.validation 包中
- 需校验的 Bean 对象和其绑定结果对象或错误对象是成对出现的,它们之间不允许声明其他的入参
- Errors 接口提供了获取错误信息的方法,如 getErrorCount() 或 getFieldErrors(String field)
- BindingResult 扩展了 Errors 接口
在页面上显示错误
Spring MVC 除了会将表单/命令对象的校验结果保存到对应的 BindingResult 或 Errors 对象中外,还会将所有校验结果保存到 “隐含模型”
即使处理方法的签名中没有对应于表单/命令对象的结果入参,校验结果也会保存在 “隐含对象” 中。
隐含模型中的所有数据最终将通过 HttpServletRequest 的属性列表暴露给 JSP 视图对象,因此在 JSP 中可以获取错误信息
在 JSP 页面上可通过 《form:errors path=“userName”> 显示错误消息
可以通过modelAttribute指定绑定的模型属性,
若没有指定该属性,则默认从request域中查找command的表单的bean
如果该属性也不存在,那么,则会发生错误。
一个早上无法回显一直无法回显《% pageContext.setAttribute("ctp", request.getContextPath()); System.out.println(request.getContextPath()); %> 《form:form action="${ctp}/book" method="post" modelAttribute="book"> {book类名 而不是自己定义的bookmodel等} 书名:《form:input path="bookName"/>《form:errors path="bookName"/>《br> 书id:《form:input path="id"/>《form:errors path="id"/>《br> 书出版日期:《form:input path="birth"/>《form:errors path="birth"/>《br> 《input type="submit" value="提交"/> 《/form:form>
控制器的代码
@RequestMapping(value = "/book", method = RequestMethod.POST) public String setBook(@Valid Book book, BindingResult errors) {// 两个参数之间别有其他参数 System.out.println("book校验"); System.out.println(book); System.out.println(errors.getErrorCount()); // if (errors.getErrorCount() > 0) { // System.out.println("类型转换出错误了"); // List<FieldError> fieldErrors = errors.getFieldErrors(); // for (FieldError fieldError : fieldErrors) { // System.out.println(fieldError.getField() + " - " + fieldError.getDefaultMessage()); // } // } if (!errors.hasErrors()) { System.out.println(book); bookDao.addBook(book.getBookName(), Integer.parseInt(book.getId())); return "redirect:/book"; } else { System.out.println("校验有误"); return "hello"; } }
类名
public class Book { @NotNull @Length(min=10,max=16) private String bookName; @NotNull private String id; @Past @NotNull @DateTimeFormat(pattern = "yyyy-MM-dd") private Date birth; } 没有配置springmvc.xml 但是还是放在笔记里 以后可以看 <!-- 可以告诉springmvc使用我们自己配置的类型转换组件 validator="validator" --> <mvc:annotation-driven conversion-service="conversionService" /> <!-- 校验器 --> <!-- <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">--> <!-- Hibernate校验器 --> <!-- <property name="providerClass" value="org.hibernate.validator.HibernateValidator" />--> <!-- </bean> -->
原生的表单怎么办
《form:errors path="bookName"/> --等效--> ${errors.bookName} Model mv; HashMap hm = new HashMap(); mv.addAttribute("errorInfo", hm);
错误消息 --> 定制自己信息
提示消息的国际化
- 每个属性在数据绑定和数据校验发生错误时,都会生成一个对应的 FieldError 对象。错误信息:codes[Length.book.bookName【校验规则.隐含模型中这个对象.这个对象的key】,Length.bookName【校验规则.属性】,Length.java.lang.String,Length【校验规则】] 四种代码
每一个字段发生错误以后,都会有自己的错误代码;国际化文件种错误消息的key必须对应一个错误代码
创建两个文件errors_zh_CN.properties Length=\u9519\u8BEF NotNull=\u9519\u8BEF Past=\u9519\u8BEF errors_en_US.properties Length=false NotNull=false Past=false 取参数 @length(min=5,max=17) 属性名{0} 属性值{1} 属性值{2} 顺序是按字母大小来写
- 当一个属性校验失败后,校验框架会为该属性生成 4 个消息代码,这些代码以校验注解类名为前缀,结合 modleAttribute、属性名及属性类型名生成多个对应的消息代码:例如 User 类中的 password 属性标注了一个 @Pattern 注解,当该属性值不满足 @Pattern 所定义的规则时, 就会产生以下 4 个错误代码:
- Pattern.user.password 精确程度最高 若errors_en_US多个同类型 则找最精准
- Pattern.password 次
- Pattern.java.lang.String 差
- Pattern 最差
- 当使用 Spring MVC 标签显示错误消息时, Spring MVC 会查看 WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认的错误消息,否则使用国际化消息。
- 若数据类型转换或数据格式转换时发生错误,或该有的参数不存在,或调用处理方法时发生错误,都会在隐含模型中创建错误消息。其错误代码前缀说明如下:
- required:必要的参数不存在。如 @RequiredParam(“param1”) 标注了一个入参,但是该参数不存在
- typeMismatch:在数据绑定时,发生数据类型不匹配的问题
- methodInvocation:Spring MVC 在调用处理方法时发生了错误
- 注册国际化资源文件
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="errors"></property> </bean>
不能国际化 可以直接暴力 @NotNull(message="不能为空") @length(message="长度不一样")
返回JSON
Springmvc快速的完成ajax 功能
- 返回数据是json就ok
- 页面。$.ajax()
原生javaWeb
- 导入GSON
- 返回的数据用GSON转成json
- 写出去
处理 JSON
导包
编写目标方法,使其返回 JSON 对应的对象或集合
@Controller public class MyController { @Autowired private BookDao bookDao; /** * 将返回的数据放在响应体中 * 如果是对象,jackson包自动将数据对象转为json格式 * @return */ @ResponseBody @RequestMapping(value = "/bookAll") public Collection<Book> getAllBookHandle() { Collection<Book> all = bookDao.getAll(); return all; } }
字段隐藏
private String bookName; //输出json数据忽略该字段 @JsonIgnore private String id; @JsonFormat(pattern = "yyyy-MM-dd") private Date brith = new Date();
页面发送ajax请求获取数据:https://zhidao.baidu.com/question/543799596.html
《script src="http://code.jquery.com/jquery-latest.js">《/script> 《script type="text/javascript"> $("a:first").click(function(){ $.ajax({ url:"${ctp}/bookAll", type:"GET", success:function(data){ console.log(data); $.each(data,function(){ var empInfo = this.bookName+"-->"+this.id $("div").append(enInfo+"<br>"); }) } }); return false; }) 《/script> @ResponseBody @RequestMapping(value = "/bookAll") public Collection<Book> getAllBookHandle() { Collection<Book> all = bookDao.getAll(); return all; }
请求体后台信息
《form action="${ctp}/bookAll" method="post" enctype="multipart/form-data"> 《input type="text" name="id"> 《input type="text" name="bookName"> 《input type="file" name="file"> 《input type="submit" value="提交"> 《/form> @ResponseBody @RequestMapping(value ="/bookAll") public void getAllBookHandle(@RequestBody String body) { System.out.println(body); //id=1&bookName=a }
多文件上传的请求体效果截图
利用ajax发json 数据到后台 并封装成一个目标对象
《script src="http://code.jquery.com/jquery-latest.js">《/script> 《script type="text/javascript"> $("a:first").click(function(){ var book = {id:"101",bookName:"计算机网络"}; var bookStr = JSON.stringify(book); $.ajax({ url:"${ctp}/bookAll", type:"POST", data:bookStr, contentType : "application/json;",//需要表明 success:function(data){ alert(data); } }); return false; }) 《/script> @ResponseBody @RequestMapping(value ="/bookAll") public String getAllBookHandle(@RequestBody Book book) { System.out.println(book); return "hello"; }
@ResponseBody 返回json数据 可以把对象转为json数据,返回给浏览器 将返回的数据放到响应体中 如return "success" 是把success 放到响应体中 而不是转发到success页面 需要将注解打上 return "《h1>success《/h1>"
@RequestBody 接收json数据 封装为对象
使用HttpMessageConverter
HttpEntity 可以过的所有的请求头的信息 如下
功能比@RequestHeader更强大
@RequestMapping(value ="/bookAll") public String getAllBookHandle(HttpEntity<String> set) { System.out.println(set); return "hello"; }
{host=[localhost:8080], connection=[keep-alive], content-length=[41], accept=[/], sec-fetch-dest=[empty], x-requested-with=[XMLHttpRequest], user-agent=[Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36], origin=[http://localhost:8080], sec-fetch-site=[same-origin], sec-fetch-mode=[cors], referer=[http://localhost:8080/ssmDemoTest/hello], accept-encoding=[gzip, deflate, br], accept-language=[zh-CN,zh;q=0.9,en;q=0.8], cookie=[JSESSIONID=D4E08B6D7EA8A7350515A7DB9E593B29], Content-Type=[application/json;charset=UTF-8]}>
使用 HttpMessageConverter<t> 将请求信息转化并绑定到处理方法的入参中或将响应结果转为对应类型的响应信息,Spring 提供了两种途径:</t>
- 使用 @RequestBody / @ResponseBody 对处理方法进行标注
- 使用 HttpEntity<t> / ResponseEntity<t> 作为处理方法的入参或返回值</t></t>
当控制器处理方法使用到 @RequestBody/@ResponseBody 或
HttpEntity<t>/ResponseEntity<t> 时, Spring 首先根据请求头或响应头的 Accept 属性选择匹配的 HttpMessageConverter, 进而根据参数类型或泛型类型的过滤得到匹配的 HttpMessageConverter, 若找不到可用的 HttpMessageConverter 将报错</t></t>@RequestBody 和 @ResponseBody 不需要成对出现
Content-Disposition:attachment; filename=abc.pdfResponseEntity
@Controller public class MyController { @RequestMapping(value ="/bookAll") public ResponseEntity<String> getAllBookHandle() { MultiValueMap<String, String> headers = new HttpHeaders(); headers.add("Set-Cookie", "name=cznzcai"); String body = "<h1>hello,world</h1>"; return new ResponseEntity<String>(body, headers, HttpStatus.OK); }
ServletContext介绍及用法
https://blog.csdn.net/qq_36371449/article/details/80314024
文件下载的头部
https://www.jianshu.com/p/4c52cb691f54
https://blog.csdn.net/github_38885296/article/details/81205684
两种文件头分别影响对应的下载效果
@RequestMapping(value ="/download") public ResponseEntity<byte[]> downloadFile(HttpServletRequest request) throws IOException { MultiValueMap<String, String> headers = new HttpHeaders(); ServletContext hc = request.getServletContext(); String realpath = hc.getRealPath("/js/jquery-1.9.1.min.js"); FileInputStream fis = new FileInputStream(realpath); byte[] b = new byte[fis.available()]; fis.read(b ); fis.close(); //headers.add("Content-Type", "application/octet-stream"); headers.add("Content-Disposition", "attachment;filename="+"jquery-1.9.1.min.js"); return new ResponseEntity<byte[]>(b,headers , HttpStatus.OK); }
HttpMessageConverter原理
HttpMessageConverter
- HttpMessageConverter<t> 是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为 T),将对象(类型为 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<mediatype> getSupportMediaTypes():该转换器支持的媒体类型。</mediatype>
- T read(Class<? extends T> clazz,HttpInputMessage inputMessage):将请求信息流转换为 T 类型的对象。
- void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage):将T类型的对象写到响应流中,同时指定相应的媒体类型为 contentType [参数都是接口]
package org.springframework.http; import java.io.IOException; import java.io.InputStream; public interface HttpInputMessage extends HttpMessage { InputStream getBody() throws IOException; } package org.springframework.http; import java.io.IOException; import java.io.OutputStream; public interface HttpOutputMessage extends HttpMessage { OutputStream getBody() throws IOException; }
- DispatcherServlet 默认装配 RequestMappingHandlerAdapter ,
而 RequestMappingHandlerAdapter 默认装配如下 HttpMessageConverter: - 加入 jackson jar 包后, RequestMappingHandlerAdapter
装配的 HttpMessageConverter 如下: - 默认情况下数组长度是6个;增加了jackson的包,后多个一个
MappingJackson2HttpMessageConverter