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.

As always, the full source code of the examples is available over on GitHub.