2016-04-26 14 views
5

春servlet.xmlせる私の迎撃者は春の前に呼び出されますコントローラメソッドであり、Validator注釈で注釈が付けられています。意図は、検証が失敗した場合に要求を検証し、要求を別のビューに転送することです。これは通常動作しています。エラー(!valid)がある場合、RequestDispatcher.forwardが呼び出されます。これにより、別のSpringコントローラメソッドが呼び出され、最終的にエラービューが表示されます。これは通常動作します。春AOPは、再帰呼び出しが

問題:いくつかの春のコントローラの場合

、私のRequestDispatcherのerrorViewは(invoke()が何度も呼び出される)要求が無限ループの原因となるバック同じ方法に転送されます。これは、Springコントローラのリクエストマッピング(下記参照)がどのように設定されているかによると思います。

エラービュー:@RequestMapping(value = URL, params="error")

ノーマルビュー:最初の要求は、リクエストパラメータに「進む」持ってルーティングされますので@RequestMapping(value = URL, params="proceed")

。次に、エラーが発生し、RequestDispatcherがクエリ文字列の 'error'パラメータを持つビューに転送すると、上記の "Error view"メソッドに転送されますが、そうではありません。常に 'proceed'メソッドに転送され、MethodInterceptor invoke()の無限ループが発生します。これは、 'proceed'パラメータがまだHttpServletRequestにあるためです。しかし、これは修正するのは簡単ではありません。なぜなら、インターセプタのポイントは、Springコントローラ自体についての知識がないからです。エラーが発生したかどうかだけを知り、エラーが発生した場合はエラービューに転送する必要があります。

回避策:

は、以下の要求のマッピングを使用して、それが問題を解決します。これは、キー=値表記を使用すると、HttpServletRequestパラメータが上書きされるためです。

エラービュー:@RequestMapping(value = URL, params="view=error")

ノーマルビュー:@RequestMapping(value = URL, params="view=proceed")

質問

どのように私は、 "適切に" 上記の回避策に頼ることなく、問題を解決することができますか?正しいスプリングコントローラに転送する標準的な方法はありますか?

+0

すべてのソースコードをgithubまたは** MethodInterceptor invoke()**で共有できますか?詳細は? – CrawlingKid

答えて

1

溶液#1:

Error view: @RequestMapping(value = URL, params="error") 

Normal view: @RequestMapping(value = URL, params="proceed") 

次のようにリダイレクトするために試みることができる:

MethodInterceptorのインボーク():

if (!valid){ 

// RequestDispatcher rd = request.getRequestDispatcher(errorView); 
// rd.forward(request, response); 
    response.sendRedirect(errorView); 
} 
を次のように構成された

欠点:ブラウザは2番目のリクエストを行います。そのため、古いメソッドパラメータはhttpservletrequestに存在しません。

WorkArround:欠点を避けるには、Spring MVC Flash属性を使用できます。このチュートリアルに従うことで、Flash属性の仕組みを知ることができます。

参考文献:FlashAttributesExample

溶液#2:

どのように私は、 "適切に" 上記の回避策 に頼ることなく、問題を解決することができますか?正しい スプリングコントローラに転送する標準的な方法はありますか?

RequestMappingHandlerAdapterを実装することで組み込むことができます。

溶液#3:

ここでは、アスペクトのコードです:この回避策と

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 &amp;&amp; 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; 
    } 
} 

1つの制限は、それだけですべてのコントローラに単一バリデータを適用することができるということです。あなたもそれを避けることができます。

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

一緒にバリの側面とメタバリデータを使用して、春のXML設定ファイル:

<!-- enable Spring AOP support --> 
    <aop:aspectj-autoproxy proxy-target-class="true"/> 

    <!-- declare the validator aspect and inject the validator into it --> 
    <bean id="validatorAspect" class="com.something.RequestBodyValidatorAspect"> 
    <property name="validator" ref="validator"/> 
    </bean> 

    <!-- inject the validator into the DataBinder framework --> 
    <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> 

    <!-- declare the meta-validator bean --> 
    <bean id="validator" class="com.something.TypeMatchingValidator"/> 

    <!-- declare all Validator beans, these will be discovered by TypeMatchingValidator --> 
    <bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/> 
    <bean class="com.something.PersonValidator"/> 
    <bean class="com.something.AccountValidator"/> 

資源参考文献:scottfrederick:pring-3-Validation-Aspect

ソリューション#4:

さらに別ソリューションaopを使用してフォーム検証を行うには、ブログを確認することができます:form-validation-using-aspect-oriented-programming-aop-in-spring-framework

+0

技術的には、ブラウザが2回目のリクエストを行うため、古いメソッドのパラメータはhttpservletrequestには存在しないため、この問題が修正されます。しかし、基本的には、Springのライフサイクルにどのように繋がり、Springのコントローラにリクエストを「適切に」転送するのかという疑問を避けることができます。 – KyleM

関連する問題