StackTips

How to Validate the @PathVariable in Spring Boot?

Feb 14, 2024 Spring Boot 3.06K 

The @PathVariable in spring is used to handle the template variables in the request URI mapping and extract them into method parameter.

For example, in the following controller the vehicleId is a path variable mapped directly to the the updateModel() controller method.

@PostMapping("/vehicles/{vehicleId}")
public ResponseEntity updateModel(@PathVariable String vehicleId, 
				@Valid @RequestBody VehicleDto vehicle) throws Exception {
    // Your implementation goes here
}

Validation can range from simple checks like ensuring the vehicleId is not null or empty, to more complex validations like checking if it adheres to a specific format or verifying against the database. To validate the @PathVariable in a Spring Boot application, we have several options:

Here are some common approaches:

Basic Non-Null or Non-Empty Validation

We can use the @NotNull or @NotEmpty annotations to ensure that the vehicleId is not null or empty.

@PostMapping("/vehicles/{vehicleId}")
public ResponseEntity updateModel(@PathVariable @NotEmpty String vehicleId, 
			@Valid @RequestBody VehicleDto vehicle) throws Exception {
    // Your implementation goes here
}

Custom Validation with Regular Expressions

To validate a specific pattern like a specific length or containing only numbers, you can use the @Pattern annotation from the javax.validation.constraints package and use the regular expressions.

@PostMapping("/vehicles/{vehicleId}")
public ResponseEntity updateModel(@PathVariable @Pattern(regexp = "\d{4,6}") String vehicleId, 
		@Valid @RequestBody VehicleDto vehicle) throws Exception {
    // Your implementation goes here
}

The regular expressions \d{4,6} allows only numbers between 4 and 6 characters length.

Using Custom Validator

We can use the custom validator if we have more complex validations such as checking against a database. For this we can implement the ConstraintValidator interface and inside the isValid method it should return either true or false based on the business logic.

@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = VehicleIdValidator.class)
public @interface ValidVehicleId {

    String message() default "Invalid vehicheId";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}


public class VehicleIdValidator implements ConstraintValidator<ValidVehicleId, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // validation logic goes here
        return true;
    }
}

Now here is how we can use the our custom validator in the controller mapping.

@PostMapping("/vehicles/{vehicleId}")
public ResponseEntity updateModel(@PathVariable @ValidVehicleId String vehicleId, 
		@Valid @RequestBody VehicleDto vehicle) throws Exception {
    // Your implementation goes here
}

Manual Validation Inside the Method

It is recommended to use one of the above method for validating the @PathVariable. But otherwise we can also validate manually inside the method implementation and throw custom exceptions if the validation fails..

@PostMapping("/vehicles/{vehicleId}")
public ResponseEntity updateModel(
		@PathVariable @ValidVehicleId String vehicleId, 
		@Valid @RequestBody VehicleDto vehicle) throws Exception {
    if (vehicleId == null || vehicleId.isEmpty()) {
        throw new MyCustomValiationException("Vehicle id is invalid")
    }

    // Your implementation goes here
}

nilan avtar

Nilanchala

I'm a blogger, educator and a full stack developer. Mainly focused on Java, Spring and Micro-service architecture. I love to learn, code, make and break things.