如何在 asp net core 2.2 中间件中多次读取请求正文?

Posted

技术标签:

【中文标题】如何在 asp net core 2.2 中间件中多次读取请求正文?【英文标题】:How to read request body multiple times in asp net core 2.2 middleware? 【发布时间】: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;
    

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

【问题讨论】:

【参考方案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 并且它起作用了。

以上是关于如何在 asp net core 2.2 中间件中多次读取请求正文?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ASP.Net Core 中使用 条件中间件

如何在 ASP.NET Core 2.2 中实现标识

本地 Javascript Fetch Post 请求失败,调用 ASP.NET Core 2.2 Web API。 CORS 已启用

如何在启用 ASP.NET Core 2.2 EndpointRouting 的情况下使用 RouteDataRequestCultureProvider?

ASP.NET Core Web API - 如何在中间件管道中隐藏 DbContext 事务?

如何在 ASP.NET Core 中间件中直接将响应正文设置为文件流?