«1. Обзор

В этом уроке мы узнаем, как сделать переменную пути необязательной в Spring. Во-первых, мы опишем, как Spring связывает параметры @PathVariable в методе обработчика. Затем мы покажем различные способы сделать переменную пути необязательной в разных версиях Spring.

Краткий обзор переменных пути можно найти в нашей статье Spring MVC.

2. Как Spring связывает параметры @PathVariable

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

Например, рассмотрим следующий метод getArticle, который пытается (безуспешно) сделать переменную пути id необязательной:

@RequestMapping(value = {"/article", "/article/{id}"})
public Article getArticle(@PathVariable(name = "id") Integer articleId) {
    if (articleId != null) {
        //...
    } else {
        //...
    }
}

Здесь предполагается, что метод getArticle обслуживает запросы как к /article, так и к /article/{id }. Spring попытается привязать параметр articleId к переменной пути id, если она присутствует.

Например, отправка запроса на /article/123 устанавливает значение articleId равным 123.

С другой стороны, если мы отправляем запрос на /article, Spring возвращает код состояния 500 из-за следующего исключения:

org.springframework.web.bind.MissingPathVariableException:
  Missing URI template variable 'id' for method parameter of type Integer

Это произошло из-за того, что Spring не мог установить значение для параметра articleId, так как идентификатор отсутствовал.

Итак, нам нужен какой-то способ сказать Spring игнорировать привязку определенного параметра @PathVariable, если у него нет соответствующей переменной пути, как мы увидим в следующих разделах.

3. Сделать переменные пути необязательными

3.1. Использование обязательного атрибута @PathVariable

Начиная с Spring 4.3.3 аннотация @PathVariable определяет логический атрибут, необходимый для того, чтобы мы могли указать, является ли переменная пути обязательной для метода обработчика.

Например, в следующей версии getArticle используется атрибут required:

@RequestMapping(value = {"/article", "/article/{id}"})
public Article getArticle(@PathVariable(required = false) Integer articleId) {
   if (articleId != null) {
       //...
   } else {
       //...
   }
}

Поскольку атрибут required равен false, Spring не будет жаловаться, если в запросе не будет отправлена ​​переменная пути id. То есть Spring установит для articleId значение id, если оно отправлено, или null в противном случае.

С другой стороны, если бы требование имело значение true, Spring выдавал бы исключение в случае отсутствия идентификатора.

3.2. Использование необязательного типа параметра

В следующей реализации показано, как Spring 4.1 вместе с классом Optional из JDK 8 предлагает еще один способ сделать articleId необязательным:

@RequestMapping(value = {"/article", "/article/{id}"}")
public Article getArticle(@PathVariable Optional<Integer> optionalArticleId) {
    if (optionalArticleId.isPresent()) {
        Integer articleId = optionalArticleId.get();
        //...
    } else {
        //...
    }
}

Здесь Spring создает экземпляр Optional\u003cInteger\u003e, optionalArticleId, для хранения значения идентификатора. Если id присутствует, optionArticleId будет обертывать его значение, в противном случае optionArticleId будет обертывать нулевое значение. Затем мы можем использовать методы isPresent(), get() или orElse() для работы со значением.

3.3. Использование типа параметра карты

Еще один способ определить необязательную переменную пути, доступную начиная с Spring 3.2, — использовать карту для параметров @PathVariable:

@RequestMapping(value = {"/article", "/article/{id}"})
public Article getArticle(@PathVariable Map<String, String> pathVarsMap) {
    String articleId = pathVarsMap.get("id");
    if (articleId != null) {
        Integer articleIdAsInt = Integer.valueOf(articleId);
        //...
    } else {
        //...
    }
}

В этом примере параметр pathVarsMap Map\u003cString, String\u003e собирает все переменные пути, которые находятся в URI, в виде пар ключ/значение. Затем мы можем получить определенную переменную пути, используя метод get().

Обратите внимание: поскольку Spring извлекает значение переменной пути как строку, мы использовали метод Integer.valueOf() для преобразования его в целое число.

3.4. Использование двух методов обработчика

В случае, если мы использовали устаревшую версию Spring, мы можем разделить метод обработчика getArticle на два метода.

Первый метод будет обрабатывать запросы к /article/{id}:

@RequestMapping(value = "/article/{id}")
public Article getArticle(@PathVariable(name = "id") Integer articleId) {
    //...        
}

Второй метод будет обрабатывать запросы к /article:

@RequestMapping(value = "/article")
public Article getDefaultArticle() {
    //...
}

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

Подводя итог, мы обсудили, как сделать переменную пути необязательной в разных версиях Spring.

Как обычно, полный код этой статьи доступен на GitHub.