2017-05-09 2 views
6

मेरे जावा वसंत REST API नियंत्रक इस तरह दिखता है:@RequestBody का उपयोग करते हुए और एक अन्य समाप्ति बिंदु को अग्रेषण फेंकता अपवाद स्ट्रीम बंद कर दिया

Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Stream closed; nested exception is java.io.IOException: Stream closed 

यह इसलिए होता है क्योंकि मैं चाहता हूँ:

public void signup(@RequestBody RequestBody requestBody) throws IOException, ServletException { 

मैं इस अपवाद अनुरोध निकाय को अनुरोध बॉडी क्लास (जो अनुरोध इनपुट स्ट्रीम खोलता है और इसे खत्म करता है) को प्रस्तुत करता है, और आगे/इसे दूसरे एंडपॉइंट पर रीडायरेक्ट भी करता है।

वास्तविक नियंत्रक है:

@RequestMapping(value = "/signup", method = RequestMethod.POST) 
    public void signup(@RequestBody CustomUserDetails user, HttpServletRequest request, HttpServletResponse response) { 

     String userName = user.getUsername(); 
     logger.debug("User signup attempt with username: " + userName); 

     try{ 
      if(customUserDetailsService.exists(userName)) 
      { 
       logger.debug("Duplicate username " + userName); 
userName + " already exists"); 
       String newUrl = "login"; 
       RequestDispatcher view = request.getRequestDispatcher(newUrl); 
       view.forward(request, response); 
      } else { 
       customUserDetailsService.save(user); 
       authenticateUserAndSetSession(user, response); 
      } 
     } catch(Exception ex) { 

     } 
    } 

मैं इस संभाल चाहिए?

+1

आप धारावाहिक वस्तु के स्थान पर अनुरोध क्यों चाहिए? आप 'HttpServletRequest' को नहीं डाल सकते हैं क्योंकि यह स्पष्ट रूप से' अनुरोधबॉडी 'उदाहरण नहीं है। आपको अनुरोध को पार्स करने की आवश्यकता होगी (यानी 'इनपुटस्ट्रीम' पढ़ें और उसे अपनी ऑब्जेक्ट में कनवर्ट करें)। लेकिन उन्हें जोड़ना केवल काम करना चाहिए (यदि आपको ऐसा कुछ नहीं करना चाहिए जो आपको पहले स्थान पर नहीं करना चाहिए)। –

+0

नहीं, यह काम नहीं करता है। मैंने विधि हस्ताक्षर के लिए @RequestBody, HttpServletReques, HttpServletResponse जोड़ने की कोशिश की और बाद में यह इनपुट स्ट्रीम पढ़ने में विफल रहा, जब मैंने पहले तर्क (अनुरोधबॉडी) को हटा दिया। मैं नियंत्रक में जाली अनुरोधबॉडी ऑब्जेक्ट को पढ़ना चाहता हूं और इसे किसी अन्य नियंत्रक को अग्रेषित करना चाहता हूं। –

+0

आप इनपुट स्ट्रीम (दोबारा) नहीं पढ़ सकते हैं क्योंकि इसे केवल एक बार उपभोग किया जा सकता है। आप फिर से इनपुट स्ट्रीम क्यों पढ़ रहे हैं। (और मुझ पर भरोसा करें कि यह कई परियोजनाओं पर इसका उपयोग कर रहा है)। –

उत्तर

3

अनुरोध शरीर वस्तु एक धारा जो केवल एक बार पढ़ा जा सकता है है। तो इसे अग्रेषित करना बहुत तुच्छ नहीं है। इसके आस-पास एक तरीका एक फ़िल्टर बनाना है जो इनपुट भाप को पढ़ता है और इनपुट स्ट्रीम को किसी चीज़ पर प्रतिस्थापित करता है जिसे कई बार पढ़ा जा सकता है।उदाहरण एक और जवाब में पाया जा सकता:

How can I read request body multiple times in Spring 'HandlerMethodArgumentResolver'?

अपने विधि का सवाल है, वहाँ भी एक और समस्या है:

public void signup(@RequestBody RequestBody requestBody) 

जहाँ तक मुझे पता है, RequestBody एक एनोटेशन है और आप नहीं कर सकते इसे इस तरह नक्शा करें। लेकिन कच्चे डेटा प्राप्त करने के लिए, आप इसे स्ट्रिंग के रूप में मैप कर सकते हैं।

public void signup(@RequestBody String requestBody) 

और फिर आप बस मैन्युअल एपीआई आप स्ट्रिंग अनुरोध शरीर का उपयोग कर इसे आगे करना चाहते हैं के लिए एक REST कॉल कर सकते हैं। बस सुनिश्चित करें कि आप सामग्री-प्रकार को मूल के रूप में सेट करते हैं, जो मुझे लगता है कि इस मामले में JSON होगा।

1

अनुरोध मानचित्रण में डालने की कोशिश करो खपत = { "application/json"}, = पैदा करता है { "application/json"}

+0

कृपया अपना उत्तर पूरा करें और विवरण जोड़ें । मुझे समझ में नहीं आता –

+0

@RequestMapping (value = "/ signup", method = RequestMethod.POST, = {"application/json"}, उत्पादन = {"application/json"}) –

+0

यदि आप xml में डेटा भेज रहे हैं तो आप json को xml में बदल सकते हैं प्रारूप .... –

1
  1. RequestBody एक एनोटेशन उम्मीद वर्ग अक्रमांकन आपके अनुरोध वस्तु पर कार्रवाई करने के लिए है। वे संदेश कनवर्जन के तर्क को निकालने और इसे एक पहलू बनाकर बॉयलरप्लेट कोड से बचने में आपकी सहायता करते हैं।
  2. आप किसी भी नियंत्रक में किसी ऑब्जेक्ट के रूप में RequestBodyसीधे नहीं प्राप्त कर सकते हैं। इसे इस तरह इस्तेमाल करने के लिए डिज़ाइन नहीं किया गया था।
  3. हालांकि में RequestBody प्राप्त करना संभव नहीं है, इसलिए आप यह तय नहीं कर सकते कि यह अच्छा या बुरा अभ्यास है या नहीं।
  4. यदि आपको प्रक्रिया के लिए नए नियंत्रक/एंडपॉइंट का उपयोग करना है। तो मुझे लगता है कि बेहतर तरीका है CustomUserDetails@RequestBody नियंत्रक में इसे संसाधित करने के लिए। फिर किसी अन्य नियंत्रक को अग्रेषित करने की बजाय आगे की प्रक्रिया के लिए नेस्टेड विधि या सेवा को कॉल करें। फिर नियंत्रक से प्रतिक्रिया वापस करें। एक ही नियंत्रक में एक ExceptionHandler परिभाषित

    @RequestMapping(value = "/signup", method = RequestMethod.POST) 
    public void signup(@RequestBody CustomUserDetails user, HttpServletResponse response) { 
    
        String userName = user.getUsername(); 
        logger.debug("User signup attempt with username: " + userName); 
    
        //try{ 
        if (customUserDetailsService.exists(userName)) { 
         logger.debug("Duplicate username " + userName); 
         throw new SignupException(userName + " already exists"); 
        } else { 
         customUserDetailsService.save(user); 
         authenticateUserAndSetSession(user, response); 
        } 
        /*} catch(Exception ex) { 
    
        }*/ 
    } 
    

    :

5

आप एक ExceptionHandler में पेज लॉगइन करने के लिए आगे इस तरह कर सकते हैं

@ExceptionHandler(SignupException.class) 
public String duplicateName() { 
    return "login"; 
} 

और SignupException इस तरह हो सकता है :

public class SignupException extends RuntimeException { 
    public SignupException(String message) { 
     super(message); 
    } 

    public SignupException() { 
    } 
} 
2

मुझे लगता है कि आप RequestBody के साथ एक यूआरएल को अग्रेषित करने का प्रयास कर रहे हैं, कृपया उत्तर के लिए Spring 3.2 forward request with new object पर एक जांच करें।

वस्तु बना सकते हैं और पहले नियंत्रक में विशेषता के रूप में अनुरोध के लिए इसे जोड़ने,

request.setAttribute("user",user), 
return "forward:/login"; 
+0

मैंने इस दृष्टिकोण की कोशिश की, स्ट्रिंग को लौटने से केवल प्रतिक्रिया के शरीर में लौटा दिया जाता है। –

+0

इसे आज़माएं: नया मॉडल और दृश्य देखें ("आगे:/लॉगिन"); – Jane

+0

मैं ModelAndView का उपयोग नहीं कर रहा हूं। –

3

आपकी समस्या का रूट का उपयोग HttpServletRequest request के साथ कर रहा है।

उसी अनुरोध पर दो बार इनपुट स्ट्रीम खोलने की अनुमति नहीं है। आपके मामले में एक सिस्टम को अनुरोध निकाय निकालने के लिए एक इनपुट स्ट्रीम खोलनी चाहिए और फिर पुन: उपयोग करने के लिए आगे बढ़ना चाहिए।

इसे संभालने के लिए आपको एक ही अनुरोध स्ट्रीम के एकाधिक उपयोग से बचना चाहिए। संभव समाधान हैं:

  1. लपेटें अनुरोध
  2. कॉपी अनुरोध शरीर
  3. उपयोग वसंत देशी आगे
संबंधित मुद्दे