Validadores + Aspectos: personalizar la validación

Buen dia, Habr!

Después de un tiempo, decidí volver a escribir aquí y compartir mi experiencia. Esta vez, el artículo tratará sobre cómo personalizar validadores estándar y llamarlos donde sea que necesitemos usando los aspectos de Spring. Bueno, me animó a escribir: la falta de dicha información, especialmente en ruso.

El problema


Por lo tanto, la esencia de la aplicación es aproximadamente la siguiente: hay una puerta de enlace, una API que acepta la solicitud y la modifica y redirige aún más al banco correspondiente. Pero la solicitud para cada uno de los bancos fue diferente, así como los parámetros de validación. Por lo tanto, no fue posible validar la solicitud inicial. Había dos formas: usar anotaciones de javax.validation o escribir su propia capa de validación por separado. En el primer caso, hubo un problema: de forma predeterminada, los objetos solo se pueden validar en el controlador. En el segundo caso, también hubo desventajas: esta es una capa adicional, una gran cantidad de código, e incluso si los modelos cambiaran, los validadores tendrían que cambiarse.

Por lo tanto, se decidió encontrar una forma de extraer validadores estándar donde fuera necesario, y no solo en el controlador.

Tiramos validadores


Después de un par de horas de excavación en Google, se encontraron un par de soluciones, la más adecuada de las cuales fue conectar automáticamente javax.validation.Validator y llamar al método de validación, al que debe pasar un objeto validado como parámetro.

Parece que se ha encontrado una solución, pero validar en todas partes el validador no parecía una buena idea, quería una solución más elegante.

Añadir AOP


Sin pensarlo dos veces, decidí intentar adaptar mis aspectos favoritos a esta solución.

La lógica era aproximadamente la siguiente: crear una anotación y colgarla en un método que convierta un objeto en otro. Además en el aspecto, interceptamos todos los métodos marcados con esta anotación y llamamos al método de validación por el valor que devuelven. Ganancia

Entonces anotación:

//      @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Validate {} 


Un método para convertir consultas es:

 @Validate public SomeBankRequest requestToBankRequest(Request<T> request) { SomeBankRequest bankRequest = ...; ... //        ... return bankRequest; } 

Bueno, en realidad el aspecto en sí:

 @Aspect @Component public class ValidationAspect { private final Validator validator; //    public ValidationAspect(Validator validator) { this.validator = validator; } //       // @Validate       @AfterReturning(pointcut = "@annotation(api.annotations.Validate)", returning = "result") public void validate(JoinPoint joinPoint, Object result) { //     Set<ConstraintViolation<Object>> violations = validator.validate(result); //    ,    ,     //        if (!violations.isEmpty()) { StringBuilder builder = new StringBuilder(); //          ,   //  violations.forEach(violation -> builder .append(violation.getPropertyPath()) .append("[" + violation.getMessage() + "],")); throw new IllegalArgumentException("Invalid values for fields: " + builder.toString()); } } } 

Brevemente sobre el aspecto del trabajo:

Interceptamos el objeto devuelto por el método que está marcado con la anotación Validar , luego lo pasamos al método del validador, que devolverá Set<ConstraintViolation<Object>> , en resumen, un conjunto de clases con información diferente sobre campos y errores validados. Si no hay errores, el conjunto estará vacío. Luego, simplemente revisamos el conjunto y creamos un mensaje de error, con todos los campos no validados, y lanzamos la ejecución.

 violation.getPropertyPath() -    violation.getMessage() -  ,       

Conclusión


Por lo tanto, podemos llamar la validación de cualquier objeto que necesitemos en cualquier punto de la aplicación, y si lo deseamos, podemos complementar la anotación y el aspecto para que la validación pase no solo para los métodos que devuelven un objeto, sino también para los campos y parámetros de los métodos.

PS


Además, si llama a un método marcado Validar desde otro método de la misma clase, recuerde la conexión entre aop y proxy .

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


All Articles