目录
一、前言
二、目录结构
三、数据格式化
四、数据验证
五、数据格式化、验证梳理图
六、数据格式化、验证梳理图
相关文章
【SpringMVC】入门篇:带你了解SpringMVC的执行流程 | 【SpringMVC】入门篇:带你了解SpringMVC的执行流程 |
【SpringMVC】使用篇:SpringMVC的开始 | 【SpringMVC】使用篇:SpringMVC的开始 |
【SpringMVC】SpringMVC模型数据+视图解析器 | 【SpringMVC】SpringMVC模型数据+视图解析器 |
一、前言
在上一节的内容中,我们着重了解了SpringMVC的视图解析器。理清了一个请求从发送到响应的整个大致流程,最主要的是在Controller
中为什么可以通过return
一个字符串,就可以跳转到响应的页面。
这节我们来了解一下SpingMVC其余的一些内容。在此之前,我们先提出一个需求:当我们需要对提交数据的类型进行限制的时候,应该如何来处理❓
✅利用之前所学的知识 ,我们可以通过前端js
来做限制,也可以通过在后端Servlet中根据需求来做一些数据的转化工作。以上这些,虽然都可以完成我们的工作但很明显的一个问题就是:我们需要知道这个数据到底需要哪些类型的数据。
✅基于以上的问题SpringMVC给我们提供了一个解决方案:在后端定义数据类型的时候,通过注解直接来指定我们的数据类型(类型的自动转换)或者说是对数据做一些限制,这种方式使得我们业务逻辑更加的清晰。
二、目录结构
三、数据格式化
1. 普通类型的转换
前端
<%--
Created by IntelliJ IDEA.
User: huawei
Date: 2022/11/28
Time: 18:56
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SpringMVC数据格式化</title>
</head>
<body>
<h1>SpringMVC数据格式化</h1>
<hr>
<a href="<%=request.getContextPath()%>/addMonsterUI">添加妖怪</a>
</body>
</html>
<h3>添加妖怪</h3>
<form action="save">
妖怪名字:<input type="text" name="name"/><br/>
妖怪年龄:<input type="text" name="age"/><br/>
妖怪邮箱:<input type="text" name="email"/><br/>
<input type="submit" value="添加妖怪">
</form>
后端 Montser类
package com.jl.web.datavild.entity;
import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;
import javax.validation.constraints.NotNull;
import java.util.Date;
/**
* ClassName: Monster
* Package: com.jl.web.datavild.entity
* Description: 数据转换测试需要用到的类
*
* @Author: Long
* @Create: 2022/11/28 - 18:54
* @Version: v1.0
*/
public class Monster {
private String name;
private Integer age;
private String email;
public Monster() {
}
public Monster(String name, Integer age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Monster{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
在SpringMVC的使用
那一章节中,我们提到了请求映射数据:SpringMVC可以将前端提交的数据,封装成对象。在这个例子中:我们看一下不同情况下的响应结果。
- 前端年龄
age
为非数字时:
- 前端年龄
age
为数字时:
通过上边的这个示例,我们可以看到:当前端数据为字符串的时候,SpringMVC可以将根据需要转换的类型进行自动转换,但当转换失败的时候(例如:非数字无法转换成数数字),会返回400错误
。
2. 特殊类型的转换
除了字符串转换为数字之外,我们在使用中也会遇到很多其他特殊类型的数据格式需要进行转换。
下边我们演示两种特使格式的转化(格式化):
前端
<h3>添加妖怪</h3>
<form action="save1">
妖怪名字:<input type="text" name="name"/><br/>
妖怪年龄:<input type="text" name="age"/><br/>
妖怪邮箱:<input type="text" name="email"/><br/>
生日:<input type="text" name="birthday"/>格式:"2000-11-11"<br/>
薪水:<input type="text" name="salary"/>格式:"666,666.66"<br/>
<input type="submit" value="添加妖怪">
</form>
后端
package com.jl.web.datavild.entity;
import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;
import javax.validation.constraints.NotNull;
import java.util.Date;
/**
* ClassName: Monster
* Package: com.jl.web.datavild.entity
* Description: 数据转换测试需要用到的类
*
* @Author: Long
* @Create: 2022/11/28 - 18:54
* @Version: v1.0
*/
public class Monster {
private String name;
private Integer age;
private String email;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@NumberFormat(pattern = "###,###.##")
private Float salary;
public Monster(String name, Integer age, String email, Date birthday, Float salary) {
this.name = name;
this.age = age;
this.email = email;
this.birthday = birthday;
this.salary = salary;
}
public Monster() {
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Float getSalary() {
return salary;
}
public void setSalary(Float salary) {
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Monster{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
", birthday=" + birthday +
", salary=" + salary +
'}';
}
- 生日和薪水格式都正确的情况:
- 生日和薪水格式有误的情况:
从上边这些我们都可以看出,当我们的数据转换出现问题的时候,都会返回400错误
,而这种回显方式显然是很不合理的,下边我们会给出解决方案。
四、数据验证
数据验证需要的jar包。
数据验证实际上就是我们有的场景下需要对一些数据做一些限制,例如:不为空,数值范围做一些限制等…
前端
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="from" uri="http://www.springframework.org/tags/form" %>
<%--
Created by IntelliJ IDEA.
User: huawei
Date: 2022/11/28
Time: 18:57
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>添加妖怪页面</title>
</head>
<body>
<h3>添加妖怪</h3>
<form:form action="save" modelAttribute="monster">
妖怪名字:<from:input path="name"/><form:errors path="name"/><br/>
妖怪年龄:<from:input path="age"/><form:errors path="age"/><br/>
妖怪邮箱:<from:input path="email"/><form:errors path="email"/><br/>
生日:<form:input path="birthday"/><form:errors path="birthday"/><br/>
薪水:<form:input path="salary"/><form:errors path="salary"/><br/>
<input type="submit" value="添加妖怪">
</form:form>
</body>
</html>
在这个表单中,我们使用了<form:form>
这个标签,这是SpringMVC提供的一个标签。它的使用主要注意以下几点:
- SpringMVC 表单标签在显示之前必须在
request
中有一个 bean, 该 bean 的属性和表单标签的字段要对应。 - request 中的 key 为: form 标签的
modelAttrite
属性值, 比如这里的 monsters。
在上边的前端代码中,我们使用了SpringMVC提供的标签,这是为了方便于回显我们的数据。也就是当出现数据转换错误,或者数据验证错误的时候,不会回显我们的400错误
,而是回显提示信息
。
后端
package com.jl.web.datavild.entity;
import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;
import javax.validation.constraints.NotNull;
import java.util.Date;
/**
* ClassName: Monster
* Package: com.jl.web.datavild.entity
* Description: 数据转换测试需要用到的类
*
* @Author: Long
* @Create: 2022/11/28 - 18:54
* @Version: v1.0
*/
public class Monster {
private Integer id;
// 不能为空(null),也不能为empty
// Asserts that the annotated string, collection, map or array is not {@code null} or empty.
@NotEmpty
private String name;
// 我们的注解可以配合使用。但需要注意的是:NotNull和NotEmpty是有所区别的,NotNull支持所有的类型
@NotNull(message = "age不能为空")
// 指定数值的范围,默认最小值是0,最大值是Long的最大值(Long.MAX_VALUE)
@Range(min = 1,max=100)
private Integer age;
@NotEmpty(message = "邮箱不能为空")
private String email;
@NotNull(message = "生日不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@NotNull(message = "薪水不能为空")
@NumberFormat(pattern = "###,###.##")
private Float salary;
public Monster(Integer id, String name, Integer age, String email, Date birthday, Float salary) {
this.id = id;
this.name = name;
this.age = age;
this.email = email;
this.birthday = birthday;
this.salary = salary;
}
public Monster() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Float getSalary() {
return salary;
}
public void setSalary(Float salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Monster{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
", birthday=" + birthday +
", salary=" + salary +
'}';
}
}
package com.jl.web.datavild;
import com.jl.web.datavild.entity.Monster;
import org.springframework.context.annotation.Scope;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.validation.Errors;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
/**
* ClassName: MonsterHandler
* Package: com.jl.web.datavild
* Description: 用于演示数据转换/验证
*
* @Author: Long
* @Create: 2022/11/28 - 19:12
* @Version: v1.0
*/
@Controller
@Scope(value = "prototype")
public class MonsterHandler {
/**
* 当我们在map存放数据的时候,他会存放到request
* @param map
* @return
*/
// 显示添加monster的页面
@RequestMapping(value = "/addMonsterUI")
public String addMonster(Map<String,Object> map){
// 如果使用了SpringMVC的表单,那么就得在request中放入一个bean对象
// 这个对象的属性名要对应 表单标签的 modelAttribute 属性值。
map.put("monster",new Monster());
return "datavaild/monster_addUI";
}
/**
* SpringMVC可以将提交的数据,按参数名和对象的属性匹配,封装成一个对象。
* @Valid Monster monster表示对monster进行校验
* Errors errors,如果校验的过程中出现错误,会将其保存到 errors 对象中
* Map<String,Object> map,如果校验出现错误,讲错误信息保存到map中去,同时报错monster对象
*
* 校验发生的时机:在SpringMVC底层反射调用目标方法时,会接收到http请求,然后根据注解来进行校验
* 在校验过程中,如果出现了错误,就把错误信息填充到errors和 map 中
* @param monster
* @return
*/
// 编写方法,处理添加妖怪
@RequestMapping(value = "/save")
public String save(@Valid Monster monster, Errors errors, Map<String,Object> map){
System.out.println("----monster----" + monster);
System.out.println("====map=====");
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " " + "value= " +entry.getValue());
}
System.out.println("====errors=====");
if (errors.hasErrors()){
List<ObjectError> allErrors = errors.getAllErrors();
for (ObjectError allError : allErrors) {
System.out.println("error=" + allError);
}
return "datavaild/monster_addUI";
}
return "datavaild/success";
}
注意:我们想要使用数据验证,还得再验证的数据(对象)之前,加上@Vaild
注解才能生效。
通过后端控制台输出的信息发现,SpringMVC会将错误信息封装到Error
对象中,同时也会将信息存入Map
对象中。
除此之外,我们前端虽然回显了错误信息,但是这中回显信息可以说是巨丑😜的。所以还需要做一些配置,来处理这种问题。
配置国际化文件
NotEmpty.monster.name=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a
typeMismatch.monster.age=\u5e74\u9f84\u4e0d\u6b63\u786e
typeMismatch.monster.birthday=\u751f\u65e5\u683c\u5f0f\u4e0d\u6b63\u786e
typeMismatch.monster.salary=\u85aa\u6c34\u683c\u5f0f\u4e0d\u6b63\u786e
这里创建的是properties
文件。key是错误信息类型,value是该错误对应的回显的信息,这里我们需要将中文转成unicode
。
我们的错误信息类型可以在控制台输出信息中找到:
创建好国际化配置文件之后,我们还需要添加到SpirngMVC配置文件中。
<!--配置国际化资源处理Bean-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!--配置国际化文件名字
这样表示 messageSource 会到src/i18nXXX.properties去读取错误信息
-->
<property name="basename" value="i18n"/>
</bean>
这里的il8n是我们的国际化配置文件名称。
配置好这些之后,我们看一下效果:
五、@InitBinder注解的使用
讲解@InitBinder
是在有些场景下,我们不想自动绑定某个属性,我们的这个注解可以取消属性的绑定。
@InitBinder
public void initBinder(WebDataBinder webDataBinder){
/**
* 1. 方法上标注@initBinder springMVC底层会初始化WebDataBinder
* 2. 调用webDataBinder.setDisallowedFields("name")。表示取消指定属性的绑定
* 3. 底层:springMVC通过反射调用目标方法时,接收到http请求的参数和值,使用反射+注解的结束,取消
* 对指定属性的填充。
* 4. setDisallowedFields;支持可变参数,参数可以有多个
* 5. 如果我们取消某个属性的绑定,验证也就没有意义了,应当把验证的注解去掉
*/
webDataBinder.setDisallowedFields("name");
}
我们这里演示取消Monster类中的name属性绑定。
我们这里可以发现,虽然我们的前端数据提交了name
数据,大但我们取消了属性的绑定,所以name的值为null。
在取消属性绑定的时候,需要注意的是:取消了name的属性绑定,那么name上也就不能@NotEmpty等验证注解,否则提交不了数据。
六、数据格式化、验证流程梳理图
以上就是这节的所有内容。
如果文章中有描述不准确或者错误的地方,还望指正。您可以留言📫或者私信我。🙏
最后希望大家多多 关注+点赞+收藏^_^,你们的鼓励是我不断前进的动力!!!
感谢感谢~~~🙏🙏🙏