SpringMVC 参数绑定
默认的参数绑定类型
在 SpringMVC 框架的 Controller 方法(配置了@RequestMapping
或其他请求映射)中,我们可以使用的默认参数类型主要有:
- HttpServletRequest
- HttpServletResponse
- HttpSession
- HttpMethod
- Model
我们只需要在方法的参数列表中添加上述类型的参数,那么在处理请求时,SpringMVC 就会自动绑定与该请求相关的对象,正确传递给方法。
举个最简单的栗子:
@PostMapping("/user")
public String login(HttpServletRequest request) {
// 获取请求参数
String name = request.getParameter("username");
String pwd = request.getParameter("password");
// 调用业务方法
Boolean status = userService.login(name, pwd);
// 返回状态
if (status) {
return "登录成功";
} else {
return "登录失败";
}
}
除了上面列举的各个请求都通用的类型,我们也可以添加其他类型的形参,比如 Integer,String,用来接收具体请求携带的参数,免去了request.getParameter
的频繁使用。但是请注意,这么做必须保证请求参数的 key 与形参的名称相同,SpringMVC 才能识别并绑定。
相似的例子:
@PostMapping("/user")
public String login(String username, String password) {
Boolean status = userService.login(username, password);
if (status) {
return "登录成功";
} else {
return "登录失败";
}
}
POJO 类也可以自动识别,但还是那条规则:请求参数的 key 必须与该 POJO 类型的字段名称相同;另外如果参数的 key 出现了这种形式address.provinceName
,那就意味着包含了另一个 POJO 类,字段名也必须相同:
public class User {
private Long id;
private String name;
private String password;
private Address address; // 包含了Address类型
}
public class Address {
private String provinceName;
private String cityName;
}
参数绑定的注解
1. @RequestParam
你可能觉得请求参数与方法形参必须保证名称相同的限制有点严格,那么请使用@RequestParam
注解,可以将指定的请求参数赋值给方法中的形参,并提供更灵活的选项。它有 3 个可使用的属性:
- value:指定请求参数的名称
- required:指定参数是否必须绑定,默认 true
- defaultValue:当请求不携带该参数时使用的默认值
@GetMapping("/category")
public Category findById(@RequestParam(value = "id",
required = true, defaultValue = "1") categoryId) {
Category = categoryService.findById(categoryId);
return Category;
}
另外请注意,不管你的方法形参是直接使用还是加上@RequestParam
注解,都只能接收 Content-Type 为 application/x-www-form-urlencoded 编码的内容,JSON 格式的数据会被拒绝。
2. @RequestBody
使用@RequestBody
注解在参数上就可以接收非 application/x-www-form-urlencoded 编码格式了,用于处理HttpEntity
中的数据。由于 GET 请求没有请求体,所以不适用;对 POST 请求,SpringMVC 就会通过处理适配器HandlerAdapter
中配置的HttpMessageConverters
来解析HttpEntity
的数据,然后绑定到相应的 POJO 上。
@PostMapping("/book")
public void saveBook(@RequestBody book) {
bookService.save(book);
}
通过@RequestBody
获取的是整个请求体的数据,这也就是说:(敲重点)如果你想要同时接收两个 POJO 类型对象,那么只能用另一个 POJO 类包含它们。
3. @PathVariable
@PathVariable
注解可以绑定 URL 中的动态参数,该注解只有一个 value 属性,类型为 String,表示 URL 中需要绑定的参数名称;如果省略,则默认绑定与形参相同的名称。比较适合传递 id 或 name 等能够标识一个资源的数据。
栗子:
@GetMapping("/user/{userId}/articles")
public List<Article> findArticleByUser(@PathVariable("userId") Long id) {
return articleService.findArticleByUserId(id);
}
4. @RequestHeader
@RequestHeader
注解可以绑定请求的报头信息(数据),有 3 个可用的属性:
- value:指定请求头的字段名称
- required:是否必须绑定
- defaultValue:当该字段不存在时使用的默认值
回顾一下 HTTP 的知识,一般的请求报头信息有下面这些字段(这里截取了在「简书」浏览一篇文章时发出的其中一个请求):
我们现在想要获取 Accept-Encoding 的信息,用@RequestHeader
就可以一步搞定:
@GetMapping("/article/{id}")
public Article findArticle(@PathVariable Long id,
@RequestHeader("Accept-Encoding") String[] encoding) { // 这里注意是字符串数组
// ...
}
5. @CookieValue
@CookieValue
注解可以绑定请求的 Cookie 信息,同样有 3 个可用的属性:
- value:指定 Cookie 的 key
- required:是否必须绑定
- defaultValue:不存在该 Cookie 时的默认值
示例:
@GetMapping("/article/{id}")
public Article findArticle(@PathVariable Long id,
@CookieValue("JSESSIONID") String cookie) {
// ...
}
PS:JSESSIONID 是 Servlet 容器(tomcat,jetty)用来记录用户 session 的默认 Cookie 名称
PS*2:调用request.isRequestedSessionIdFromCookie
判断 Cookie 是否可用,逻辑就是读取 request 里面有没有 JSESSIONID 这个 Cookie。
自定义参数转换
我们已经知道 SpringMVC 在参数绑定和类型转换方面提供了不少的支持,那么有没有什么地方是需要我们自定义参数转换的呢?最典型的场景是日期类型转换(字符串转 java.util.Date 类型),对于这个问题,解决方案有两个:实现Converter
接口或Formatter
接口。
1. Converter
先看Converter
,需要指定两个泛型,前者为要转换的类型,后者为转换后的类型,只有一个convert
方法需要实现:
public class DateConverter implements Converter<String, Date> {
// 日期的数据格式
private String dataPattern = "yyyy-MM-dd HH:mm:ss";
@Override
public Date convert(String source) {
try {
// 进行日期转换
return new SimpleDateFormat(dataPattern).parse(source);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2. Formatter
再来看Formatter
接口,只需要指定一个泛型,即转换后的类型,而待转换的类型必须是 String,需要实现两个方法:parse
和print
,前一个方法规定了如何将字符串转换成指定类型,后一个方法规定了如何以字符串形式输出指定类型:
public class DateFormatter implements Formatter<Date> {
// 日期的数据格式
private String dataPattern = "yyyy-MM-dd HH:mm:ss";
@Override
public Date parse(String s, Locale locale) throws ParseException {
// 将字符串转换成Date
return new SimpleDateFormat(dataPattern).parse(s);
}
@Override
public String print(Date date, Locale locale) {
// 将Date转换成字符串
return new SimpleDateFormat(dataPattern).format(date);
}
}
3. 配置
接下来就是配置了,假设你用的是mvc:annotation-driven
:
<!-- 配置自定义的日期类型转换器 -->
<mvc:annotation-driven conversion-service="dataConverterService"/>
<bean id="dataConversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!-- 使用Convert接口-->
<property name="converters">
<set>
<bean class="cn.cna.convert.DateConverter"/>
</set>
</property>
</bean>
<mvc:annotation-driven conversion-service="dataConverterService"/>
<bean id="dataConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!-- 使用Formatter接口-->
<property name="formatters">
<set>
<bean class="cn.cna.convert.DateConverter"/>
</set>
</property>
</bean>
比较:
Converter
是一种任意类型转换另一种任意类型,具有普遍性;Formatter
是 String 转换另一种,更符合 web 层的特点,比较适合在 springMVC 中使用。
END
整理自:SpringMVC+Mybatis快速开发与项目实战,黄文毅,清华大学出版社
其他参考:
一文读懂SpringMVC中的数据绑定,Wizey,简书
SpringMVC|参数绑定,GGarrett,简书
springmvc formatter,抱明月,博客园