Spring MVC 数据绑定
【基本类型 or 包装类型】
基本类型 => 必传 (不传的话爆 500 服务器端的错)
包装类型 => 可选 不传为 null(+ @RequestParam )
【数组类型】
基本类型 包装类型 String[]
【POJO】
前端传 POJO 的字段就可以。
=》 如果字段也是 POJO ,比如 ContactInfo,前端传 contanctInfo.phone 即可。
=》 如果方法入参是两个相似的 POJO ,比如 Admin 和 User,前端传 admin.name 和 user.name 即可,但需要后端配合 @InitBinder
@InitBinder
public void initUser(WebDataBinder binder){
binder.setFieldDefaultPrefix("user.");
}
@InitBinder
public void initAdmin(WebDataBinder binder){
binder.setFieldDefaultPrefix("admin.");
}
【List Set Map】
List 【适合:连续的索引】
前端传 【/list.do?users[0].name=Bob&users[99].name=Alice】,后端配合 UserListForm 实现数据绑定。(规律就是 SpringMVC 允许前端传 Pojo 的字段过来)
@Data // lombok setter getter
public class UserListForm{
private List<User> users;
}
// Controller 相关代码
@GetMapping("list.do")
@ResponseBody
public String addUsers(UserListForm userListForm){
return userListForm.toString();
}//坑:userListForm.getUserList().size = 前端最大索引99
Set 【坑:目的用来排重,但是 Spring MVC 初始化 Set 索引大小是按照 前端传的数据去重后的大小,因为 index < set.size() 然后抛异常】
【绕过坑的办法,Set 转 List 返回给前端,用 List 进行前端数据绑定】
POJO => 配合 Set ,实现 equals 和 hashcode 方法,equals 相同 hashcode 必须相同,equals 不同 hashcode可以相同,没那么绕,因为 equals 比较的字段比 hashcode 多。
前端传 【/set.do?users[0].name=Alice&users[99].name=Alice】,后端配合 UserSetForm 实现数据绑定(相对 List 要求初始化 Set,所以索引是确定的)。
// 代码 【数据绑定实现方式 和 List 相似】
Map【适合:Map<String,Object>】
前端传 【/map.do?users["X"].name=Tom&users["X"].name=Alice】,后端配合 UserMapForm 实现数据绑定。
// 代码 【数据绑定实现方式 和 List 相似】
【JSON XML】
JSON ,前端传一个 User 的 Json 对象,后端使用 @RequestBody 支持入参实现数据绑定。
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
@GetMapping("json.do")
@ResponseBody
public String getJson(@RequestBody User user){
return user.toString();
}
XML 前端传 xml ,后端使用 @XmlRootElement @XmlElement ,其他和 JSON 数据绑定相似。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>4.3.17.RELEASE</version>
</dependency>
// 注解 POJO
@XmlRootElement(name = "user")
@Data // lombok
public class User{
@XmlElement(name = "name")
private Sring name;
@XmlElement(name = "age")
prviate int age;
}
/////////////////////////////
// Controller
【PropertyEditor PropertyEditorSupport】局部使用 WebDataBinder
约定 前端传 "foo,19"
public class MyPropertyEditor extends PropertyEditorSupport{
@Override
public void setAsText(String text)throws Ille...{
String[] textArray = text.spilt(",");
User user = new User();
user.setName(textArray[0]);
user.setAge(Integer.parseInt(textArray[1]));
this.setValue(user);
}
}
解决问题一 前端传 /date1.do?date1=2018-01-01 后端使用 webdatabinder 仍然使用 Spring MVC 的数据绑定功能。
@GetMapping("date1.do")
@ResponseBody
public String getDate1(Date date1){
return date1.toString();
}
@InitBinder
public void initDate1(WebDataBinder binder){
binder.registerCustomEditor(Date.class,
new CustomEditor(
new SimpleDateFormat("yyyy-MM-dd"),true));
}
【Formatter<T> extends Printer<T>, Parser<T>】 针对 String
@Test
public void testPrint(){
CurrencyFormatter formatter = new CurrencyFormatter();
formatter.setFractionDigits(2);//保留小数后2位
formatter.setRoundingMode(RoundingMode.CEILING);
Assert.assertEquals("$123.00",formatter.print(new BigDecimal("123"), Locale.US));
}
@Test
public void testParse() throws ParseException {
CurrencyFormatter formatter = new CurrencyFormatter();
formatter.setFractionDigits(2);//保留小数后2位
formatter.setRoundingMode(RoundingMode.CEILING);
Assert.assertEquals(new BigDecimal("123.13"),formatter.parse("$123.125", Locale.US));
}
解决问题一 (全局)
public class MyDateFormatter implements Formatter<Date>{
@Override
public Date parse(String text,Locale locale) throws ParseE{
SimpleDateFormat sdf = new Sim("yyyy-MM-dd");
return sdf.parse(text);
}
// print method
}
//dispatcherServlet
<context:component-scan base-package="com" annotation-config="true"/>
<mvn:annotation-driven conversion-service="myDateFormatter"/>
<bean id="myDateFormatter" class="springframework.format.support.FCServiceFactoryBean"
<property name="formatters">
<set>
<bean class="MyDateFormatter">
</bean>
</set>
</property>
<bean>
【Converter 】内置的不可扩展 final ,我们要直接实现接口
解决问题一(全局 + 可自定义类型)
public class MyDateConverter implements Converter<String,Date>{
@Override
public Date convert(String src) throws ParseE{
SimpleDateFormat sdf = new Sim("yyyy-MM-dd");
return sdf.parse(src);
}
}
//dispatcherServlet
<context:component-scan base-package="com" annotation-config="true"/>
<mvn:annotation-driven conversion-service="myDateConverter"/>
<bean id="myDateConverter" class="org.springframework.format.support.FCServiceFactoryBean"
<property name="convertters">
<set>
<bean class="MyDateConverter">
</bean>
</set>
</property>
<bean>
…………………………
【RESTful URI】
https://book.douban.com/subject/3024391/ 对,用了名词
https://book.douban.com/show/subject/3024391/ 错,不应该用动词
@GetMapping("/subject/{subjectId}") @ResponseBody public String getOneBook(@PathVariable("subjectId")Integer subjectId){ return bookService.selectBookByPrimaryKey(subjectId).toString(); }
POST /accounts/1/transfer/500/to/2 错
POST /transaction
from=1&to=2&amount=500.00
www.example.com/app/v1 不好
www.example.com/app 使用 HEADER version=1.0