An optional path variable is a URI template variable that a request may leave out, so one handler method serves both the URL with the variable and the URL without it. In Spring WebMVC, the annotation @PathVariable binds a URI template variable to a method parameter, and its attribute required or the Java Optional type makes that binding optional. We use an optional path variable when one controller method both creates a record and edits an existing record. The following examples show two approaches, which are @PathVariable(required = false) and multiple mappings with an Optional parameter.
1. When We Need an Optional @PathVariable
A controller method often performs more than one task, which is not recommended. For example, a Spring MVC controller method can create a new record or edit an existing record, depending on whether the id attribute of the record is present. The following method has the single mapping /edit/{id} for both cases.
@RequestMapping(path = {"/edit/{id}"})
public String createOrUpdateEmployee(@PathVariable("id") Long id, Employee employee) throws RecordNotFoundException
{
if(id == null) {
//create
} else {
//update
}
}
This code looks correct, but a request to the URL /edit fails at runtime, because the mapping requires the id path variable.
/edit/123
/edit/1000
/edit - ERROR !!
So we cannot skip the id parameter, and it is never null.
2. How to Make an Optional Path Variable
2.1. Using @PathVariable(required = false) – Spring 4.3.3
The annotation @PathVariable has the boolean attribute required, which marks a path variable as mandatory or optional. When we set required to false, we store the value of the parameter in the Java 8 Optional class.
@RequestMapping(path = {"/edit/{id}"})
public String editEmployeeById(@PathVariable(required = false) Optional<Long> id)
throws RecordNotFoundException
{
if(id.isPresent()) {
//update
} else {
//create
}
}
2.2. Using Java 8 Optional Parameter Type and Multiple Mappings
In earlier versions of Spring, we could use multiple path mappings in the @RequestMapping annotation. We map both /edit and /edit/{id} to the same handler method.
@RequestMapping(path = {"/edit", "/edit/{id}"})
public String editEmployeeById(@PathVariable Optional<Long> id) throws RecordNotFoundException
{
if(id.isPresent()) {
//update
} else {
//create
}
}
With both mappings, all the URLs are mapped to the handler method, and the request to /edit no longer fails.
/edit/123
/edit/1000
/edit - WORKS NOW !!
3. Conclusion
Mapping the path variable as optional lets one handler method serve both the create case and the edit case. Drop me your comments related to how to make path variable optional in spring boot.
Happy Learning !!