Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 870
Nelio Alves Avatar asked Oct 07 '17 05:10

Nelio Alves


1 Answers

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;
    }
}
like image 100
Chi Dov Avatar answered Sep 29 '22 11:09

Chi Dov