Solution # 1:
It is configured as follows:
Error view: @RequestMapping(value = URL, params="error") Normal view: @RequestMapping(value = URL, params="proceed")
You can try redirecting like this:
Method Interceptor invoke ():
if (!valid){ // RequestDispatcher rd = request.getRequestDispatcher(errorView); // rd.forward(request, response); response.sendRedirect(errorView); }
Disadvantage: the browser will execute the second request, so the old method parameters are no longer in httpservletrequest.
WorkArround: To avoid flaws, you can use Spring MVC Flash Attribute. You can follow this guide to learn how Flash Attribute works.
Refs: FlashAttributesExample
Solution # 2:
How can I "correct" fix the problem without resorting to the workaround shown above? Is there a more standard way to redirect the correct spring controller?
You can enable by implementing your own RequestMappingHandlerAdapter .
Solution # 3:
Here is the code for the aspect:
public class RequestBodyValidatorAspect { private Validator validator; @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)") private void controllerInvocation() { } @Around("controllerInvocation()") public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); Annotation[][] argAnnotations = method.getParameterAnnotations(); String[] argNames = methodSignature.getParameterNames(); Object[] args = joinPoint.getArgs(); for (int i = 0; i < args.length; i++) { if (hasRequestBodyAndValidAnnotations(argAnnotations[i])) { validateArg(args[i], argNames[i]); } } return joinPoint.proceed(args); } private boolean hasRequestBodyAndValidAnnotations(Annotation[] annotations) { if (annotations.length < 2) return false; boolean hasValid = false; boolean hasRequestBody = false; for (Annotation annotation : annotations) { if (Valid.class.isInstance(annotation)) hasValid = true; else if (RequestBody.class.isInstance(annotation)) hasRequestBody = true; if (hasValid && hasRequestBody) return true; } return false; } @SuppressWarnings({"ThrowableInstanceNeverThrown"}) private void validateArg(Object arg, String argName) { BindingResult result = getBindingResult(arg, argName); validator.validate(arg, result); if (result.hasErrors()) { throw new HttpMessageConversionException("Validation of controller input parameter failed", new BindException(result)); } } private BindingResult getBindingResult(Object target, String targetName) { return new BeanPropertyBindingResult(target, targetName); } @Required public void setValidator(Validator validator) { this.validator = validator; } }
One of the limitations of this work is that it can only use one validator for all controllers. You can also avoid this.
public class TypeMatchingValidator implements Validator, InitializingBean, ApplicationContextAware { private ApplicationContext context; private Collection<Validator> validators; public void afterPropertiesSet() throws Exception { findAllValidatorBeans(); } public boolean supports(Class clazz) { for (Validator validator : validators) { if (validator.supports(clazz)) { return true; } } return false; } public void validate(Object target, Errors errors) { for (Validator validator : validators) { if (validator.supports(target.getClass())) { validator.validate(target, errors); } } } private void findAllValidatorBeans() { Map<String, Validator> validatorBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, Validator.class, true, false); validators = validatorBeans.values(); validators.remove(this); } public void setApplicationContext(ApplicationContext context) throws BeansException { this.context = context; } }
Spring XML configuration file using validator and meta validator together:
<aop:aspectj-autoproxy proxy-target-class="true"/> <bean id="validatorAspect" class="com.something.RequestBodyValidatorAspect"> <property name="validator" ref="validator"/> </bean> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="webBindingInitializer"> <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer" p:validator-ref="validator"/> </property> </bean> <bean id="validator" class="com.something.TypeMatchingValidator"/> <bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/> <bean class="com.something.PersonValidator"/> <bean class="com.something.AccountValidator"/>
Resources Refs: scottfrederick: pring-3-Validation-Aspect
Solution # 4:
Another solution for validating a form with aop, you can check out the blog: form-validation-using-aspect-oriented-programming-aop-in-spring-framework