«1. Обзор

В этом кратком руководстве мы обсудим распространенную проблему при работе с Spring MVC — при использовании Spring @PathVariable с @RequestMapping для сопоставления конца URI запроса, содержащего точку, мы в итоге получим частичное значение в нашей переменной, усеченное до последней точки.

В следующих разделах мы сосредоточимся на том, почему это происходит и как изменить это поведение.

Введение в Spring MVC см. в этой статье.

2. Нежелательная справка Spring

Фреймворк вызывает такое часто нежелательное поведение из-за того, как он интерпретирует переменную пути.

В частности, Spring считает, что все, что стоит за последней точкой, является расширением файла, таким как .json или .xml.

В результате он усекает значение для получения параметра.

Давайте рассмотрим пример использования переменных пути, а затем проанализируем результат с различными возможными значениями:

@RestController
public class CustomController {
    @GetMapping("/example/{firstValue}/{secondValue}")
    public void example(@PathVariable("firstValue") String firstValue,
      @PathVariable("secondValue") String secondValue) {
        // ...  
    }
}

В приведенном выше примере давайте рассмотрим следующие запросы и оценим наши переменные:

    пример URL/галерея /link приводит к оценке firstValue = «gallery» и secondValue = «link» при использовании URL-адреса example/gallery.df/link.ar, у нас будет firstValue = «gallery.df» и secondValue = «link». € с URL-адресом example/gallery.df/link.com.ar наши переменные будут: firstValue = «gallery.df» и secondValue = «link.com»

Как мы видим, первая переменная не не влияет, но второй всегда усекается.

3. Решения

Один из способов решить эту проблему — изменить наше определение @PathVariable, добавив сопоставление с регулярным выражением. Таким образом, любая точка, включая последнюю, будет считаться частью нашего параметра:

@GetMapping("/example/{firstValue}/{secondValue:.+}")   
public void example(
  @PathVariable("firstValue") String firstValue,
  @PathVariable("secondValue") String secondValue) {
    //...
}

Еще один способ избежать этой проблемы — добавить косую черту в конце нашей переменной @PathVariable. Это будет заключать в себя нашу вторую переменную, защищая ее от поведения Spring по умолчанию:

@GetMapping("/example/{firstValue}/{secondValue}/")

Два приведенных выше решения применимы к одному сопоставлению запроса, которое мы модифицируем.

Если мы хотим изменить поведение на глобальном уровне MVC, нам нужно предоставить пользовательскую конфигурацию. Для этой цели мы можем расширить WebMvcConfigurationSupport и переопределить его метод getPathMatchConfigurer() для настройки PathMatchConfigurer.

@Configuration
public class CustomWebMvcConfigurationSupport extends WebMvcConfigurationSupport {

    @Override
    protected PathMatchConfigurer getPathMatchConfigurer() {
        PathMatchConfigurer pathMatchConfigurer = super.getPathMatchConfigurer();
        pathMatchConfigurer.setUseSuffixPatternMatch(false);

        return pathMatchConfigurer;
    }
}

Мы должны помнить, что этот подход влияет на все URL.

С этими тремя параметрами мы получим тот же результат: при вызове URL-адреса example/gallery.df/link.com.ar наша переменная secondValue будет оцениваться как «link.com.ar», что равно что мы хотим.

3.1. Уведомление об устаревании

Начиная с Spring Framework 5.2.4, метод setUseSuffixPatternMatch(boolean) устарел, чтобы не поощрять использование расширений пути для маршрутизации запросов и согласования содержимого. По сути, текущая реализация затрудняет защиту веб-приложений от атаки отраженной загрузки файлов (RFD).

Кроме того, начиная с Spring Framework 5.3, сопоставление шаблонов суффиксов будет работать только для явно зарегистрированных суффиксов, чтобы предотвратить произвольные расширения.

Суть в том, что начиная с Spring 5.3 нам не нужно будет использовать setUseSuffixPatternMatch(false), поскольку по умолчанию он отключен.

4. Заключение

В этом кратком обзоре мы рассмотрели различные способы решения общей проблемы при работе с @PathVariable и @RequestMapping в Spring MVC и источник этой проблемы.

Как всегда, полный исходный код примеров доступен на GitHub.