
Todos a menudo escribimos métodos simples en controladores que trabajan a través de identificadores numéricos.
@RequestMapping(value = {"/entityName/{entityId}/get"}, method = RequestMethod.GET) @ResponseBody public Entity get(@PathVariable(value = "entityId") Integer entityId) {
La identificación entrante debe ser validada.
Por ejemplo, no todos los usuarios tienen acceso a todas las ID.
Se entiende que el uso de UUID es más seguro y más confiable. Pero debe ser creado, almacenado, indexado, etc. Para todas las entidades, hacer esto es largo y difícil. En muchos casos, es más fácil trabajar con ID numéricos regulares.
La validación se puede hacer de una manera simple:
@RequestMapping(value = {"/entityName/{entityId}/get"}, method = RequestMethod.GET) @ResponseBody public Entity get(@PathVariable(value = "entityId") Integer entityId) { if(!@dao.validate(entityId)) return some_error;
Solo hay una ventaja en esta solución. Simple y rapido.
Todo lo demás es malo. Duplicación de código, la validación no es compatible con la validación de objetos; se necesita un procesamiento separado en cada método.
Quiero hacer esto:
@RequestMapping(value = {"/entityName/{entityId}/get"}, method = RequestMethod.GET) @ResponseBody public Entity get(@Validated @PathVariable(value = "entityId") Integer entityId) {
Esta opción simple y lógica no funciona. El validador simplemente no se llama. La validación de PathVariable por Spring no es compatible.
Para que esta opción funcione, debe convertir PathVariable en ModelAttribute:
@ModelAttribute private Integer integerAsModelAttribute(@PathVariable("entityId") Integer id) { return id; }
Y recibe un error al acceder al controlador. Los contenedores de tipos genéricos no tienen un constructor predeterminado sin parámetros y no hay configurador. Esto se soluciona usando Opcional. Tiene un constructor predeterminado y un setter que acepta entradas ordinarias.
Convertir entero en opcional:
@ModelAttribute private Integer integerAsModelAttribute(@PathVariable("entityId") Optional<Integer> id) { return id.orElse(null); }
Y en consecuencia, el método del controlador en sí y la declaración del 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) {
La clase de validador es 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"); } } }
Un ejemplo de trabajo completo se puede
tomar aquí .