1.概述

在本文中,我们将探讨Spring的@RequestParam注解。

@RequestParam可以从请求中提取查询参数(query parameters),表单参数(form data)甚至文件。

本文将讨论如何使用@RequestParam及其属性。还会讨论@RequestParam@PathVariable之间的区别。

2. 一个简单的例子

假设我们有一个终点(endpoint)/api/book,它接受一个名为id的参数:

@RequestMapping("/api/book")
@ResponseBody
public String getBook(@RequestParam String id) {
  return "id : " + id;
}

上面的代码,我们使用 @RequestParam提取查询参数id。

我们使用CURL来测试一下:

# curl http://localhost:8080/api/book?id=abc
ID: abc%

接下来,让我们看一下这个注解的属性:name, value, required和defaultValue。

3. 指定请求参数名称

在前面的示例中,变量名称参数名称相同。

但是有时候它们可能会有所不同。我们可以使用name属性来配置@RequestParam名称:

@RequestMapping("/api/book2")
@ResponseBody
public String getBooks(@RequestParam("id") String bookId, @RequestParam String name) {
  return String.format("id: %s; name: %s", bookId, name);
}

我们使用CURL来测试一下:

# curl http://localhost:8080/api/book2?id=abc&name=pei

id: abc; name: pei%

对于@RequestParam注解,@RequestParam("id")@RequestParam(value = "id")是一样的。

4. 设定请求参数为可选

默认情况下,@RequestParam注解的参数是必须的,如果不存在,会收到错误。

# curl http://localhost:8080/api/book3?id=abc

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Error 400 Required String parameter &apos;name&apos; is not present</title>
</head>
<body><h2>HTTP ERROR 400 Required String parameter &apos;name&apos; is not present</h2>
<table>
<tr><th>URI:</th><td>/api/book2</td></tr>
<tr><th>STATUS:</th><td>400</td></tr>
<tr><th>MESSAGE:</th><td>Required String parameter &apos;name&apos; is not present</td></tr>
<tr><th>SERVLET:</th><td>mvc</td></tr>
</table>
<hr><a href="http://eclipse.org/jetty">Powered by Jetty:// 9.4.24.v20191120</a><hr/>

</body>
</html>

查看日志,我们会发现spring会抛出MissingServletRequestParameterException异常:

21:31:26.763 [qtp934993374-17] DEBUG o.s.w.s.m.m.a.ServletInvocableHandlerMethod - Could not resolve parameter [1] in public java.lang.String com.ripjava.spring.mvc.model.controller.RequestParamController.getBooks(java.lang.String,java.lang.String): Required String parameter 'name' is not present
21:31:26.763 [qtp934993374-17] WARN  o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required String parameter 'name' is not present]
21:31:26.763 [qtp934993374-17] DEBUG o.s.web.servlet.DispatcherServlet - Completed 400 BAD_REQUEST

如果,我们把@RequestParam设置为可选的话

@RequestMapping("/api/book3")
@ResponseBody
public String getBooks3(@RequestParam(value = "id",required = false) String bookId, @RequestParam(required = false) String name) {
  return String.format("id: %s; name: %s", bookId, name);
}

我来测试一下:

# curl http://localhost:8080/api/book3?id=abc
id: abc; name: null%                                                                        

# curl http://localhost:8080/api/book3
id: null; name: null%

如果未指定参数,则方法参数会绑定为null。

4.1 使用Java 8 可选

除了使用required参数,我们可以将参数包装在Optional类中,这样我们不使用required参数,也可以将参数设置为可选。

@RequestMapping("/api/book4")
@ResponseBody
public String getBooks4(@RequestParam(value = "id") Optional<String> bookId, @RequestParam Optional<String> name) {
  return String.format("id: %s; name: %s", bookId.orElse("null"), name.orElse("null"));
}

我们来测试一下:

# curl http://localhost:8080/api/book4?id=abc
id: abc; name: null%                                                                                                     

# curl http://localhost:8080/api/book4
id: null; name: null%

5. 设定请求参数的默认值

我们还可以使用defaultValue属性将默认值设置为@RequestParam

@RequestMapping("/api/book5")
@ResponseBody
public String getBooks5(@RequestParam(value = "id", defaultValue = "null") String bookId, @RequestParam(defaultValue = "null") String name) {
  return String.format("id: %s; name: %s", bookId, name);
}

这就像required = false一样,如果用户不提供参数将使用默认值:

# curl http://localhost:8080/api/book5?id=abc
id: abc; name: default%   

# curl http://localhost:8080/api/book5
id: default; name: default%

需要注意的是:当我们设置 defaultValue 属性 required被设置为了false。

6. 映射所有的参数

还也可以通过使用Map 来映射多个参数,而不需要定义它们的名称和数量:

@RequestMapping("/api/book6")
@ResponseBody
public String getBooks6(@RequestParam Map<String,String> allParams) {
  return  allParams.entrySet().stream()
    .map(entry -> String.format("name: %s, value:%s ", entry.getKey(),  entry.getValue()))
    .collect(Collectors.joining(System.lineSeparator()));
}

我们来测试一下:

# curl http://localhost:8080/api/book6?id=abc&name=pei&domin=ripjava

name: name, value:pei
name: domin, value:ripjava
name: id, value:abc %

7.映射参数的多个值到List

一个@RequestParam可以具有多个值:

@RequestMapping("/api/book7")
@ResponseBody
public String getBooks7(@RequestParam List<String> ids) {
  return  String.join(",", ids);
}

我们来测试一下:

  • 将映射以逗号分隔的参数:
# curl http://localhost:8080/api/book7?ids=one,two,three

one,two,three%
  • 单独的参数的列表
# curl http://localhost:8080/api/book7?ids=one&ids=two&ids=three

one,two,three%

8.结论

在本文中,我们学习了如何使用@RequestParam

与往常一样,可以在GitHub上找到示例实现。