2016-04-26 12 views
5

वसंत-servlet.xml का कारण बनता है मेरे इंटरसेप्टर को किसी भी वसंत से पहले बुलाया जाता है नियंत्रक विधि जो Validator एनोटेशन के साथ एनोटेटेड है। अगर अनुरोध विफल रहता है, तो अनुरोध को सत्यापित करना है, अनुरोध को एक अलग दृश्य के लिए अग्रेषित करें। यह आमतौर पर काम कर रहा है। अगर (!valid) में कोई त्रुटि है, तो RequestDispatcher.forward कहा जाता है। इससे एक और वसंत नियंत्रक विधि को बुलाया जाता है जो अंततः त्रुटि दृश्य दिखाता है। यह सामान्य रूप से काम करता है।स्प्रिंग AOP पुनरावर्ती कॉल

मुद्दा:

कुछ वसंत नियंत्रकों के लिए, मेरे RequestDispatcher के errorView अनुरोध वापस अनंत लूप के कारण ही विधि के लिए (invoke() से अधिक कहा जाता है और अधिक हो जाता है) को अग्रेषित किए जाने का कारण बनता है। मुझे लगता है कि इस वजह से स्प्रिंग नियंत्रक के अनुरोध मैपिंग (नीचे देखें) कैसे स्थापित किए गए हैं।

त्रुटि दृश्य: @RequestMapping(value = URL, params="error")

सामान्य दृश्य: @RequestMapping(value = URL, params="proceed")

तो जब पहली अनुरोध रूट किया जाता है यह अनुरोध पैरामीटर में 'आगे बढ़ना' मिला है। फिर जब क्वेरी स्ट्रिंग में 'त्रुटि' param के साथ देखने के लिए कोई त्रुटि और RequestDispatcher आगे की ओर है, तो उसे उपरोक्त "त्रुटि दृश्य" विधि को आगे बढ़ाना चाहिए, लेकिन ऐसा नहीं है। यह हमेशा 'आगे बढ़ने' विधि को आगे बढ़ाता है जिससे MethodInterceptor invoke() पर अनंत लूप होता है। ऐसा लगता है क्योंकि 'आगे बढ़ना' पैरामीटर अभी भी HttpServletRequest में है। हालांकि यह ठीक करना आसान नहीं है क्योंकि इंटरसेप्टर का पूरा बिंदु यह है कि इसे स्प्रिंग नियंत्रक के बारे में कोई जानकारी नहीं है - यह केवल यह जानता है कि कोई त्रुटि हुई है, और यदि कोई त्रुटि हुई तो उसे त्रुटि दृश्य को आगे बढ़ाना चाहिए।

वर्कअराउंड:

नीचे अनुरोध मैपिंग का उपयोग करना, यह समस्या ठीक होती है। ऐसा इसलिए है क्योंकि कुंजी = मान नोटेशन का उपयोग करते समय HttpServletRequest पैरामीटर ओवरराइट किया गया है।

त्रुटि दृश्य: @RequestMapping(value = URL, params="view=error")

सामान्य दृश्य: @RequestMapping(value = URL, params="view=proceed")

प्रश्न

मैं कैसे "ठीक से" मुद्दा वैकल्पिक हल ऊपर दिखाए का सहारा के बिना ठीक कर सकते हैं? सही वसंत नियंत्रक को आगे बढ़ाने के लिए एक और मानक तरीका है?

+0

आप MethodInterceptor आह्वान() ** अधिक जानकारी के GitHub या ** द्वारा सभी स्रोत कोड का हिस्सा सकता है? – CrawlingKid

उत्तर

1

समाधान # 1:

निम्नलिखित के रूप में विन्यस्त करने के बाद:

MethodInterceptor आह्वान():

if (!valid){ 

// RequestDispatcher rd = request.getRequestDispatcher(errorView); 
// rd.forward(request, response); 
    response.sendRedirect(errorView); 
} 

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

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

आप इस प्रकार रीडायरेक्ट के लिए कोशिश कर सकते

वापसी: ब्राउज़र इसलिए पुराना तरीका मापदंडों httpservletrequest में नहीं रह रहे हैं एक दूसरे अनुरोध करना होगा।

WorkArround: दोष बचने के लिए आप वसंत MVC फ़्लैश गुण इस्तेमाल कर सकते हैं। फ्लैश विशेषता कैसे काम करती है यह जानने के लिए आप इस ट्यूटोरियल का अनुसरण कर सकते हैं।

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

एक सीमा है कि यह केवल सभी नियंत्रकों के लिए एक एकल सत्यापनकर्ता आवेदन कर सकते हैं। आप इससे भी बच सकते हैं।

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"/> 

संसाधन Refs: scottfrederick:pring-3-Validation-Aspect

समाधान # 4:

फिर भी एक और समाधान एओपी का उपयोग करके फॉर्म सत्यापन के लिए, आप ब्लॉग देख सकते हैं: form-validation-using-aspect-oriented-programming-aop-in-spring-framework

+0

तकनीकी तौर पर इस समस्या को ठीक होगा क्योंकि ब्राउज़र एक दूसरे अनुरोध करना होता है, इसलिए पुराने विधि मानकों httpservletrequest में नहीं रह रहे हैं। हालांकि यह मूल रूप से उन प्रश्नों से परहेज करता है जो स्प्रिंग लाइफसाइक्ल में कैसे जुड़ें और वसंत नियंत्रक के अनुरोध को 'ठीक से' आगे बढ़ाएं। – KyleM

संबंधित मुद्दे