Spring @PathVariable and @RequestParam

This article explores the difference between @PathVariable and @RequestParam annotations in Spring, as well as compares these to their equivalents in the Java/Jakarta EE provided @PathParam and @QueryParam annotations. Additionally, we will look into the encoding of parameter values and how to make the parameters optional to enhance flexibility.

1. Passing Parameters in URLs

Generally, a URL can contain two types of request parameters i.e. path parameters and query parameters. Here’s an example of a URL that includes both:

http://localhost:8080/users/{id}/accounts?type=current&status=active

In the above URL:

  • id is the path parameter
  • type and status are the query parameters

2. Spring @PathVariable and @RequestParam Annotations

Spring supports accepting path variables and query parameters using easy-to-use annotations.

@GetMapping("/users/{id}/accounts")
public List<Accounts> getAccounts(@PathVariable String id, @RequestParam String type, @RequestParam String status) {

    //...
}

Consider the following request, and see how the request parts are populated into method arguments of the handler method.

  • The @PathVariable extracts the URI template variables from the request URL. For example, in the URI template “/users/{id}/accounts”, the value of id is mapped to a value 101. In runtime, the method parameter id is initialized with the value 101 for this reason.
  • The @RequestParam extracts the query parameters, form parameters, and even files from the request. In our example, we have two query parameters: type and status. Any value passed in these parameters will be used to initialize the method arguments type and status.

This way, we can pass both dynamic values in the URL path and additional parameters as query parameters to provide more information to the server when making a request.

If the same query parameter is passed multiple times in a URL then the first value (lexically) of the parameter is used.

2.1. Naming Method Variables Different from URI Template Variables

In the previous example, we didn’t define the name of the path variable since the names of the method parameter and the path variable were the same. However, if the path variable name is different from the URI template variable, we must provide the name in the @PathVariable annotation:

In the following handler method, we are mapping the id path variable to userId method parameter. 

@GetMapping("/user/{id}")
public String getUserById(@PathVariable(name = "id") String userId) {
    //...
}

Similarly, @RequestParam name also can be configured using the name attribute. In the following example, any query parameter sent with ‘?name=‘ will be populated into username variable.

@GetMapping("/users")
public String getUsers(@RequestParam(name = "name") String username) {
    //...
}

2.2. Collecting Multiple Parameters and Values in a Map

Consider the URL: http://localhost:8080/users/{id}/{name}/accounts?type=current&status=active. Similarly, there can be N number of path variables and query parameters for a complex API. Additionally, the client may not be sending all the parameters in each request, thus many variables will not be utilized in each request.

In such cases, when API has more than one path variable and request parameters, we can use a Map to collect the parameters and values instead of naming each parameter individually.

//Example URL: /users/location/IN/region/DEL?status=active&minAge=18&maxAge=60

@GetMapping("/users/country/{countryCode}/state/{stateCode}")
public String getUsers(@PathVariable Map<String, String> locationMap, @RequestParam Map<String,String> queryMap) {

    //...
}

In the above example, if we invoke the URL: /users/location/IN/region/DEL?status=active&minAge=18&maxAge=60, the content of the maps will be:

  • locationMap => [countryCode=IN, stateCode=DEL]
  • queryMap => [status=active, minAge=18, maxAge=60]

3. Support for Optional Parameters

3.1. Using Attribute ‘require= false

By default, all the method parameters annotated with @RequestParam and @PathVariable are required. To make a @PathVariable or @RequestParam parameter optional, the required attribute can be used by setting its value to false. Its default value is true.

In the following example, passing both query parameters, type and status, is optional.

@GetMapping("/user/{id}")
public String getAccounts(@PathVariable String id, 
  @RequestParam(required = false) String type, 
  @RequestParam(required = false)  String status) {

    //...
}

3.2. Using Java 8 Optional

Alternatively, we can use java.util.Optional to handle a non-mandatory path variable or request params. TheOptional gives us the flexibility to either throw an error or provide a default value if the value is missing.

The above example using Optional is shown below:

@GetMapping("/user/{id}")
public String getAccounts(@PathVariable Optional<String> String id, 
  @RequestParam Optional<String> type, 
  @RequestParam Optional<String> status) {
    
    String userId = id. orElseThrow(...);
    String userType = type.orElseGet(() -> "GUEST");
    //...
}

4. Setting Default Values

Spring framework lacks a built-in capability to handle default values for method parameters annotated with @PathVariable. Nevertheless, the use of java.util.Optional enables us to ascertain whether the path variable holds a value or not. In case it turns out to be an empty value, we have the option to handle the situation by providing a default value, as shown in the previous section.

However, for method parameters annotated with @RequestParam we can use the defaultValue attribute as a fallback when the parameter is not provided or has an empty value. Using defaultValue attribute automatically makes a parameter optional (required = false).

Consider a URL: http://localhost:8080/users?pageNo=1&pageSize=20. We can set a default value for pageNo and pageSize parameters as shown below:

@GetMapping("/users")
public String getAllUsers(@RequestParam(value = "pageNo", defaultValue = "0") int pageNo,
            @RequestParam(value = "pageSize", defaultValue = "30") int pageSize) {

    //...
}

5. Passing Encoded vs Exact Values to Parameters

The value of @PathVariable parameter is extracted from the URI path so it cannot be not encoded. HTTP request paths cannot have encoded values.

For example, Consider URL: http://localhost:8080/user/A%20BC. The controller method and response returned will be:

@GetMapping("/user/{id}")
public String getUserById(@PathVariable String Id) {
    return "User Id: " + id;
}

---- response ----
User Id: A%20BC

The value is mapped to id parameter as it is without being decoded.

The value of @RequestParam parameters are automatically encoded in the URL when sent, and the controller subsequently decodes it upon receiving.

For example, the controller method and response for URL: http://localhost:8080/users?name=John%20Doe will be:

@GetMapping("/users")
public String getUsers(@RequestParam String name) {
    return "Name: " + name;
}

---- response ----
Name: John Doe

Here the single space in the name “John Doe” is URL encoded to “%20” while sending the request to the server.

6. FAQs

6.1. What is the difference between @PathVariable and @PathParam?

Both, @PathParam and @PathVariable, annotations extract values from the URI path of a request URL. However, they are associated with different frameworks.

The @PathVariable annotation is used in the Spring framework. Whereas, @PathParam annotation is used in the Java/Jakarta EE (Enterprise Edition) framework, specifically in JAX-RS, which is a specification for building RESTful web services in Java. It is available to use in frameworks that implement the specification, such as RESTEasy.

Here’s an example of how we would use @PathParam on a JAX-RS resource method:

@GET
@Path("/user/{id}")
public Response getUserById(@PathParam("id") String id) {

    // ...
}

6.2. What is the difference between @QueryParam and @RequestParam?

Both, @QueryParam and @RequestParam, binds the value(s) of a HTTP query parameter to a resource method parameter.

The @RequestParam annotation is part of the Spring framework. Whereas, @QueryParam annotation is part of the JAX-RS specification and is available to use in frameworks that implement the specification.

Here’s an example of how @QueryParam can be used in JAX-RS:

@GET
@Path("/users")
public Response getUsers(@QueryParam("name") String name) {
    // Method implementation
}

7. Conclusion

In this Spring annotations tutorial, we learned the differences between @RequestParam and @PathVariable in capturing the request parameters and query parameters. We also learned about setting default values to parameters, making them optional as well as passing encoded vs exact values.

We also learned about @PathParam and @QueryParam and their usage.

Happy Learning !!

Comments

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.