Validación de parámetros genéricos en controladores Spring

imagen
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) { //    ID } 

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; //    ID } 

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) { //    ID } 

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) { //    ID.   . } 

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í .

Source: https://habr.com/ru/post/es425001/


All Articles