C# System.ObjectDisposedException:无法访问.NET Core 3.1中HTTP POST/PUT上的封闭流

C# System.ObjectDisposedException:无法访问.NET Core 3.1中HTTP POST/PUT上的封闭流,c#,http,middleware,asp.net-core-3.1,objectdisposedexception,C#,Http,Middleware,Asp.net Core 3.1,Objectdisposedexception,将我的应用程序从.NET Core 2.2升级到.NET Core 3.1。在通过HTTP POST或PUT创建/更新记录的一个api端点上,我遇到以下错误: System.ObjectDisposedException: Cannot access a closed Stream. at System.IO.MemoryStream.get_Position() at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream

将我的应用程序从.NET Core 2.2升级到.NET Core 3.1。在通过HTTP POST或PUT创建/更新记录的一个api端点上,我遇到以下错误:

System.ObjectDisposedException: Cannot access a closed Stream.
   at System.IO.MemoryStream.get_Position()
   at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.get_Position()
   at Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext bindingContext)
   at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value)
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
不确定这是否重要,但这里是我的api端点的开始片段,以防万一

[HttpPost("SomeApi/SomeRecords")]
[HttpPut("SomeApi/SomeRecords")]
[ServiceFilter(typeof(SomeApiResourceFilter))]
public SomeApiResponse PutSomeRecordBody([FromBody] SomeRecord indexRecord)
{
    SomeApiResponse response = new SomeApiResponse();

    SomeApiError sae;

    var userClaims = HttpContext.User.Claims;
    var clientSystem = userClaims.First(c => c.Type == ClaimTypes.Name).Value;
    ...

}
这是过滤代码

using System;
using Microsoft.AspNetCore.Mvc.Filters;
using NLog;
using System.Text;
using System.IO;
using System.Linq;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;

namespace SomeApi.Filters
{
    public class SomeApiResourceFilter : Attribute, IResourceFilter
    {
        private readonly ILogger _logger;

        public SomeApiResourceFilter()
        {
            _logger = LogManager.GetCurrentClassLogger();
        }

        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            _logger.Debug(context.ActionDescriptor.DisplayName + "- OnResourceExecuted");
        }

        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            _logger.Debug(context.ActionDescriptor.DisplayName + "- OnResourceExecuting");

            //var body = context.HttpContext.Request.Body;
            HttpRequestRewindExtensions.EnableBuffering(context.HttpContext.Request);

            var injectedRequestStream = new MemoryStream();

            try
            {

                string clientSystem = "Unknown";


                var userClaims = context.HttpContext.User.Claims;
                if (userClaims != null && userClaims.Count()>0)
                {
                    clientSystem = userClaims.First(c => c.Type == ClaimTypes.Name).Value;
                }
                else
                {
                    if (context.HttpContext.Connection.RemoteIpAddress != null &&
                        context.HttpContext.Connection.RemoteIpAddress.ToString().Length > 0)
                    {
                        clientSystem += " " + context.HttpContext.Connection.RemoteIpAddress.ToString();
                    }
                }

                var requestLog = clientSystem + " | " + context.HttpContext.Request.Method + " " 
                                + context.HttpContext.Request.Path + context.HttpContext.Request.QueryString.Value;

                using (var bodyReader = new StreamReader(context.HttpContext.Request.Body))
                {
                    var bodyAsText = bodyReader.ReadToEnd();
                    if (string.IsNullOrWhiteSpace(bodyAsText) == false)
                    {
                        requestLog += $" | {bodyAsText}";
                        
                    }

                    var bytesToWrite = Encoding.UTF8.GetBytes(bodyAsText);
                    injectedRequestStream.Write(bytesToWrite, 0, bytesToWrite.Length);
                    injectedRequestStream.Seek(0, SeekOrigin.Begin);
                    context.HttpContext.Request.Body = injectedRequestStream;
                }

                _logger.Info(requestLog);

            }
            catch(Exception ex)
            {
                _logger.Error(ex, "Unable to generate token");
            }
            finally
            {
                //injectedRequestStream.Dispose();
            }
        }
    }
}

因为你没有改变身体,你不需要像那样的第二流。只要阅读并倒带它,在创建
StreamReader
@sellotape时小心确保通过
leaveOpen:true
,添加
leaveOpen:true
就足以让它工作,谢谢。你能告诉我你所说的阅读和倒带是什么意思吗?从这个例子看不出来。你是说所有的
injectedRequestStream
行目前都没有任何用处吗?它们看起来很大程度上是多余的,是的。您可以将它们全部删除,然后在请求正文上执行
Seek(0)
(或
context.Request.Body.Position=0
,就像在链接中一样);这就是我所说的“倒带”。
using System;
using Microsoft.AspNetCore.Mvc.Filters;
using NLog;
using System.Text;
using System.IO;
using System.Linq;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;

namespace SomeApi.Filters
{
    public class SomeApiResourceFilter : Attribute, IResourceFilter
    {
        private readonly ILogger _logger;

        public SomeApiResourceFilter()
        {
            _logger = LogManager.GetCurrentClassLogger();
        }

        public void OnResourceExecuted(ResourceExecutedContext context)
        {
            _logger.Debug(context.ActionDescriptor.DisplayName + "- OnResourceExecuted");
        }

        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            _logger.Debug(context.ActionDescriptor.DisplayName + "- OnResourceExecuting");

            //var body = context.HttpContext.Request.Body;
            HttpRequestRewindExtensions.EnableBuffering(context.HttpContext.Request);

            var injectedRequestStream = new MemoryStream();

            try
            {

                string clientSystem = "Unknown";


                var userClaims = context.HttpContext.User.Claims;
                if (userClaims != null && userClaims.Count()>0)
                {
                    clientSystem = userClaims.First(c => c.Type == ClaimTypes.Name).Value;
                }
                else
                {
                    if (context.HttpContext.Connection.RemoteIpAddress != null &&
                        context.HttpContext.Connection.RemoteIpAddress.ToString().Length > 0)
                    {
                        clientSystem += " " + context.HttpContext.Connection.RemoteIpAddress.ToString();
                    }
                }

                var requestLog = clientSystem + " | " + context.HttpContext.Request.Method + " " 
                                + context.HttpContext.Request.Path + context.HttpContext.Request.QueryString.Value;

                using (var bodyReader = new StreamReader(context.HttpContext.Request.Body))
                {
                    var bodyAsText = bodyReader.ReadToEnd();
                    if (string.IsNullOrWhiteSpace(bodyAsText) == false)
                    {
                        requestLog += $" | {bodyAsText}";
                        
                    }

                    var bytesToWrite = Encoding.UTF8.GetBytes(bodyAsText);
                    injectedRequestStream.Write(bytesToWrite, 0, bytesToWrite.Length);
                    injectedRequestStream.Seek(0, SeekOrigin.Begin);
                    context.HttpContext.Request.Body = injectedRequestStream;
                }

                _logger.Info(requestLog);

            }
            catch(Exception ex)
            {
                _logger.Error(ex, "Unable to generate token");
            }
            finally
            {
                //injectedRequestStream.Dispose();
            }
        }
    }
}