Spring Boot REST API Validate Query Parameters Examples
- Details
- Written by Nam Ha Minh
- Last Updated on 16 November 2023   |   Print Email
GET /v1/students?pageSize=10&pageNum=2
This request has 2 query parameters: the first one is pageSize which has value 10 and the second one is pageNum which has value 2.If the request method is POST, then the query parameters will be in the request body - also in form of name=value pairs.And in Java code with Spring, we can bind parameters of a REST controller’s handler method directly to query parameters. For example:@RestController @RequestMapping("/api/v1/students") public class StudentApiController { @GetMapping public ResponseEntity<?> list(int pageSize, int pageNum) { // code that uses query parameters pageSize and pageNum... // return response... } }Here, the method parameter pageSize is bound to the query parameter pageSize, and the same for the method parameter pageNum. If the method parameter and query parameter have different names, we can use the @RequestParam annotation like this:
@GetMapping public ResponseEntity<?> list(@RequestParam("pageSize") int pSize, @RequestParam("pageNum") int pNum) { // ... }Also, the @RequestParam annotation should be used to indicate that a parameter is optional and has default value if not present. For example:
@GetMapping public List<Student> list( @RequestParam(required = false, defaultValue = "10") int pageSize, @RequestParam(required = false, defaultValue = "1") int pageNum) { //... }So, how to validate the values of the query parameters pageSize and pageNum? Suppose that the API requires pageSize must be in the range of 10 to 50 and pageNum must be a positive number.
- Add the dependency spring-boot-starter-validation to the project, if not available
- Use Jakarta Bean/Hibernate Validator’s validation constraint annotations, such as @NotNull, @NotBlank, @Size, @Min, @Max, @Positive…
- Use @Validated annotation for the REST controller class
- Customize error response in a global exception handler class
import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Positive; import org.springframework.validation.annotation.Validated; @RestController @RequestMapping("/api/v1/students") @Validated public class StudentApiController { @GetMapping public ResponseEntity<?> list( @Min(value = 10) @Max(value = 50) int pageSize, @Positive int pageNum) { // code that uses query parameters pageSize and pageNum... // return response... } }As you can see, we use the @Validated annotation for the controller class, so the specified constraint annotations will be called to validate the requests. And we use the @Min and @Max constraints for the query parameter pageSize, and @Positive constraint for pageNum.Run the following curl command to test a negative case:
curl "localhost:8080/api/v1/students?pageSize=5&pageNum=0"
If no custom exception handler is configured, the server will return HTTP status code 500 (Internal Server Error) with the following error details in the response body:{ "timestamp": "2023-03-01T04:56:18.349+00:00", "status": 500, "error": "Internal Server Error", "trace": "jakarta.validation.ConstraintViolationException:...", "message": "list.pageNum: must be greater than 0, list.pageSize: must be greater than or equal to 10", "path": "/api/v1/students" }However, what if we want to customize this error response? Such as sending the status code 400 without exception trace exposed to clients?Here’s an example, code a custom global exception handler class as below:
package net.codejava; import java.util.*; import org.springframework.http.*; import org.springframework.web.bind.annotation.*; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.ConstraintViolationException; @ControllerAdvice public class CustomExceptionHandler { @ExceptionHandler(ConstraintViolationException.class) @ResponseBody @ResponseStatus(HttpStatus.BAD_REQUEST) public Object handleRequestValidationException(Exception ex, HttpServletRequest request) { Map<String, Object> responseBody = new LinkedHashMap<>(); responseBody.put("timestamp", new Date()); responseBody.put("status", HttpStatus.BAD_REQUEST.value()); responseBody.put("error", ex.getMessage()); responseBody.put("path", request.getServletPath()); return responseBody; } }Then, test the API using the same curl command above, you’ll see the error response is now updated:
< HTTP/1.1 400 < Content-Type: application/json < Transfer-Encoding: chunked < Date: Wed, 01 Mar 2023 05:16:27 GMT < Connection: close < { "timestamp": "2023-03-01T05:16:27.022+00:00", "status": 400, "error": "list.pageSize: must be greater than or equal to 10, list.pageNum: must be greater than 0", "path": "/api/v1/students" }You can also specify custom error messages for the validation constraints, as shown in the below example:
@GetMapping public List<Student> list( @Min(value = 10, message = "Page size must greater than 9") @Max(value = 50, message = "Page size must be less than 51") int pageSize, @Positive(message = "Page number must be greater than 0") int pageNum) { //... }Then below is the response for a negative test case:
< HTTP/1.1 400 < Content-Type: application/json < Transfer-Encoding: chunked < Date: Wed, 01 Mar 2023 05:29:58 GMT < Connection: close < { "timestamp": "2023-03-01T05:29:58.815+00:00", "status": 400, "error": "list.pageNum: Page number must be greater than 0, list.pageSize: Page size must greater than 9", "path": "/api/v1/students" }And test the API for a positive case:
curl "localhost:8080/api/v1/students?pageSize=20&pageNum=1"
You should get HTTP status 200 (OK) with student information in the response body.If the query parameters are optional, you should use the @RequestParam annotation like this:@GetMapping public List<Student> list( @RequestParam(required = false, defaultValue = "10") @Min(value = 10) @Max(value = 50) Integer pageSize, @RequestParam(required = false, defaultValue = "1") @Positive Integer pageNum) { //... }Note that in this case, we use wrapper type Integer instead of primitive type int, to avoid conversion of nulls to primitive numbers.Those are some code examples for validating query parameters of REST API requests with Spring Boot starter validation. I hope you find this article helpful. You can see the full list of Jakarta validation constraints here.Watch the following video to see the coding in action:
Recommendation:
Related REST API Tutorials:
- Spring Boot REST API Validate Path Variables Examples
- Spring Boot REST API Request Validation Examples
- REST API Best Practices: How to Use the Right HTTP Methods and Status Codes
- How to Use curl for Testing REST APIs (Test CRUD Operations)
- Java RESTful Web Services Tutorial for Beginner with Jersey and Tomcat
- Java CRUD RESTful Web Services Examples with Jersey and Tomcat
- Spring Boot Hello World RESTful Web Services Tutorial
- Spring Boot RESTful CRUD API Examples with MySQL database
- Spring Boot File Download and Upload REST API Examples
- Spring Boot REST API CRUD with HATEOAS Tutorial
Comments