In this article, I’d like to share with you the steps and code examples for validating path variables (or path parameters) of REST APIs with Spring Boot starter validation and Jakarta Bean validation annotations.

A path parameter is a variable in the request URI. Consider the following Delete student API:

DELETE /api/v1/students/{id}

This operation removes a student identified by a specific ID, so {id} is a path variable or path parameter. A couple of concrete requests would be like this:

DELETE /api/v1/students/22

DELETE /api/v1/students/38

In Java code with Spring, it’s equivalent to the following handler method in a REST controller class:

package net.codejava;

@RestController
@RequestMapping("/api/students")
public class StudentApiController {

	//...
	
	@DeleteMapping("/{id}")
	public ResponseEntity<?> delete(@PathVariable("id") Integer id){

		// code that deletes student by ID...
	}
			
}
So, how to validate the value of the parameter id that binds with the path variable {id} in the request? For example, the ID value must be a number and greater than 0. Otherwise an error message should be sent in the response body to the client, with HTTP status code 400 (Bad Request).

Basically, do the following steps to validate path parameters for a REST API:

  • Use constraint annotations provided by Jakarta Bean Validation or Hibernate Validator
  • Mark the REST controller as @Validated
  • Code or update the exception handler class for customizing error response
For example, you can use the @Positive constraint annotation to require that a value must be a strictly positive number. So update the REST controller class as follows:

package net.codejava;

import jakarta.validation.constraints.Positive
import org.springframework.validation.annotation.Validated;

@RestController
@RequestMapping("/api/students")
@Validated
public class StudentApiController {

	//...
	
	@DeleteMapping("/{id}")
	public ResponseEntity<?> delete(@PathVariable("id") @Positive Integer id){

		// code that deletes student by ID...
	}
			
}
And for modifying the default error response, create the ErrorDTO class as below:

package net.codejava;

import java.util.*;

public class ErrorDTO {
	private Date timestamp;
	private int status;
	private String path;
	private List<String> errors = new ArrayList<>();

	public void addError(String message) {
		this.errors.add(message);
	}

	// getters and setters are not shown
}
And code a global exception handler class as follows:

package net.codejava;

import java.util.Date;

import javax.validation.ConstraintViolationException;

import org.slf4j.*;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
public class GlobalExceptionHandler {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);


	@ExceptionHandler(ConstraintViolationException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ResponseBody
	public ErrorDTO handleConstraintViolationException(HttpServletRequest request, Exception ex) {
		ErrorDTO error = new ErrorDTO();
		
		error.setTimestamp(new Date());
		error.setStatus(HttpStatus.BAD_REQUEST.value());
		error.addError(ex.getMessage());
		error.setPath(request.getServletPath());
		
		LOGGER.error(ex.getMessage(), ex);
		
		return error;
	}	
	
}


This exception handler will intercept all handler methods of controllers in the application. If a validation fails, a ConstraintViolationException is thrown, which is then caught by this handler. Then it will send a response with status 400 (Bad Request) and the error details as a JSON document in the response body.

For example, if we test the API using the following curl command:

curl -X DELETE localhost:8080/api/students/-1

Then it returns the response as follows, because value -1 is not a positive number:

< HTTP/1.1 400 
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Fri, 24 Feb 2023 11:31:57 GMT
< Connection: close

{
   "errors": [
      "delete.id: must be greater than 0"
   ],
   "path": "/api/students/-1",
   "status": 400,
   "timestamp": "2023-02-24T11:31:57.380+00:00"
}
You can also specify a custom error message if the validation fails, for example:

@DeleteMapping("/{id}")
public ResponseEntity<?> delete(@PathVariable("id") @Positive(message = "Student ID must be greater than zero") Integer id){
	// ...
}
Using this curl command for testing:

curl -X DELETE localhost:8080/api/students/0

And the error response:

< HTTP/1.1 400 
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Fri, 24 Feb 2023 11:33:08 GMT
< Connection: close

{
   "errors": [
      "delete.id: Student ID must be greater than zero"
   ],
   "path": "/api/students/0",
   "status": 400,
   "timestamp": "2023-02-24T11:33:08.518+00:00"
}
That’s how to validate path variable of REST API requests with Spring Boot and Jakarta validation constraints. I hope you found the code examples helpful. I recommend you take this course to gain extensive experience in REST API development with Spring and related technologies.

 

NOTES:

  • You must add the spring-boot-starter-validation dependency to your project. Follow my guide in this article.
  • You can use other constraints such as @Digits, @Email, @Min, @Max, etc. See the full list of Jakarta validation constraints.
 

Watch the following video to see the coding in action:

 

Related REST API Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He started programming with Java in the time of Java 1.4 and has been falling in love with Java since then. Make friend with him on Facebook and watch his Java videos you YouTube.



Add comment