1.概述

在本文中,我们将学习如何在Spring中将路径变量(PathVariable)设为可选。

首先,我们将描述Spring MVC如何在控制器方法中绑定@PathVariable参数。

然后,我们将展示在不同的Spring版本中将路径变量(PathVariable)设置为可选的不同方法。

2.如何绑定@PathVariable参数

默认情况下,Spring MVC将尝试将控制器方法中用@PathVariable注解的所有参数与URI模板中的相应变量绑定。

如果Spring MVC失败,它将不会将我们的请求传递给该处理程序方法。会直接抛出异常。

比如,我们尝试将路径变量id设置为可选:

@RequestMapping(value = { "/opv","/opv/{id}" }, method = RequestMethod.GET)
@ResponseBody
public String optionalPath(@PathVariable(name = "id") Integer id){
  if(id == null){
    return  "OptionalPathVariable id  not set";
  }
  return  "OptionalPathVariable id =" + id;
}

在上面的代码中,我们假定optionalPath方法可以同时向/opv/opv/{id}提供请求。

Spring尝试将id参数绑定到id路径变量。

然后我们用curl来测试一下:

# curl http://localhost:8080/opv/123
OptionalPathVariable id =123%                                                                                            
# curl http://localhost:8080/opv/
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Error 500 Missing URI template variable &apos;id&apos; for method parameter of type Integer</title>
</head>
<body><h2>HTTP ERROR 500 Missing URI template variable &apos;id&apos; for method parameter of type Integer</h2>
<table>
<tr><th>URI:</th><td>/opv/</td></tr>
<tr><th>STATUS:</th><td>500</td></tr>
<tr><th>MESSAGE:</th><td>Missing URI template variable &apos;id&apos; for method parameter of type Integer</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>

当我们向/opv发送请求时,Spring将返回状态代码500。我们来确认一下debug日志:

21:51:26.050 [qtp1329572464-17] WARN o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolved [org.springframework.web.bind.MissingPathVariableException: Missing URI template variable 'id' for method parameter of type Integer]

这是因为缺少id,Spring无法为id路径变量设置值。

因此,我们需要一些方法来告诉Spring 如果没有相应的path变量,则忽略绑定特定的*@PathVariable*参数。

3.设置路径变量为可选

3.1. 使用required属性

从Spring 4.3.3开始,@PathVariable注解定义了required属性,来告诉Spring这个路径变量是否是必须的。

required属性的默认值为true

下面我们来修改一下之前的代码:

@RequestMapping(value = { "/opv","/opv/{id}" }, method = RequestMethod.GET)
@ResponseBody
public String optionalPath(@PathVariable(name = "id",required = false) Integer id){
  logger.info("optional Path Variable");
  if(id == null){
    return  "OptionalPathVariable id  not set";
  }
  return  "OptionalPathVariable id =" + id;
}

我们简单的测试一下

# curl http://localhost:8080/opv/
OptionalPathVariable id  not set%

由于required属性为了false,如果没有在请求中发送id路径变量,Spring不会抛出异常。

3.2. 使用Optional类型参数

从Spring 4.1开始, 我们还可以使用JDK 8的Optional类提供了另一种使路径变量设置为可选:

@RequestMapping(value = { "/opv2","/opv2/{id}" }, method = RequestMethod.GET)
@ResponseBody
public String optionalPath2(@PathVariable Optional<Integer> id){
  logger.info("optional Path Variable");
  if(id.isPresent()){
    return  "OptionalPathVariable id  not set";
  } else {
    return  "OptionalPathVariable id =" + id.get();
  }
}

Spring创建Optional <Integer>实例,以保存id的值。

如果存在id,则包装其值,否则,将包装null值。

然后,我们可以使用Optional的isPresent()get()orElse()方法来处理该值。

最后, 我们简单的测试一下:

# curl http://localhost:8080/opv2/
OptionalPathVariable id  not set%                                                                                       
# curl http://localhost:8080/opv2/123
OptionalPathVariable id =123%

3.3 使用Map作为参数

从Spring 3.2开始,定义可选路径变量的另一种方法是定义@PathVariable参数类型为Map

@RequestMapping(value = { "/opv3","/opv3/{id}" }, method = RequestMethod.GET)
@ResponseBody
public String optionalPath3(@PathVariable Map<String, String> pvMap){
  logger.info("optional Path Variable");
  String id = pvMap.get("id");
  if(id == null){
    return  "OptionalPathVariable id  not set";
  }
  return  "OptionalPathVariable id =" + id;
}

使用Map <String,String> 收集URI中的所有路径变量作为键/值对。

然后,我们可以使用get()方法获取特定的路径变量。

需要注意的是,由于Spring将路径变量的值提取为String,如果需要Integer的话,我们还需要使用Integer.valueOf()方法将其转换为Integer。

最后, 我们简单的测试一下:

# curl http://localhost:8080/opv3/
OptionalPathVariable id  not set%                                                                                        
# curl http://localhost:8080/opv3/213
OptionalPathVariable id =213%

3.4 使用两个处理方法

如果使用的Spring版本比较老,还可以将optionalPath处理方法分为两个方法。

第一个处理方法将处理对/pv4/ {id}的请求:

@RequestMapping(value = {"/opv4/{id}" }, method = RequestMethod.GET)
@ResponseBody
public String optionalPath4(@PathVariable Integer id){
	return  "OptionalPathVariable id =" + id;
}

第二个处理方法将处理对/pv4的请求:

@RequestMapping(value = { "/opv4" }, method = RequestMethod.GET)
@ResponseBody
public String optionalPath4(){
  return  "OptionalPathVariable id  not set";
}

最后, 我们简单的测试一下:

# curl http://localhost:8080/opv4
OptionalPathVariable id  not set%                                                                                        
# curl http://localhost:8080/opv4/123
OptionalPathVariable id =123%

4. 结论

在本文,我们讨论了如何在不同的Spring版本和不同方法设置路径变量为可选。

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