example - spring mvc validation bindingresult
Agregar mĂșltiples validadores usando initBinder (7)
Estoy agregando un validador de usuario usando el método initBinder
:
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new UserValidator());
}
Aquí está el UserValidator
public class UserValidator implements Validator {
public boolean supports(Class clazz) {
return User.class.equals(clazz);
}
public void validate(Object target, Errors errors) {
User u = (User) target;
// more code here
}
}
El método de validate
se llama correctamente durante la llamada al método del controlador.
@RequestMapping(value = "/makePayment", method = RequestMethod.POST)
public String saveUserInformation(@Valid User user, BindingResult result, Model model){
// saving User here
// Preparing CustomerPayment object for the payment page.
CustomerPayment customerPayment = new CustomerPayment();
customerPayment.setPackageTb(packageTb);
model.addAttribute(customerPayment);
logger.debug("Redirecting to Payment page.");
return "registration/payment";
}
Pero al regresar a la pantalla de pago recibo este error:
java.lang.IllegalStateException: destino no válido para Validator [com.validator.UserValidator@710db357]: com.domain.CustomerPayment [customerPaymentId = null] org.springframework.validation.DataBinder.setValidator (DataBinder.java:476) com.web. UserRegistrationController.initBinder (UserRegistrationController.java:43) sun.reflect.NativeMethodAccessorImpl.invoke0 (Método nativo) sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39) sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:25) java .lang.reflect.Method.invoke (Method.java:597) org.springframework.web.bind.annotation.support.HandlerMethodInvoker.initBinder (HandlerMethodInvoker.java:393) org.springframework.web.bind.annotation.support.HandlerMethodInvoker .updateModelAttributes (HandlerMethodInvoker.java:222) org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod (AnnotationMethodHandlerAdapter.java:429) org.springframework.web.servlet.mvc.annota tion.AnnotationMethodHandlerAdapter.handle (AnnotationMethodHandlerAdapter.java:414)
Esto puede deberse a que estoy devolviendo un CustomerPayment
y no hay un validador definido para eso.
Tampoco puedo agregar múltiples validadores en el método initBinder
.
¿Cómo puedo arreglar esto?
Declarar solicitud como
(... , Model model,HttpServletRequest request)
y cambio
model.addAttribute(customerPayment);
a
request.setAttribute("customerPayment",customerPayment);
El validador múltiple en un comando es compatible con Spring MVC 4.x ahora. Puede usar este código de fragmento:
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new UserValidator(), new CustomerPaymentValidator());
}
Es un poco complicado de hacer, 1 controlador tiene solo 1 validador en 1 objeto de comando. necesita crear un "Validador compuesto" que obtendrá todos los validadores y los ejecutará por separado.
Aquí hay un tutorial que explica cómo hacerlo: usando múltiples validadores
Hay un truco simple, siempre devuelve true
en el método de supports
y delega la verificación de clase para validate
. Entonces, básicamente, puede agregar múltiples validadores en el initBinder
sin problema.
@Component
public class MerchantRegisterValidator implements Validator {
@Autowired
private MerchantUserService merchantUserService;
@Autowired
private MerchantCompanyService merchantCompanyService;
@Override
public boolean supports(Class<?> clazz) {
return true; // always true
}
@Override
public void validate(Object target, Errors errors) {
if (!XxxForm.getClass().equals(target.getClass()))
return; // do checking here.
RegisterForm registerForm = (RegisterForm) target;
MerchantUser merchantUser = merchantUserService.getUserByEmail(registerForm.getUserEmail());
if (merchantUser != null) {
errors.reject("xxx");
}
MerchantCompany merchantCompany = merchantCompanyService.getByRegno(registerForm.getRegno());
if (merchantCompany != null) {
errors.reject("xxx");
}
}
}
No veo una razón por la cual Spring no filtre todos los validadores que no son aplicables a la entidad actual de forma predeterminada, lo que obliga a usar elementos como CompoundValidator descrito por @ Rigg802.
InitBinder
permite especificar solo el nombre, lo que le brinda cierto control pero no un control total sobre cómo y cuándo aplicar su validador personalizado. Que desde mi punto de vista no es suficiente.
Otra cosa que puede hacer es realizar una comprobación usted mismo y agregar un validador a la carpeta solo si es realmente necesario, ya que la carpeta tiene información de contexto vinculante.
Por ejemplo, si desea agregar un nuevo validador que funcione con su objeto Usuario además de los validadores incorporados, puede escribir algo como esto:
@InitBinder
protected void initBinder(WebDataBinder binder) {
Optional.ofNullable(binder.getTarget())
.filter((notNullBinder) -> User.class.equals(notNullBinder.getClass()))
.ifPresent(o -> binder.addValidators(new UserValidator()));
}
Puede agregar múltiples validadores al iterar sobre org.springframework.validation.Validator en un ApplicationContext y configurar los adecuados en @InitBinder para cada solicitud.
@InitBinder
public void setUpValidators(WebDataBinder webDataBinder) {
for (Validator validator : validators) {
if (validator.supports(webDataBinder.getTarget().getClass())
&& !validator.getClass().getName().contains("org.springframework"))
webDataBinder.addValidators(validator);
}
}
Ver mi proyecto para ejemplos y puntos de referencia simples. https://github.com/LyashenkoGS/spring-mvc-and-jms-validation-POC/tree/benchamark
@InitBinder
establecer el valor de la anotación @InitBinder
en el nombre del comando que desea validar. Esto le dice a Spring a qué aplicar la carpeta; sin él, Spring intentará aplicarlo a todo. Esta es la razón por la que está viendo esa excepción: Spring está tratando de aplicar la carpeta, con su UserValidator
, a un parámetro de tipo CustomerPayment
.
En su caso específico, parece que necesita algo como:
@InitBinder("user")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new UserValidator());
}
Para su segunda pregunta, como explicó Rigg802, Spring no admite la conexión de múltiples validadores a un solo comando. Sin embargo, puede definir múltiples métodos @InitBinder
para diferentes comandos. Entonces, por ejemplo, puede poner lo siguiente en un solo controlador y validar su usuario y los parámetros de pago:
@InitBinder("user")
protected void initUserBinder(WebDataBinder binder) {
binder.setValidator(new UserValidator());
}
@InitBinder("payment")
protected void initPaymentBinder(WebDataBinder binder) {
binder.setValidator(new CustomerPaymentValidator());
}