How to validate Spring @PathVariable properties first?

Viewed 3065

I have the following PUT method in my Spring Controller class. There is a parameter annotated with @PathVariable. There is another parameter annotated with @RequestBody @Valid. The problem is RequestBody is getting validated first and the method returns in case of exception. But I want @Pathvariable to be validated first and return in case of exception.

@RestController
@Validated
@RequestMapping(value = "/v1/order", produces = MediaType.APPLICATION_JSON_VALUE)
public class OrderMappingController {

    @PutMapping(value = "/{id}/order-mapping", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<?> UpdateOrderMapping(@PathVariable(value = "id") @NotBlank(message = "InvalidID")
                                                @ValidOrderId String id,
                                                @RequestBody @Valid OrderMappingRequest request) {
        //...
    }
}

Thanks in Advance!

2 Answers

You can write custom annotation and validator to validate the @Pathvariable

@RestController
    @Validated
    @RequestMapping(value = "/v1/order", produces = MediaType.APPLICATION_JSON_VALUE)
    public class OrderMappingController {
    
            @PutMapping(value = "/{id}/order-mapping", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
            public ResponseEntity<?> UpdateOrderMapping(@PathVariable(value = "id") @OrderId  String id,
                                                        @RequestBody @Valid OrderMappingRequest request) {
                //...
            }
        }

Create custom annotation OrderId which can validate with custom validator class OrderIdValidator

@Constraint(validatedBy = OrderIdValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD,PARAMETER })
public @interface OrderId{
    public String message() default "order id not found ";
    public Class<?>[] groups() default {};
    public Class<? extends Payload>[] payload() default{};
}

Then you can write the custom validator class which can check the business logic if the id is present in db or any other validation

public class OrderValidator implements ConstraintValidator<OrderId, String> {

    @Autowired
    private OrderRepository repository;

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
            return (repository.existsByPeriod(value));

    }
}

I suggest to try a different approach. Try not to annotate @Valid on your OrderMappingRequest, then implement a custom org.springframework.validation.Validator for your OrderMappingRequest bean which will then be injected and invoke in your OrderMappingController#UpdateOrderMapping method.

public class OrderMappingController {

    @Autowired
    private OrderMappingRequestValidator validator

    @PutMapping("/{id}/order-mapping")
    public ResponseEntity<?> UpdateOrderMapping(@PathVariable 
                                                @NotBlank(message = "InvalidID")
                                                @ValidOrderId String id,
                                                @RequestBody OrderMappingRequest request,
                                                BindingResult result) {
        
       validator.validate(request, result);

       if(result.hasErrors()){
          ... do stuff or throw
       }

    }
}
Related