前言
这是 Spring MVC 处理器方法参数实战的第二篇,我们来尝试使用复杂的成员变量类型来接收 form 表单字段。
对于普通的 Java Web 项目,我们一般通过 ServletRequest#getParameter
方法来获取字符串类型的 form 表单字段值。
对于 Spring MVC 项目,直接将简单类型定义为处理器方法参数即可获取 form 表单字段值,如果表单字段比较多,还可以使用一个复杂类型来接收所有的表单字段值。
通常情况下,我们使用简单类型接收单个表单字段就足够了,然而在复杂的场景下,简单类型可能无法满足我们的需求。对于 application/json
类型的请求,我们可以定义任意复杂的类型来接收 json 格式的请求体,那么对于 form 类型的请求又该如何处理呢?
简单类型
先看看 Spring MVC 如何使用简单类型接收表单参数,假定我们要做一个注册功能,我们需要接收账号和密码,可以使用如下方式。
@RestController
public class UserController {
@PostMapping("/register")
public User register(@RequestParam("username") String username, @RequestParam("password") String password) {
return null;
}
}
如果表单字段比较多,我们可以把参数移到自定义的类型中。
@Data
public class User {
private String username;
private String password;
}
@RestController
public class UserController {
@PostMapping("/register")
public User register(User user) {
return user;
}
}
自定义类型
现在除了用户名和密码,我们还希望用户注册时能够提供所在省市,我们常规的思路是添加两个分别表示省份和城市的表单字段。
@Data
public class User {
private String username;
private String password;
private String province;
private String city;
}
省份和城市属于地址信息,可能我们希望定义到 Address 类中。
@Data
public class Address {
private String province;
private String city;
}
@Data
public class User {
private String username;
private String password;
private Address address;
}
我们还希望使用 User 接收表单字段,但是表单字段只能传递字符串,该怎么办呢?
好在 Spring MVC 已经为我们考虑到了这种情况,使用 .
分隔成员变量作为表单字段名即可将表单字段值正确的绑定到自定义的成员变量类型中。PostMan 测试如下。
Spring MVC 成功解析出了表单 address.province
及 address.city
字段,并设置到 User
类型 address
成员变量中。
数组类型
除了自定义的类型,我们可能还有使用 List
或者数组接收表单字段的场景,例如在注册用户时我们希望用户提供兴趣爱好。修改 User 类型如下。
@Data
public class User {
private String username;
private String password;
private Address address;
private List<String> interests;
}
对于这种场景,我们可以使用类似 Java 数组的语法,成员变量后面添加 [index]
作为索引,其中索引值 index
为数字,也可以被单引号或双引号包括。PostMan 测试如下。
Spring 成功将表单字段解析到 List
中,对于数组同样适用,而且 Spring 还兼容了 []
中的单引号或者双引号,索引从 0 开始,如果索引不连续中间的值则为 null。
Map 类型
如果我们还想让用户添加自定义的字段该怎么办呢?假定我们想使用 `Map<String,Object> 接收,可以修改 User 类型如下。
@Data
public class User {
private String username;
private String password;
private Address address;
private List<String> interests;
private Map<String,Object> extra;
}
此时可以表单字段名称可以同样可以使用数组类型的语法,成员变量名后跟 [key]
作为 Map
中的 key,[]
内部可以添加单引号或者双引号。使用 PostMan 测试如下。
服务端成功使用 Map 接收到了表单字段。
实现分析
简单对复杂类型接收表单字段的实现做一个分析。对于处理器方法参数解析,都是 HandlerMethodArgumentResolver 处理的,前面几篇文章也断断续续提到了这个接口,对于不带注解的复杂类型,Spring MVC 使用的实现是 ModelAttributeMethodProcessor
,这个实现会将 request 表单字段绑定到方法参数中,主要就是用到了 Spring 数据绑定的特性。
关于数据绑定的细节,感兴趣的小伙伴可移步《聊聊 Spring 核心特性中的数据绑定 (DataBinder)》。