
Todos nós escrevemos métodos simples em controladores que trabalham através de identificadores numéricos.
@RequestMapping(value = {"/entityName/{entityId}/get"}, method = RequestMethod.GET) @ResponseBody public Entity get(@PathVariable(value = "entityId") Integer entityId) {
O ID recebido deve ser validado.
Por exemplo, nem todos os usuários têm acesso a todos os IDs.
Entende-se que o uso de UUIDs é mais seguro e mais confiável. Mas deve ser criado, armazenado, indexado etc. Para todas as entidades, isso é longo e difícil. Em muitos casos, é mais fácil trabalhar com códigos numéricos regulares.
A validação pode ser feita de uma maneira simples:
@RequestMapping(value = {"/entityName/{entityId}/get"}, method = RequestMethod.GET) @ResponseBody public Entity get(@PathVariable(value = "entityId") Integer entityId) { if(!@dao.validate(entityId)) return some_error;
Existe apenas uma vantagem nesta solução. Simples e rápido.
Tudo o resto é ruim. Duplicação de código, validação não é compatível com a validação de objeto; é necessário processamento separado em cada método.
Eu quero fazer isso:
@RequestMapping(value = {"/entityName/{entityId}/get"}, method = RequestMethod.GET) @ResponseBody public Entity get(@Validated @PathVariable(value = "entityId") Integer entityId) {
Esta opção simples e lógica não funciona. O validador simplesmente não está sendo chamado. A validação de PathVariable by Spring não é suportada.
Para que esta opção funcione, você precisa transformar PathVariable em ModelAttribute:
@ModelAttribute private Integer integerAsModelAttribute(@PathVariable("entityId") Integer id) { return id; }
E obtenha um erro ao acessar o controlador. Os wrappers de tipos genéricos não têm um construtor padrão sem parâmetros e não há setter. Isso funciona usando o Opcional. Ele tem um construtor padrão e um setter aceitando ints comuns.
Transforme Inteiro em Opcional:
@ModelAttribute private Integer integerAsModelAttribute(@PathVariable("entityId") Optional<Integer> id) { return id.orElse(null); }
E, portanto, o próprio método do controlador e a declaração do validador:
@InitBinder({"entityId"}) protected void initCommissionIdBinder(WebDataBinder binder) { binder.setValidator(validateEntityIdValidator); binder.setBindEmptyMultipartFiles(false); } @RequestMapping(value = {"/entityName/{entityId}/get"}, method = RequestMethod.GET) @ResponseBody public Entity get(@Validated @ModelAttribute(value = "entityId") Integer entityId) {
A classe validadora é absolutamente normal:
public class ValidateIntegerValidator implements Validator { @Override public boolean supports(Class<?> aClass) { return Integer.class.equals(aClass); } @Override public void validate(Object o, Errors errors) { if(o instanceof Integer) { Integer integer = (Integer) o; if(!dao.checkId(integer)) { errors.reject("-1", "ERROR"); } } else { errors.reject("-2","WRONG TYPE"); } } }
Um exemplo de trabalho completo pode ser
obtido aqui .