How to inject PathVariable id into RequestBody *before* JSR-303 validation is executed?
Asked Answered
W

1

9

I'm stuck in an apparently simple problem: I want to perform some custom validation based on the object id in a PUT request.

@RequestMapping(value="/{id}", method=RequestMethod.PUT)
public ResponseEntity<Void> update(@Valid @RequestBody ClientDTO objDto, @PathVariable Integer id) {
    Client obj = service.fromDTO(objDto);
    service.update(obj);
    return ResponseEntity.noContent().build();
}

I'd like to create a custom validator to output a custom message in case I update some field that can't be the same of another object in my database. Something like this:

public class ClientUpdateValidator implements ConstraintValidator<ClientUpdate, ClientDTO> {

    @Autowired
    private ClientRepository repo;

    @Override
    public void initialize(ClientInsert ann) {
    }

    @Override
    public boolean isValid(ClientDTO objDto, ConstraintValidatorContext context) {

        Client aux = repo.findByName(objDto.getName());
        if (aux != null && !aux.getId().equals(objDto.getId())) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("Already exists")
                    .addPropertyNode("name").addConstraintViolation();
            return false;
        }

        return true;
    }
}

However, the object id comes from @PathVariable, not from @RequestBody. I can't call "objDto.getId()" like I did above.

On the other hand, it doesn't make much sense to obligate to fill up the object id in the request body, because this way the path variable would become meaninless.

How can I solve this problem? Is there a way to inject the id from PathVariable into RequestBody object before bean validation is executed? If not, what would be a viable solution? Thanks.

Waterfront answered 7/10, 2017 at 5:52 Comment(2)
Add Code please....Blaineblainey
There you go. Tried my best.Waterfront
B
13

Try to inject httpServletRequest into the custom validator

public class ClientUpdateValidator implements ConstraintValidator<ClientUpdate, ClientDTO> {
    @Autowired
    private HttpServletRequest request;

    @Autowired
    private ClientRepository repo;

    @Override
    public void initialize(ClientInsert ann) {
    }

    @Override
    public boolean isValid(ClientDTO objDto, ConstraintValidatorContext context) {
        // for example your path to put endpoint is /client/{id}
        Map map = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        String id = map.get("id");
        Client aux = repo.findByName(objDto.getName());
        if (aux != null && !aux.getId().equals(id)) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("Already exists")
                    .addPropertyNode("name").addConstraintViolation();
            return false;
        }

        return true;
    }
}
Biting answered 7/10, 2017 at 7:5 Comment(3)
Worked! Thanks a lot! :)Waterfront
Voted but system says my vote doesn't appear as I still got less then 15 points. Thanks again for the valuable help.Waterfront
Thanks for coming back, have fun with the dev.Biting

© 2022 - 2024 — McMap. All rights reserved.