RequestParam注解在springmvc的作用是:将请求参数绑定到控制器的方法的参数上
用来处理 Content—Type:application/x-www-form-urlencoded 编码的内容,在http协议中,
不指 定Content-Type 则默认传递的就是 application/x-www-form-urlencoded 类型。
RequestParam 可以接受简单类型的属性,也可以接受对象类型。实质是将 Request.getParameter()
中的Key-Value 参 数Map 利用 Spring 的转化机制 ConversionService 配置,转化成参数接收对象或字段。
在Content-Type:application/x-www-form-urlencoded的请求中,get 方式中 queryString 的值,
和 post 方式中 body data 的值都会被 Servlet 接受到并转化到 Request.getParameter() 参数集中,
所以 @RequestParam 可以获取的到。
tip: application/x-www-form-urlencoded 是最常见的POST提交数据方式,也是原生form默认的提交方式,
支持GET/POST等方法,所有数据变成键值对的形式key1=value1&key2=value2的形式,并且特殊字符需要转义成utf-8编码,如空格会变成 %20
示例代码如下:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
/**
* @FileName: TestUserController
* @Author: fjp
* @Date: 2020/6/16 14:56
* @Description: 测试一个User对象的RequestParam注解
* History:
*
@RestController
public class TestUserController {
@PostMapping("/user")
public String testUser(String name, String age) {
return new User(name, age).toString();
}
@PostMapping("/user1")
public String testUser1(@RequestParam("name") String name, @RequestParam("name") String age) {
return new User(name, age).toString();
}
@PostMapping("/user2")
public String testUser2(@RequestParam("name1") String name, @RequestParam("age1") String age) {
return new User(name, age).toString();
}
@PostMapping("/user3")
public String testUser3(User user) {
return user.toString();
}
@PostMapping("/user4")
public String testUser4(@Valid Info info) {
return info.toString();
}
@PostMapping("/user5")
public String testUser5(User user, Info info) {
return new UserInfo(user,info).toString();
}
@PostMapping("/user6")
public String testUser6(UserInfo userInfo) {
return userInfo.toString();
}
@PostMapping("/user7")
public String testUser7(@RequestParam("userInfo") UserInfo userInfo) {
return userInfo.toString();
}
}
class User {
public User() {
}
public User(String name, String age) {
this.name = name;
this.age = age;
}
private String name;
private String age;
@Override
public String toString() {
final StringBuilder sb;
sb = new StringBuilder();
sb.append('{');
sb.append("\"name\":\"").append(name).append('\"');
sb.append(",\"age\":\"").append(age).append('\"');
sb.append('}');
return sb.toString();
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return this.age;
}
public void setAge(String age) {
this.age = age;
}
}
class Info {
public Info() {
}
public Info(String profile) {
this.profile = profile;
}
@NotBlank
private String profile;
public String getProfile() {
return profile;
}
public void setProfile(String profile) {
this.profile = profile;
}
@Override
public String toString() {
final StringBuilder sb;
sb = new StringBuilder();
sb.append('{');
sb.append("\"profile\":\"").append(profile).append('\"');
sb.append('}');
return sb.toString();
}
}
class UserInfo {
private User user;
private Info info;
public UserInfo() {
}
public UserInfo(User user, Info info) {
this.user = user;
this.info = info;
}
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
public Info getInfo() {
return this.info;
}
public void setInfo(Info info) {
this.info = info;
}
@Override
public String toString() {
final StringBuilder sb;
sb = new StringBuilder();
sb.append('{');
sb.append("\"user\":").append(user);
sb.append(",\"info\":").append(info);
sb.append('}');
return sb.toString();
}
}
class StringToUserInfoConverter implements Converter<String, UserInfo> {
@SneakyThrows
@Override
public UserInfo convert(String from) {
//System.out.println("from = " + from);
return new ObjectMapper().readValue(from, UserInfo.class);
}
}
@Configuration
class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToUserInfoConverter());
}
}
调用上方各接口的参数及结果如下所示:
0. 接口地址: /user
请求参数: name : test
age : 12
响应结果: {"name":"test","age":"12"}
1. 接口地址: /user1
请求参数: name : test
age : 12
响应结果: {"name":"test","age":"12"}
2. 接口地址: /user2
请求参数: name1 : test
age1 : 12
响应结果: {"name":"test","age":"12"}
3. 接口地址: /user3
请求参数: name : test
age : 12
响应结果: {"name":"test","age":"12"}
4. 接口地址: /user4
请求参数: profile : info class
响应结果: {"info":"info class"}
5. 接口地址: /user5
请求参数: name : test
age : 12
profile : info class
响应结果: {"user":{"name":"test","age":"12"},"info":{"profile":"info class"}}
6. 接口地址: /user6
# 若未添加自定义转换类
请求参数: userInfo : {"user":{"name":"test","age":"12"},"info":{"profile":"info class"}}
响应结果: {"user":null,"info":null}
# 若添加自定义转换类
请求参数: userInfo : {"user":{"name":"test","age":"12"},"info":{"profile":"info class"}}
响应结果: {"user":{"name":"test","age":"12"},"info":{"profile":"info class"}}
7. 接口地址: /user7
请求参数: userInfo : {"user":{"name":"test","age":"12"},"info":{"profile":"info class"}}
响应结果: {"user":{"name":"test","age":"12"},"info":{"profile":"info class"}}
通过使用 @RequestParam 在控制器中将请求参数绑定到方法参数,而不需要请求参数名与方法形参名完全一致。(如/user1,/user2中请求参数完全不同)
尽管 @RequestParam 注解能够将请求参数绑定到控制器的方法的参数上,并能通过参数 value 设置请求的参数名称(如/user2),但是在多个参数的情况下,过长的参数列表也会让代码的可读性变得很差。
解决这个这个问题最简单的方式就是,将这组参数合并到一个简单的对象(POJO)中,即如上方代码将参数合并成对象 User(如/user3)。
并非强制要求把所有的参数全部封装到一个对象中,完全可以按照功能将请求参数分布到多个POJO中(如/user5)。
@RequestParam(value=“参数名”,required=“true|false”,defaultValue="")
value : 参数名称
required : 是否包含改参数,默认为true,参数必须在请求中,此时参数不存在则报错
defaultValue : 默认参数值,设置默认值后,required 为 true 自动失效转为 false,若没传参数值,则将使用设置的默认值。
使用 @RequestParam 注解,可以对绑定的参数进行校验(Java Bean Validation 框架或自定义参数校验),如果参数校验失败,可以将违反定义的约束的行为作为集合返回或拒绝该请求。在 controller 方法中的参数 POJO 前添加 @Valid 注解,Spring 将在绑定参数前自动执行校验操作;也可通过创建对应 validator 对象的方式进行手动校验。如 /user4 中添加 @Valid 注解进行参数绑定前校验(需在实体类成员变量上添加对应约束注解)。
@RequestParam 注解会在接收时将多个同名参数的值只保留第一个,后面的会忽略。若未添加注解,则会将同名请求值以逗号分隔进行拼接。
通过实现一个自定义的 Converter (即 StringToUserInfoConverter ),自行重写请求参数到实体类的转换,然后添加到解析请求参数的 converter 集合中(即 WebConfig ),这样就可以将 @RequestParam 应用到对应的请求参数上,将请求参数值绑定到实体类(如/user7)。