2016-01-15 5 views
8

मैं अनुरोध शरीर से मूल्य निकालने और उन्हें मान्य करने और उन्हें कुछ एनोटेटेड पैरामीटर में इंजेक्ट करने के लिए RequestMapping विधियों के कुछ निश्चित मानकों को हल करने का प्रयास कर रहा हूं।मैं स्प्रिंग 'हैंडलर मोडर अर्ग्यूमेंट रिसोल्वर' में अनुरोध निकाय को कई बार कैसे पढ़ सकता हूं?

@Override 
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, 
           NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { 
    // 1, get corresponding input parameter from NativeWebRequest 
    // 2, validate 
    // 3, type convertion and assemble value to return 
    return null; 
} 

सबसे बड़ी समस्या यह है कि मैं यह पता लगाने कि HttpServletRequest (NativeWebRequest से मिलता है) इनपुट स्ट्रीम (कुछ मानकों अनुरोध शरीर में हैं) अधिक एक बार से नहीं पढ़ सकते है। तो मैं Inputstream/Reader या अनुरोध निकाय को एक से अधिक बार कैसे प्राप्त कर सकता हूं?

+0

एक समाधान थ्रेडलोकल का उपयोग अनुरोध से फ़िल्टर के अंदर पैरा स्टोर करने के लिए किया जा सकता है और फिर किसी भी समय अपने कोड में कहीं भी उनका उपयोग कर सकता है। –

+0

@ सैंडिपपूनिया इससे मदद मिल सकती है। लेकिन एक समस्या यह है कि, यदि मैं शरीर को थ्रेडलोकल में सहेजता हूं (HttpServletRequest.getReader/getInputStream को कॉल करके), इसे फिर कभी नहीं कहा जाएगा। नियंत्रक परत में घटना, मैं "@RequestBody स्ट्रिंग बॉडी" घोषित नहीं कर सकता (जो वसंत द्वारा अपवाद फेंक सकता है), क्योंकि वसंत अब इनपुट स्ट्रीम नहीं पढ़ सकता है। – Kim

उत्तर

16

आप एक फ़िल्टर जोड़ सकते हैं, वर्तमान HttpServletRequest को रोकें और इसे कस्टम HttpServletRequestWrapper पर लपेटें। आपके कस्टम HttpServletRequestWrapper में, आप अनुरोध निकाय को पढ़ते हैं और इसे कैश करते हैं और फिर कैश किए गए मान से पढ़ने के लिए getInputStream और getReader लागू करते हैं। अनुरोध लपेटकर के बाद, कैश की गई मूल्य हमेशा मौजूद रहता है के बाद से, आप अनुरोध शरीर को कई बार पढ़ सकते हैं:

@Component 
public class CachingRequestBodyFilter extends GenericFilterBean { 
    @Override 
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) 
      throws IOException, ServletException { 
     HttpServletRequest currentRequest = (HttpServletRequest) servletRequest; 
     MultipleReadHttpRequest wrappedRequest = new MultipleReadHttpRequest(currentRequest); 
     chain.doFilter(wrappedRequest, servletResponse); 
    } 
} 

इस फिल्टर के बाद, हर कोई wrappedRequest जो पढ़ा जा रहा से अधिक बार की क्षमता है देखेंगे:

public class MultipleReadHttpRequest extends HttpServletRequestWrapper { 
    private ByteArrayOutputStream cachedContent; 

    public MultipleReadHttpRequest(HttpServletRequest request) throws IOException { 
     // Read the request body and populate the cachedContent 
    } 

    @Override 
    public ServletInputStream getInputStream() throws IOException { 
     // Create input stream from cachedContent 
     // and return it 
    } 

    @Override 
    public BufferedReader getReader() throws IOException { 
     // Create a reader from cachedContent 
     // and return it 
    } 
} 

MultipleReadHttpRequest को लागू करने के लिए, आप वसंत ढांचे से ContentCachingRequestWrapper पर एक नज़र डाल सकते हैं जो मूल रूप से वही काम करता है।

इस दृष्टिकोण के अपने नुकसान हैं। सबसे पहले, यह कुछ हद तक अक्षम है, क्योंकि प्रत्येक अनुरोध के लिए, अनुरोध निकाय कम से कम दो बार पढ़ा जा रहा है। अन्य महत्वपूर्ण दोष यह है कि यदि आपके अनुरोध निकाय में 10 GB स्ट्रीम की धारा है, तो आप 10 GB डेटा पढ़ते हैं और इससे भी बदतर आगे की परीक्षा के लिए स्मृति में आते हैं।

+1

हां, अब तक यह सबसे अच्छा समाधान है। लेकिन दक्षता वास्तव में एक बड़ी समस्या है। मुझे लगता है कि अगर कोई भी सड़कों या किसी चीज़ में शामिल होता है तो मुझे इनपुट स्ट्रीम आकार को प्रतिबंधित करने का एक तरीका खोजना होगा। चूंकि अनुरोध निकाय कम से कम दो बार पढ़ता है, मुझे लगता है कि यह स्वीकार्य है क्योंकि मैं केवल अनुरोध निकाय से जेसन फॉर्मेटेड इनपुट स्ट्रिंग स्वीकार करता हूं जो आमतौर पर 1 किलो/पीआर से कम होता है। – Kim

+1

इस उदाहरण में पूरा उदाहरण उपलब्ध है: https://stackoverflow.com/a/17129256/364401 – Stim

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