【问题标题】:How to read request body multiple times in asp net core 2.2 middleware?如何在 asp net core 2.2 中间件中多次读取请求正文?
【发布时间】:2019-06-23 20:23:48
【问题描述】:

我试过这个: Read request body twice 和这个: https://github.com/aspnet/Mvc/issues/4962 但没有用。 我这样阅读请求正文:

app.Use(async (context, next) =>
{
    var requestBody = await ReadStream(context.Request.Body);
    var requestPath = context.Request.Path.ToString();
    //Do some thing

    await next.Invoke();

    var responseStatusCode = context.Response.StatusCode;
    //Do some other thing
});

private async Task<string> ReadStream(Stream stream)
{
    using (var streamReader = new StreamReader(stream))
    {
        var result = await streamReader.ReadToEndAsync();

        return result;
    }
}

在控制器中,我得到“已处置的对象”或“空流”。

【问题讨论】:

    标签: c# middleware asp.net-core-2.2


    【解决方案1】:

    我为此苦苦挣扎,.net core 3.1 发生了重大变化,“context.Request.EnableRewind();” .net core 3.1 中没有更多可用的,更多有用的提示visit here

    asp.net mvc api .net core 3.1 (startup.cs) 中的工作代码

                app.Use(async (context, next) =>
            {
                context.Request.EnableBuffering();
                var stream = context.Request.Body;
    
                using (var reader = new StreamReader(stream))
                {
                    var requestBodyAsString = await reader.ReadToEndAsync();
    
                    if (stream.CanSeek)
                        stream.Seek(0, SeekOrigin.Begin);
                   
                    //some logging and other stuff goes here
    
                    await next.Invoke();
    
                }
            });
    

    【讨论】:

      【解决方案2】:

      .netcore 3.1 版本的@HoussamNasser 上面的答案。我创建了一个可重用的函数来读取请求正文。请注意更改:HttpRequestRewindExtensions.EnableBuffering(request)。 EnableBuffering 现在是HttpRequestRewindExtensions 类的一部分。

      public async Task<JObject> GetRequestBodyAsync(HttpRequest request)
          {
              JObject objRequestBody = new JObject();
      
              // IMPORTANT: Ensure the requestBody can be read multiple times.
              HttpRequestRewindExtensions.EnableBuffering(request);
      
              // IMPORTANT: Leave the body open so the next middleware can read it.
              using (StreamReader reader = new StreamReader(
                  request.Body,
                  Encoding.UTF8,
                  detectEncodingFromByteOrderMarks: false,
                  leaveOpen: true))
              {
                  string strRequestBody = await reader.ReadToEndAsync();
                  objRequestBody = SerializerExtensions.Deserialize<JObject>(strRequestBody);
      
                  // IMPORTANT: Reset the request body stream position so the next middleware can read it
                  request.Body.Position = 0;
              }
      
              return objRequestBody;
          }
      

      这个函数会返回一个JObject,可以用来读取Request Body对象的属性。 SerializerExtensions 是我用于序列化和反序列化的自定义扩展。

      在中间件中,可以在构造函数中注入IHttpContextAccessor httpContextAccessor。然后访问像HttpRequest request = _httpContextAccessor.HttpContext.Request; 这样的Request 对象。最后可以调用GetRequestBodyAsync(request)这样的可复用函数

      【讨论】:

        【解决方案3】:

        当第二次读取流时,流指针被设置到最后一个位置。您应该尝试将其移回零位置以从头开始重新读取。

        using System;
        using System.Collections.Generic;
        using System.IO;
        using System.Linq;
        using System.Text;
        using System.Threading.Tasks;
        using Microsoft.AspNetCore.Builder;
        using Microsoft.AspNetCore.Http;
        using Microsoft.AspNetCore.WebUtilities;
        using Microsoft.Extensions.Logging;
        using Newtonsoft.Json;
        using Newtonsoft.Json.Linq;
        using NLog;
        
        namespace Test_Middlewares.Middlewares
        {
            // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
            public class LoggingMiddleware
            {
                private readonly RequestDelegate _next;
                private readonly ILogger<LoggingMiddleware> _logger;
        
                public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
                {
                    _next = next;
                    _logger = logger;
                }
        
                public async Task InvokeAsync(HttpContext httpContext)
                {
                    var HttpContextBody = httpContext.Request.Body;
                    string requestBody = "";
        
                    httpContext.Request.EnableBuffering();
        
                    // Leave the body open so the next middleware can read it.
                    using (var reader = new StreamReader(
                        httpContext.Request.Body,
                        encoding: Encoding.UTF8,
                        detectEncodingFromByteOrderMarks: false,
                        bufferSize: -1,
                        leaveOpen: true))
                    {
                        var body = await reader.ReadToEndAsync();
                        // Do some processing with body…
        
                        // Reset the request body stream position so the next middleware can read it
                        httpContext.Request.Body.Position = 0;
                    }
        
                    _logger.LogDebug("Middleware 1 body =" + requestBody);
        
                    await _next.Invoke(httpContext);
                }
            }
        
            // Extension method used to add the middleware to the HTTP request pipeline.
            public static class LoggingMiddlewareExtensions
            {
                public static IApplicationBuilder UseLoggingMiddleware(this IApplicationBuilder builder)
                {
                    return builder.UseMiddleware<LoggingMiddleware>();
                }
            }
        }
        

        更多信息请参考以下链接:

        https://devblogs.microsoft.com/aspnet/re-reading-asp-net-core-request-bodies-with-enablebuffering/

        https://gunnarpeipman.com/aspnet-core-request-body/

        【讨论】:

        • 在 .netcore 3.1 中,EnableBuffering 是 HttpRequestRewindExtensions 类的一部分。也需要添加这个扩展类。
        【解决方案4】:

        经过一番挣扎和使用 “context.Request.EnableRewind()” 它终于像这样工作了:

        app.Use(async (context, next) =>
        {
            context.Request.EnableRewind();
            var stream = context.Request.Body;
        
            using (var reader = new StreamReader(stream))
            {
                var requestBodyAsString = await reader.ReadToEndAsync();
        
                if (stream.CanSeek)
                    stream.Seek(0, SeekOrigin.Begin);
        
                //Do some thing
        
                await next.Invoke();
        
                var responseStatusCode = context.Response.StatusCode;
                //Do some other thing
            }
        });
        

        【讨论】:

        • 你在 .NET Core 3 中试过了吗?我试过了,但它似乎不再工作了。
        • 是的,在dotnet core 3.0中不起作用还有另一种方法:使用Microsoft.AspNetCore.Http;然后调用'context.Request.EnableBuffering()'@JanPaoloGo
        • @AlirezaYavari 我在迁移到 .net core 3.0 时遇到了同样的问题。在 .net core 3.0 中,我想使用 BodyReader 管道。我为记录目的处理了请求正文,但收到验证错误“输入不包含任何 JSON 令牌。当 isFinalBlock 为真时,预期输入以有效的 JSON 令牌开头。路径:$ | LineNumber:0 | BytePositionInLine: 0。”你能帮帮我吗?
        • 在 .netcore 3.1 中,EnableBuffering 是 HttpRequestRewindExtensions 类的一部分。当您尝试从 Request 访问它时,这将不起作用。我花了几个小时搜索它,希望它对下一个读者有所帮助。 docs.microsoft.com/en-us/dotnet/api/…
        【解决方案5】:

        这可能是因为您的StreamReader 周围的using 语句。使用 dispose StreamReader 在底层流上调用 dispose。有关详细信息,请参阅答案here。您可以尝试保留引用并在 await next.Invoke(); 完成后处理 StreamReader

        【讨论】:

        • 好点,我在 next.Invoke() 之后处理了 streamReader 并且它起作用了。
        猜你喜欢
        • 2020-03-03
        • 2021-03-13
        • 2018-04-07
        • 2021-08-18
        • 2016-04-20
        • 2019-02-25
        • 2021-08-21
        • 2020-05-04
        • 2023-02-23
        相关资源
        最近更新 更多