Generic Top

The early-bird price of the new Learn Spring Security OAuth course packages will increase by $50 on Wednesday:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we'll learn how to make a path variable optional in Spring. First, we'll describe how Spring binds @PathVariable parameters in a handler method. Then, we'll show different ways of making a path variable optional in different Spring versions.

For a quick overview of path variables, please read our Spring MVC article.

2. How Spring Binds @PathVariable Parameters

By default, Spring will try to bind all parameters annotated with @PathVariable in a handler method with the corresponding variables in the URI template. If Spring fails, it'll not deliver our request to that handler method.

For instance, consider the following getArticle method that attempts (unsuccessfully) to make the id path variable optional:

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

Here, the getArticle method is supposed to serve requests to both /article and /article/{id}. Spring will try to bind the articleId parameter to the id path variable if present.

For instance, sending a request to /article/123 sets the value of articleId to 123.

On the other hand, if we send a request to /article, Spring return status code 500 due to the following exception:

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

This was because Spring couldn't set a value for the articleId parameter as id was missing.

So, we need some way to tell Spring to ignore binding a specific @PathVariable parameter if it has no corresponding path variable, as we'll see in the following sections.

3. Making Path Variables Optional

3.1. Using the required Attribute of @PathVariable

Since Spring 4.3.3, the @PathVariable annotation defines the boolean attribute required for us to indicate if a path variable is mandatory to a handler method.

For example, the following version of getArticle uses the required attribute:

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

Since the required attribute is false, Spring will not complain if the id path variable is not sent in the request. That is, Spring will set articleId to id if it's sent, or null otherwise.

On the other hand, if required was true, Spring would throw an exception in case id was missing.

3.2. Using an Optional Parameter Type

The following implementation shows how Spring 4.1, along with JDK 8's Optional class, offers another way to make articleId optional:

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

Here, Spring creates the Optional<Integer> instance, optionalArticleId, to hold the value of id. If id is present, optionalArticleId will wrap its value, otherwise, optionalArticleId will wrap a null value. Then, we can use Optional‘s isPresent(), get(), or orElse() methods to work with the value.

3.3. Using a Map Parameter Type

Another way to define an optional path variable that is available since Spring 3.2 is with a Map for @PathVariable parameters:

@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 {
        //...
    }
}

In this example, the Map<String, String> pathVarsMap parameter collects all path variables that are in the URI as a key/value pairs. Then, we can get a specific path variable using the get() method.

Note that because Spring extracts the value of a path variable as a String, we used the Integer.valueOf() method to convert it to Integer.

3.4. Using Two Handler Methods

In case we were using a legacy Spring version, we can split the getArticle handler method into two methods.

The first method will handle requests to /article/{id}:

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

While the second method will handle requests to /article:

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

4. Conclusion

To sum up, we've discussed how to make a path variable optional in different Spring versions.

As usual, the complete code for this article is available over on GitHub.

Generic bottom

The early-bird price of the new Learn Spring Security OAuth course packages will increase by $50 on Wednesday:

>> CHECK OUT THE COURSE

Leave a Reply

avatar
  Subscribe  
Notify of