javaspringspring-bootvalidation

Validation of custom query path variables in spring boot


I am trying to validate two custom query path variables in this custom query:

@Slf4j
@AllArgsConstructor
@RestController
@Validated
@RequestMapping("api/standing_orders")
public class StandingOrderController {
...
@GetMapping("/by_validity_date_from_to")
    public ResponseEntity<List<StandingOrderResponseDTO>> getByValidFromBetween(
            @StartDateBeforeEndDate @RequestParam LocalDate startDate, @StartDateBeforeEndDate @RequestParam LocalDate endDate) {
        log.info("Received request to find standing order valid from {} to {}", startDate, endDate);
        List<StandingOrderResponseDTO> orders = standingOrderService.findByValidFromBetween(startDate, endDate);
        log.info(orders.toString());
        return ResponseEntity.ok(orders);
    }

This custom query is for showing the standing orders between two dates. I want to make sure that the starting date is not later than the ending date, so I am making a ConstraintValidator for it:

package sk.softec.akademia.demo.validation;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = StartDateBeforeEndDateValidator.class)
public @interface StartDateBeforeEndDate {
    String message() default "Start date cannot be later than end date";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

and

package sk.softec.akademia.demo.validation;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import sk.softec.akademia.demo.DTO.StandingOrderRequestDTO;

import java.time.LocalDate;

public class StartDateBeforeEndDateValidator implements ConstraintValidator<StartDateBeforeEndDate, Object[]> {
    @Override
    public void initialize(StartDateBeforeEndDate value) {}

    @Override
    public boolean isValid(Object[] values, ConstraintValidatorContext context) {
        if (values == null || values.length < 2 || !(values[0] instanceof LocalDate) || !(values[1] instanceof LocalDate)) {
            return true;
        }
        LocalDate startDate = (LocalDate) values[0];
        LocalDate endDate = (LocalDate) values[1];
        return startDate == null || endDate == null || !startDate.isAfter(endDate);
    }

Unfortunately, it doesn't work as intented, because I get this: No validator could be found for constraint ...demo.validation.StartDateBeforeEndDate' validating type 'java.time.LocalDate'. Check configuration for 'getByValidFromBetween.startDate'

I don't want to make a new DTO for it, so my question is, is it actually possible to validate two request parameters together?

Any help would be much appreciated.


Solution

  • Haven't tried it, but you can try to pass additional to your parameters list Spring WebRequest as described here
    Than apply StartDateBeforeEndDateValidator on WebRequest. With WebRequest.getParameter you will have access to passed parameters as String

    public class StartDateBeforeEndDateValidator implements ConstraintValidator<StartDateBeforeEndDate, WebRequest> {
        @Override
        public void initialize(StartDateBeforeEndDate value) {}
    
        @Override
        public boolean isValid(WebRequest request, ConstraintValidatorContext context) {
      
            LocalDate startDate = LocalDate.parse(request.getParameter("startDate");
            LocalDate endDate = LocalDate.parse(request.getParameter("endDate"); //null check. parse check to be added
            return startDate == null || endDate == null || !startDate.isAfter(endDate);
        }
    

    controller

        @GetMapping("/by_validity_date_from_to")
        public ResponseEntity<List<StandingOrderResponseDTO>> getByValidFromBetween(
                 @RequestParam LocalDate startDate, @RequestParam LocalDate endDate, @StartDateBeforeEndDate WebRequest request ) {
            log.info("Received request to find standing order valid from {} to {}", startDate, endDate);
            List<StandingOrderResponseDTO> orders = standingOrderService.findByValidFromBetween(startDate, endDate);
            log.info(orders.toString());
            return ResponseEntity.ok(orders);
        }