1. Overview
In this short tutorial, weβll discuss a common problem when working with Spring MVC β when using a Spring @PathVariable with a @RequestMapping to map the end of a request URI that contains a dot, weβll end up with a partial value in our variable, truncated at the last dot.
In the next sections, weβll focus on why this happens and how to change this behavior.
For an introduction to Spring MVC, please refer to this article.
2. Unwanted Spring Help
The framework causes this often unwanted behavior because of the way it interprets the path variable.
Specifically, Spring considers that anything behind the last dot is a file extension such as .json or .xml.
As a result, it truncates the value to retrieve the parameter.
Letβs see an example of using path variables, then analyze the result with different possible values:
@RestController
public class CustomController {
@GetMapping("/example/{firstValue}/{secondValue}")
public void example(@PathVariable("firstValue") String firstValue,
@PathVariable("secondValue") String secondValue) {
// ...
}
}
With the above example, letβs consider the next requests and evaluate our variables:
- the URL example/gallery/link results in evaluating firstValue = βgalleryβ and secondValue = βlinkβ
- when using the example/gallery.df/link.ar URL, weβll have firstValue = βgallery.dfβ and secondValue = βlinkβ
- with the example/gallery.df/link.com.ar URL, our variables will be: firstValue = βgallery.dfβ and secondValue = βlink.comβ
As we can see, the first variable isnβt affected but the second is always truncated.
3. Solutions
One way to solve this inconvenience is to modify our @PathVariable definition by adding a regex mapping. Thereby any dot, including the last one, will be considered as part of our parameter:
@GetMapping("/example/{firstValue}/{secondValue:.+}")
public void example(
@PathVariable("firstValue") String firstValue,
@PathVariable("secondValue") String secondValue) {
//...
}
Another way to avoid this issue is by adding a slash at the end of our @PathVariable. This will enclose our second variable protecting it from Springβs default behavior:
@GetMapping("/example/{firstValue}/{secondValue}/")
The two solutions above apply to a single request mapping that weβre modifying.
If we want to change the behavior at a global MVC level, we need to provide a custom configuration. For this purpose, we can extend the WebMvcConfigurationSupport and override its getPathMatchConfigurer() method to adjust a PathMatchConfigurer.
@Configuration
public class CustomWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
@Override
protected PathMatchConfigurer getPathMatchConfigurer() {
PathMatchConfigurer pathMatchConfigurer = super.getPathMatchConfigurer();
pathMatchConfigurer.setUseSuffixPatternMatch(false);
return pathMatchConfigurer;
}
}
We have to remember that this approach affects all URLs.
With these three options, weβ ll obtain the same result: when calling the example/gallery.df/link.com.ar URL, our secondValue variable will be evaluated to βlink.com.arβ, which is what we want.
3.1. Deprecation Notice
As of Spring Framework 5.2.4, the setUseSuffixPatternMatch(boolean) method is deprecated in order to discourage the use of path extensions for request routing and content negotiation. Basically, the current implementation makes it hard to protect web applications against the Reflected File Download (RFD) attack.
Also, as of Spring Framework 5.3, the suffix pattern matching will only work for explicitly registered suffixes, to prevent arbitrary extensions.
The bottom line is, as of Spring 5.3, we wonβt need to use the setUseSuffixPatternMatch(false) since itβs disabled by default.
4. Conclusion
In this quick writeup, weβve explored different ways to solve a common problem when working with @PathVariable and @RequestMapping in Spring MVC and the source of this issue.
