Spring 中的 @PathVariable 注解

1、概览

本文将带你了解 Spring 中 @PathVariable 注解的作用和用法。

简单地说,@PathVariable 注解可用于处理请求 URI 映射中的模板变量,并将其绑定到 Controller 方法参数。

2、示例映射

@PathVariable 注解的一个简单用例是用于标识具有 ID 的实体的端点:

@GetMapping("/api/employees/{id}")
@ResponseBody
public String getEmployeesById(@PathVariable String id) {
    return "ID: " + id;
}

在本例中,使用 @PathVariable 注解来提取 URI 的模板部分,该部分由变量 {id} 表示。

调用示例如下:

http://localhost:8080/api/employees/111 
---- 
ID: 111

3、指定 PATH (路径)变量名

在上一个示例中,由于方法参数和路径变量的名称相同,可以不用主动设置模板路径变量的名称。

如果路径变量名不同,可以在 @PathVariable 注解的参数中指定:

@GetMapping("/api/employeeswithvariable/{id}")
@ResponseBody
public String getEmployeesByIdWithVariableName(@PathVariable("id") String employeeId) {
    return "ID: " + employeeId;
}

测试如下:

http://localhost:8080/api/employeeswithvariable/1 
---- 
ID: 1

为了清晰起见,还可以将路径变量名定义为 @PathVariable(value="id"),而不是 PathVariable("id")

4、单个请求中的多个路径变量

根据情况,可以在请求 URI 中为 Controller 方法设置多个路径变量,而 Controller 方法也有多个方法参数:

@GetMapping("/api/employees/{id}/{name}")
@ResponseBody
public String getEmployeesByIdAndName(@PathVariable String id, @PathVariable String name) {
    return "ID: " + id + ", name: " + name;
}

测试:

http://localhost:8080/api/employees/1/bar 
---- 
ID: 1, name: bar

还可以使用 java.util.Map<String, String> 类型的方法参数来处理多个 @PathVariable 参数:

@GetMapping("/api/employeeswithmapvariable/{id}/{name}")
@ResponseBody
public String getEmployeesByIdAndNameWithMapVariable(@PathVariable Map<String, String> pathVarsMap) {
    String id = pathVarsMap.get("id");
    String name = pathVarsMap.get("name");
    if (id != null && name != null) {
        return "ID: " + id + ", name: " + name;
    } else {
        return "Missing Parameters";
    }
}

测试如下:

http://localhost:8080/api/employees/1/bar 
---- 
ID: 1, name: bar

然而,在处理包含点(.)字符的路径变量字符串时,处理多个 @PathVariable 参数时有一个小坑需要注意,细节参阅 这里

5、可选择的路径变量

在 Spring 中,默认情况下,使用 @PathVariable 注解的方法参数是必需的。

@GetMapping(value = { "/api/employeeswithrequired", "/api/employeeswithrequired/{id}" })
@ResponseBody
public String getEmployeesByIdWithRequired(@PathVariable String id) {
    return "ID: " + id;
}

咋一看,上述 Controller 应该可以处理 /api/employeeswithrequired/api/employeeswithrequired/1 请求。但是,由于 @PathVariables 所注解的方法参数默认为必选参数,因此它无法处理 /api/employeeswithrequired 请求:

http://localhost:8080/api/employeeswithrequired 
---- 
{"timestamp":"2020-07-08T02:20:07.349+00:00","status":404,"error":"Not Found","message":"","path":"/api/employeeswithrequired"} 
http://localhost:8080/api/employeeswithrequired/1 
---- 
ID: 111

有两种不同的方法来处理这个问题。

5.1、设置 required 属性为 false

可以将 @PathVariablerequired 属性设置为 false,使其成为可选属性。这样,就可以处理带路径变量和不带路径变量的 URI 了:

@GetMapping(value = { "/api/employeeswithrequiredfalse", "/api/employeeswithrequiredfalse/{id}" })
@ResponseBody
public String getEmployeesByIdWithRequiredFalse(@PathVariable(required = false) String id) {
    if (id != null) {
        return "ID: " + id;
    } else {
        return "ID missing";
    }
}
http://localhost:8080/api/employeeswithrequiredfalse 
---- 
ID missing

5.2、使用 java.util.Optional

Spring 4.1 开始,还可以使用 java.util.Optional<T>(在 Java 8+ 中可用)来处理非必须的路径变量:

@GetMapping(value = { "/api/employeeswithoptional", "/api/employeeswithoptional/{id}" })
@ResponseBody
public String getEmployeesByIdWithOptional(@PathVariable Optional<String> id) {
    if (id.isPresent()) {
        return "ID: " + id.get();
    } else {
        return "ID missing";
    }
}

现在,如果不在请求中指定路径变量 id,就会得到默认响应:

http://localhost:8080/api/employeeswithoptional 
----
ID missing 

5.3、使用 Map<String, String> 类型的方法参数

如前所述,可以使用 java.util.Map 类型的单个方法参数来处理请求 URI 中的所有路径变量。

还可以使用这种策略来处理可选路径变量的情况:

@GetMapping(value = { "/api/employeeswithmap/{id}", "/api/employeeswithmap" })
@ResponseBody
public String getEmployeesByIdWithMap(@PathVariable Map<String, String> pathVarsMap) {
    String id = pathVarsMap.get("id");
    if (id != null) {
        return "ID: " + id;
    } else {
        return "ID missing";
    }
}

6、@PathVariable 默认值

在默认情况下,没有提供为使用 @PathVariable 注解的方法参数定义默认值的机制。然而,可以使用上面介绍的相同策略来满足 @PathVariable 的默认值情况,只需在路径变量上检查 null 值即可。

例如,可以使用 java.util.Optional<String> 来识别路径变量是否为 null。如果是 null 值,就可以使用默认值来响应请求:

@GetMapping(value = { "/api/defaultemployeeswithoptional", "/api/defaultemployeeswithoptional/{id}" })
@ResponseBody
public String getDefaultEmployeesByIdWithOptional(@PathVariable Optional<String> id) {
    if (id.isPresent()) {
        return "ID: " + id.get();
    } else {
        return "ID: Default Employee";
    }
}

7、总结

本文介绍了如何使用 Spring 的 @PathVariable 注解来处理 URI 中的路径变量,以及如何处理可选参数和参数默认值的问题。


Ref:https://www.baeldung.com/spring-pathvariable