How can I make a Spring Validator to be detected and used automatically?

39 Views Asked by At

I want to validate the JSON content of a POST request in my controller. I use @NullValue annotations and a custom Validator for a conditional validation. The input is invalid if the attribute type is A and the attribute name is empty. But I can't get the validator to work.

I have this controller:

@RestController
@RequestMapping("/orders")
@Validated
public class OrderController {
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void createOrder(@Valid @RequestBody Order order) {
      // do something
    }
}

and this Validator for the Order class:

@Component
public class OrderValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz) {
        return Order.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        final var order = (Order) target;

        if (order.getType() == Type.A && Strings.isEmpty(order.getName())) {
            errors.rejectValue("name", "name.required", "Name required for type A");
        }
    }
}

and Order class

@Getter
@Setter
public class OrderDto {
    private String name;
    @NotNull
    private Type type;
}

If I send a POST request where type is null, I get HTTP status 400 (Bad request) as expected.

But when I send a POST request with type=A and name=null the method returns HTTP 201 (Created). The OrderValidator is not executed. Breakpoints prove, that the supports and validate methods are not called. But I can inject a OrderValidator bean, which means the component was found by Spring Boot.

The only work-around I found was to use this as controller, but this does not look feel right to me:

@RestController
@RequestMapping("/orders")
@Validated
public class OrderController {
    @Autowired
    private final OrderValidator orderValidator;

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.addValidators(orderValidator);
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void createOrder(@Valid @RequestBody Order order, BindingResult result) {
      // evaluate bindingResult manually
      // do something
    }

}

What am I doing wrong?

0

There are 0 best solutions below