1、概览 {#1概览}
后端 HTTP API 开发最重要的功能之一是解析前端传递的请求查询参数。
本文将带你了解几种直接从 HttpServletRequest
获取查询参数的方法,以及 Spring MVC 提供的一些简洁方法。
2、HttpServletRequest 中的方法 {#2httpservletrequest-中的方法}
首先,来看看 HttpServletRequest
提供的与参数相关的方法。
2.1、HttpServletRequest#getQueryString() {#21httpservletrequestgetquerystring}
HttpServletRequest#getQueryString()
可以直接从 URL 获取查询字符串信息:
@GetMapping("/api/byGetQueryString")
public String byGetQueryString(HttpServletRequest request) {
return request.getQueryString();
}
使用 curl 向该 API 发送一个包含多个参数的 GET 请求时,getQueryString()
方法会返回 ?
后面的所有字符:
$ curl 'http://127.0.0.1:8080/spring-mvc-basics/api/byGetQueryString?username=bob&roles=admin&roles=stuff'
username=bob&roles=admin&roles=stuff
如果将 @GetMapping
更改为 @RequestMapping
,当使用 POST/PUT/PATCH/DELETE HTTP 方法发送请求时,返回的响应相同。也就是说 HttpServletRequest#getQueryString
始终获取到的是 URL 中的查询参数,无论 HTTP 方法是什么。因此,本教程只关注GET请求。
2.2、HttpServletRequest#getParameter(String) {#22httpservletrequestgetparameterstring}
为了简化参数的解析,HttpServletRequest
提供了一个 getParameter
方法,可以通过参数名获取参数值:
@GetMapping("/api/byGetParameter")
public String byGetParameter(HttpServletRequest request) {
String username = request.getParameter("username");
return "username:" + username;
}
发送一个查询字符串为 username=bob
的 GET 请求时,调用 getParameter("username")
会返回 bob
。
$ curl 'http://127.0.0.1:8080/spring-mvc-basics/api/byGetParameter?username=bob&roles=admin&roles=stuff'
username:bob
2.3、HttpServletRequest#getParameterValues(String) {#23httpservletrequestgetparametervaluesstring}
getParameterValues
方法的作用与 getParameter
方法类似,但它返回的是 String[]
而不是 String
。这是因为 HTTP 规范允许传递多个同名参数。
@GetMapping("/api/byGetParameterValues")
public String byGetParameterValues(HttpServletRequest request) {
String[] roles = request.getParameterValues("roles");
return "roles:" + Arrays.toString(roles);
}
因此,当传递两个 roles
参数时,数组中就会出现两个值:
$ curl 'http://127.0.0.1:8080/spring-mvc-basics/api/byGetParameterValues?username=bob&roles=admin&roles=stuff'
roles:[admin, stuff]
2.4、HttpServletRequest#getParameterMap() {#24httpservletrequestgetparametermap}
假设有以下 UserDto
POJO,用于封装请求参数:
public class UserDto {
private String username;
private List<String> roles;
// get、set 方法省略
}
如你所见,可以有多个不同的参数名和一个或多个值。针对这些情况,HttpServletRequest
提供了另一种方法 getParameterMap()
,它返回 Map<String, String[]>
。该方法允许通过 Map
获取参数值。
@GetMapping("/api/byGetParameterMap")
public UserDto byGetParameterMap(HttpServletRequest request) {
Map parameterMap = request.getParameterMap();
String[] usernames = parameterMap.get("username");
String[] roles = parameterMap.get("roles");
UserDto userDto = new UserDto();
userDto.setUsername(usernames[0]);
userDto.setRoles(Arrays.asList(roles));
return userDto;
}
测试如下:
$ curl 'http://127.0.0.1:8080/spring-mvc-basics/api/byGetParameterMap?username=bob&roles=admin&roles=stuff'
{"username":"bob","roles":["admin","stuff"]}
3、在 Spring MVC 中获取参数 {#3在-spring-mvc-中获取参数}
来看看 Spring MVC 为解析查询字符串提供了哪些方便。
3.1、名称参数 {#31名称参数}
有了 Spring MVC 框架,我们就不必直接使用 HttpServletRequest
手动解析参数了。
对于第一种情况,可以在 Handler 方法中定义两个参数,这两个参数的查询参数名称分别是 username
和 roles
,然后删除了 HttpServletRequest
参数,由 Spring MVC 处理。
@GetMapping("/api/byParameterName")
public UserDto byParameterName(String username, String[] roles) {
UserDto userDto = new UserDto();
userDto.setUsername(username);
userDto.setRoles(Arrays.asList(roles));
return userDto;
}
测试,响应结果和上面一样:
$ curl 'http://127.0.0.1:8080/spring-mvc-basics/api/byParameterName?username=bob&roles=admin&roles=stuff'
{"username":"bob","roles":["admin","stuff"]}
3.2、@RequestParam {#32requestparam}
如果 HTTP 查询参数名和 Java 方法参数名不同,或者编译字节码中不会保留方法参数名,可以在方法参数名上配置注解 @RequestParam
,以应对这种情况。
使用 @RequestParam("username")
和 @RequestParam("roles")
指定参数名称,如下:
@GetMapping("/api/byAnnoRequestParam")
public UserDto byAnnoRequestParam(@RequestParam("username") String var1, @RequestParam("roles") List<String> var2) {
UserDto userDto = new UserDto();
userDto.setUsername(var1);
userDto.setRoles(var2);
return userDto;
}
测试:
$ curl 'http://127.0.0.1:8080/spring-mvc-basics/api/byAnnoRequestParam?username=bob&roles=admin&roles=stuff'
{"username":"bob","roles":["admin","stuff"]}
3.3、POJO {#33pojo}
更简单一点,可以直接使用 POJO 作为参数类型:
@GetMapping("/api/byPojo")
public UserDto byPojo(UserDto userDto) {
return userDto;
}
Spring MVC 会解析参数,创建 POJO 实例,并自动填充所需的参数:
$ curl 'http://127.0.0.1:8080/spring-mvc-basics/api/byPojo?username=bob&roles=admin&roles=stuff'
{"username":"bob","roles":["admin","stuff"]}
最后,通过单元测试来确保最后四个方法提供的功能完全相同。
@ParameterizedTest
@CsvSource(textBlock = """
/api/byGetParameterMap
/api/byParameterName
/api/byAnnoRequestParam
/api/byPojo
""")
public void whenPassParameters_thenReturnResolvedModel(String path) throws Exception {
this.mockMvc.perform(get(path + "?username=bob&roles=admin&roles=stuff"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.username").value("bob"))
.andExpect(jsonPath("$.roles").value(containsInRelativeOrder("admin", "stuff")));
}
4、总结 {#4总结}
本文介绍了如何从 HttpServletRequest
中获取查询参数,以及 Spring MVC 提供了哪些对查询参数的封装方式,
Ref:https://www.baeldung.com/java-httpservletrequest-get-query-parameters