13

मैं एएसपी.NET कोर एक्शन से Response.Body संपत्ति प्राप्त करने के लिए लड़ रहा हूं और एकमात्र समाधान जिसे मैं पहचानने में सक्षम हूं, वह हैकी लगता है। एक स्ट्रिंग में स्ट्रीम पढ़ने के दौरान समाधान को Response.BodyMemoryStream के साथ स्वैप करने की आवश्यकता होती है, फिर क्लाइंट को भेजने से पहले इसे वापस स्वैप करना आवश्यक होता है। नीचे दिए गए उदाहरणों में, मैं कस्टम मिडलवेयर क्लास में Response.Body मान प्राप्त करने का प्रयास कर रहा हूं। Response.Body सेट किसी कारण से केवल एएसपी.NET कोर में संपत्ति है? क्या मैं यहाँ कुछ याद कर रहा हूं, या यह एक निरीक्षण/बग/डिजाइन मुद्दा है? Response.Body पढ़ने का कोई बेहतर तरीका है?एएसपी.नेट कोर प्रतिक्रिया कैसे पढ़ें। बॉडी?

Hacky समाधान:()

public class MyMiddleWare 
{ 
    private readonly RequestDelegate _next; 

    public MyMiddleWare(RequestDelegate next) 
    { 
     _next = next; 
    } 

    public async Task Invoke(HttpContext context) 
    { 
     using (var swapStream = new MemoryStream()) 
     { 
      var originalResponseBody = context.Response.Body; 

      context.Response.Body = swapStream; 

      await _next(context); 

      swapStream.Seek(0, SeekOrigin.Begin); 
      string responseBody = new StreamReader(swapStream).ReadToEnd(); 
      swapStream.Seek(0, SeekOrigin.Begin); 

      await swapStream .CopyToAsync(originalResponseBody); 
      context.Response.Body = originalResponseBody; 
     } 
    } 
} 

का प्रयास किया गया समाधान EnableRewind का उपयोग कर: केवल Request.Body, नहीं Response.Body के लिए काम करता है। इसके परिणामस्वरूप वास्तविक प्रतिक्रिया शरीर सामग्री (जिसे मैंने पोस्टमैन का उपयोग करके सत्यापित किया है) की बजाय Response.Body से खाली स्ट्रिंग पढ़ने में परिणाम मिलता है।

Startup.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifeTime) 
{ 
    loggerFactory.AddConsole(Configuration.GetSection("Logging")); 
    loggerFactory.AddDebug(); 

    app.Use(async (context, next) => { 
     context.Request.EnableRewind(); 
     await next(); 
    }); 

    app.UseMyMiddleWare(); 

    app.UseMvc(); 

    // Dispose of Autofac container on application stop 
    appLifeTime.ApplicationStopped.Register(() => this.ApplicationContainer.Dispose()); 
} 

MyMiddleWare.cs

public class MyMiddleWare 
{ 
    private readonly RequestDelegate _next; 

    public MyMiddleWare(RequestDelegate next) 
    { 
     _next = next; 
    } 

    public async Task Invoke(HttpContext context) 
    { 
     await _next(context); 
     string responseBody = new StreamReader(context.Request.Body).ReadToEnd(); //responseBody is "" 
     context.Request.Body.Position = 0; 
    } 
} 
+0

कि डिजाइन के द्वारा होता है का उदाहरण। – Nkosi

उत्तर

17

अपने मूल जवाब में मैं पूरी तरह प्रश्न पढ़ने में भूलना और सोचा था कि पोस्टर कैसे पढ़ने के लिए पूछ रहा था था Request.Body लेकिन उन्होंने पूछा था कि Response.Body कैसे पढ़ा जाए। मैं इतिहास को संरक्षित करने के लिए अपना मूल उत्तर छोड़ रहा हूं लेकिन यह दिखाने के लिए इसे अपडेट कर रहा हूं कि यह सही तरीके से पढ़ने के बाद मैं प्रश्न का उत्तर कैसे दूंगा।

मूल उत्तर

आप एक बफ़र धारा कई बार आप स्थापित करने की आवश्यकता को पढ़ने का समर्थन करता है कि चाहते हैं

context.Request.EnableRewind() 

आदर्श रूप में पहले कुछ भी शरीर को पढ़ने के लिए की जरूरत है मिडलवेयर में इस प्रारंभिक है।

इसलिए उदाहरण के लिए आप Startup.cs फ़ाइल के Configure विधि की शुरुआत में निम्न कोड बन सकता था:

 app.Use(async (context, next) => { 
      context.Request.EnableRewind(); 
      await next(); 
     }); 

पहले Request.Body के साथ जुड़े धारा रिवाइंड को सक्षम करने के लिए एक आगे ही नहीं स्ट्रीम है दूसरी बार स्ट्रीम की तलाश या पढ़ने का समर्थन नहीं करता है। अनुरोध के रूप में हल्के वजन और प्रदर्शन के रूप में अनुरोध हैंडलिंग की डिफ़ॉल्ट कॉन्फ़िगरेशन करने के लिए यह किया गया था। लेकिन एक बार जब आप रिवाइंड को सक्षम करते हैं तो स्ट्रीम एक स्ट्रीम में अपग्रेड हो जाती है जो कई बार खोजने और पढ़ने का समर्थन करती है। आप EnableRewind पर कॉल के ठीक पहले और बस ब्रेकपॉइंट सेट करके और "गुणों को देखकर "अपग्रेड" देख सकते हैं। तो उदाहरण के लिए Request.Body.CanSeekfalse से true में बदल जाएगा।

तब शरीर धारा आप उदाहरण के लिए ऐसा कर सकता है पढ़ने के लिए:

string bodyContent = new StreamReader(Request.Body).ReadToEnd(); 

एक बयान का उपयोग करने में StreamReader निर्माण लपेट नहीं है हालांकि या इसे प्रयोग के समापन पर अंतर्निहित शरीर धारा बंद हो जाएगा बाद में अनुरोध जीवनशैली में ब्लॉक और कोड शरीर को पढ़ने में सक्षम नहीं होंगे।

इसके अलावा सुरक्षा के लिए, यह एक अच्छा विचार हो सकता है कि शरीर की धारा स्थिति 0.

request.Body.Position = 0; 

वापस करने के लिए पुनर्स्थापित करने के लिए कोड की इस पंक्ति के साथ शरीर सामग्री को पढ़कर कोड के ऊपर लाइन का पालन करने के इस तरह अनुरोध कोड लाइफसाइक्ल में बाद में कोई भी कोड अनुरोध प्राप्त करेगा। राज्य में कोई भी जैसा कि अभी तक इसे पढ़ा नहीं गया है।

अपडेट किया गया उत्तर

खेद है कि मैं मूल रूप से अपने प्रश्न पढ़ने में भूलना। एक भरे भाप होने के लिए जुड़े भाप को अपग्रेड करने की अवधारणा अभी भी लागू होती है। हालांकि आपको इसे मैन्युअल रूप से करना है, मुझे किसी भी अंतर्निहित .NET कोर कार्यक्षमता से अनजान है जो आपको EnableRewind() के तरीके से लिखे गए प्रतिक्रिया स्ट्रीम को पढ़ने देता है, जिसे डेवलपर पढ़ने के बाद अनुरोध स्ट्रीम को दोबारा पढ़ने देता है।

आपका "हैकी" दृष्टिकोण पूरी तरह से उपयुक्त है। आप मूल रूप से एक धारा को परिवर्तित कर रहे हैं जो कि किसी को भी नहीं ढूंढ सकता है। दिन के अंत में Response.Body स्ट्रीम को उस स्ट्रीम के साथ बदलना होगा जो बफर किया गया है और मांग का समर्थन करता है। ऐसा करने के लिए मिडलवेयर पर एक और लेना है, लेकिन आप देखेंगे कि यह आपके दृष्टिकोण के समान है। हालांकि मैंने मूल स्ट्रीम को Response.Body पर वापस लाने के लिए अतिरिक्त सुरक्षा के रूप में अंततः ब्लॉक का उपयोग करना चुना और मैंने PositionSeek विधि की बजाय स्ट्रीम की संपत्ति का उपयोग किया क्योंकि वाक्यविन्यास थोड़ा आसान है लेकिन प्रभाव आपके से अलग नहीं है दृष्टिकोण।

public class ResponseRewindMiddleware { 
     private readonly RequestDelegate next; 

     public ResponseRewindMiddleware(RequestDelegate next) { 
      this.next = next; 
     } 

     public async Task Invoke(HttpContext context) { 

      Stream originalBody = context.Response.Body; 

      try { 
       using (var memStream = new MemoryStream()) { 
        context.Response.Body = memStream; 

        await next(context); 

        memStream.Position = 0; 
        string responseBody = new StreamReader(memStream).ReadToEnd(); 

        memStream.Position = 0; 
        await memStream.CopyToAsync(originalBody); 
       } 

      } finally { 
       context.Response.Body = originalBody; 
      } 

     } 
+0

इसलिए मुझे यह दस्तावेज EnableRewind(): [MS डॉक्स] (https://docs.microsoft.com/en-us/aspnet/core/api/microsoft.aspnetcore.http.internal.bufferinghelper) पर मिला है, लेकिन यह नहीं करता है वास्तव में यह कैसे काम करता है इसके बारे में बहुत अधिक संकेत नहीं देता ... क्या आप विस्तारित कर सकते हैं कि मैं सक्षम रिवाइंड का उपयोग करने के लिए अपना कोड कैसे बदल सकता हूं? –

+0

निश्चित रूप से, मैं अपने उत्तर में और जोड़ दूंगा। मैं इसका उपयोग कर रहा हूं और यह बहुत अच्छा काम करता है। –

+1

अतिरिक्त जानकारी @Ron C के लिए धन्यवाद, मैंने आपके द्वारा सुझाए गए समाधान की कोशिश की, और जब मैं देख सकता हूं कि CanRead और CanSeek गुणों को सत्य में अपडेट किया गया है, तो पाठक सरल शरीर पर एक खाली स्ट्रिंग को वापस स्पर्श करता है सामग्री घटक। मैं PostMan में देख सकता हूं कि एक वास्तविक पूर्ण प्रतिक्रिया निकाय ग्राहक को वापस कर दिया जाता है। मैं EnableRewind() का उपयोग करके अपने दृष्टिकोण को प्रतिबिंबित करने के लिए अपना प्रश्न अपडेट करूंगा। –

4

क्या आप एक हैक वास्तव में कैसे कस्टम मिडलवेयर में प्रतिक्रिया धाराओं का प्रबंधन करने की सुझाव दिया दृष्टिकोण है के रूप में वर्णन करते हैं।

मध्यम वेयर डिज़ाइन की पाइपलाइन प्रकृति के कारण जहां प्रत्येक मध्य वेयर पाइपलाइन में पिछले या अगले हैंडलर से अनजान है। इस बात की कोई गारंटी नहीं है कि मौजूदा मध्यम बर्तन प्रतिक्रिया लिखने वाला होगा जब तक कि यह धारा धारा को पार करने से पहले दिए गए प्रतिक्रिया प्रवाह पर निर्भर करता है (वर्तमान मध्य बर्तन) नियंत्रण। यह डिजाइन ओविन में देखा गया था और अंत में एएसपीनेट-कोर में बेक किया गया था।

एक बार जब आप प्रतिक्रिया धारा में लिखना शुरू कर देते हैं तो यह ग्राहक को शरीर और शीर्षलेख (प्रतिक्रिया) भेजता है। यदि पाइपलाइन के नीचे एक और हैंडलर ऐसा करता है कि वर्तमान हैंडलर के पास एक मौका था तो यह प्रतिक्रिया देने के बाद कुछ भी जोड़ने में सक्षम नहीं होगा।

यदि वास्तविक पाइपलाइन में पिछले मिडलवेयर लाइन के नीचे एक और धारा पारित करने की एक ही रणनीति का पालन करता है तो वास्तविक प्रतिक्रिया स्ट्रीम होने की गारंटी नहीं दी जाती है।

संदर्भित ASP.NET Core Middleware Fundamentals

चेतावनी

next लागू करने के बाद HttpResponse संशोधित सावधान रहें, क्योंकि प्रतिक्रिया पहले से ही ग्राहक को भेजा गया हो सकता है। हेडर भेजे जाने के लिए आप HttpResponse.HasStarted का उपयोग कर सकते हैं।

चेतावनी

एक write विधि बुला के बाद next.Invoke फोन न करें। एक मिडलवेयर घटक या तो प्रतिक्रिया उत्पन्न करता है या next.Invoke पर कॉल करता है, लेकिन दोनों नहीं।

aspnet/BasicMiddleware से बुनियादी middlewares में बनाया Github रेपो

ResponseCompressionMiddleware.cs

/// <summary> 
/// Invoke the middleware. 
/// </summary> 
/// <param name="context"></param> 
/// <returns></returns> 
public async Task Invoke(HttpContext context) 
{ 
    if (!_provider.CheckRequestAcceptsCompression(context)) 
    { 
     await _next(context); 
     return; 
    } 

    var bodyStream = context.Response.Body; 
    var originalBufferFeature = context.Features.Get<IHttpBufferingFeature>(); 
    var originalSendFileFeature = context.Features.Get<IHttpSendFileFeature>(); 

    var bodyWrapperStream = new BodyWrapperStream(context, bodyStream, _provider, 
     originalBufferFeature, originalSendFileFeature); 
    context.Response.Body = bodyWrapperStream; 
    context.Features.Set<IHttpBufferingFeature>(bodyWrapperStream); 
    if (originalSendFileFeature != null) 
    { 
     context.Features.Set<IHttpSendFileFeature>(bodyWrapperStream); 
    } 

    try 
    { 
     await _next(context); 
     // This is not disposed via a using statement because we don't want to flush the compression buffer for unhandled exceptions, 
     // that may cause secondary exceptions. 
     bodyWrapperStream.Dispose(); 
    } 
    finally 
    { 
     context.Response.Body = bodyStream; 
     context.Features.Set(originalBufferFeature); 
     if (originalSendFileFeature != null) 
     { 
      context.Features.Set(originalSendFileFeature); 
     } 
    } 
} 
संबंधित मुद्दे