【问题标题】:Unable to read input stream无法读取输入流
【发布时间】:2022-01-11 20:06:27
【问题描述】:

我正在使用ActionFilterAttribute 在点击控制器之前获取请求,如下所示:

 public override void OnActionExecuting(HttpActionContext actionContext)
 {
     using (var stream = new MemoryStream())
     {
        HttpContextBase context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];
        context.Request.InputStream.Seek(0, SeekOrigin.Begin);
        context.Request.InputStream.CopyTo(stream);
        requestBody = Encoding.UTF8.GetString(stream.ToArray());
     }
 }

上述方法适用于小请求,但对于大 json,它给了我这个错误:

在 HttpRequest.GetBufferedInputStream 的调用者填充内部存储之前访问了 BinaryRead、Form、Files 或 InputStream。

而输入流给出了这个错误

context.Request.InputStream 抛出 System.InvalidOperationException System.IO.Stream {System.InvalidOperationException} 类型的异常

正如我在研究中发现的超时问题,但我无法更改代码中的超时。我尝试更改 web.config 文件 maxRequestLength="102400000"maxAllowedContentLength="209715100" 中的值,但我仍然面临同样的错误。
如果我读了GetBufferedInputStream 但仍然是同样的问题,它只是读取缓冲区的一部分,而不是整个流。

我也尝试了以下方法:

 Stream InStream;
 int Len;
 InStream = HttpContext.Current.Request.InputStream;
 Len = System.Convert.ToInt32(InStream.Length);
 byte[] ByteArray = new byte[Len + 1];
 InStream.Seek(0, SeekOrigin.Begin);
 InStream.Read(ByteArray, 0, Len);
 var jsonParam = System.Text.Encoding.UTF8.GetString(ByteArray); 

请注意,如果我将内容类型设置为 application/xmlapplication/x-www-form-urlencoded 它可以工作,但如果我将其设置为 application/json 它会给我这个错误!!

请指教!

【问题讨论】:

  • 您是否尝试使用DelegatingHandler 来获取请求正文?大json的大小是多少?我用 195077 个字符的 json 尝试了你的第一个变体,它工作正常
  • 嗯,只有一个输入流,而您正在读取它,因此 ASP.NET 无法完成它的工作。该流通常是转发/只读的,您不能多次查找和读取它。为什么要阅读该流? ASP.NET 应该读取它,然后您使用 ASP.NET 功能来做您必须做的事情
  • @SimonMourier 因为请求是加密的,所以我需要对其进行解密,然后将其作为 json 传递给控制器​​
  • @Albert 您发送的 json 内容类型为 application/json 吗?因为如果我发送它application/x-www-form-urlencoded 它可以工作
  • 你有为动作定义的模型吗?

标签: c# asp.net-web-api2 inputstream memorystream


【解决方案1】:

有几点:

首先,如果您尝试从流中读取 0 个字节,那么它将引发 System.InvalidOperationException 异常。因此,我将更改您的代码,如下所示,并添加对 ContentLength > 0 的检查。

using (var stream = new MemoryStream())
     {
        HttpContextBase context = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];
        if(context.Request.Contentlength > 0)
        {
            context.Request.InputStream.Seek(0, SeekOrigin.Begin);
            context.Request.InputStream.CopyTo(stream);
            requestBody = Encoding.UTF8.GetString(stream.ToArray());
        }
     }

另外,我曾经遇到过同样的问题,在 web.config 中增加 maxRequestLength 似乎已经解决了这个问题。 此链接进一步提供更多信息here

【讨论】:

    【解决方案2】:

    这就是我在模型活页夹中执行此操作的方式,但我不确定它如何与您的操作过滤器一起使用。我在网上查了一下,有相互矛盾的信息;有人说您无法读取输入流,因为它不可搜索,并且 ASP.NET 需要读取它来绑定模型。有人说它确实是可搜索的,并使用您上面分享的方法。所以要想弄清楚什么是真正起作用的唯一方法就是测试。

    我希望我的代码示例能帮助你弄清楚。

    object request = null;
    if (actionContext.Request.Method == HttpMethod.Post && "application/json".Equals(actionContext.Request.Content.Headers.ContentType.MediaType))
    {
        var jsonContentTask = actionContext.Request.Content.ReadAsStringAsync();
        Task.WaitAll(jsonContentTask);
        string jsonContent = jsonContentTask.Result;
        //... other stuff
    }
    

    【讨论】:

    • 我试过了,如果我在正文中发送一个大字符串它可以工作,但如果我发送一个大 json 它不起作用,我得到jsonContent 作为空字符串
    【解决方案3】:

    我可能错了,但这是我发现的。动作过滤器在模型绑定之后执行,这意味着请求流已经被读取。在你的情况下,我不确定这意味着什么。 https://exceptionnotfound.net/the-asp-net-web-api-2-http-message-lifecycle-in-43-easy-steps-2/ 详细解释了生命周期。更改内容类型不会更改生命周期事件,而是会更改请求内容,这反过来可能会影响模型绑定。 如果您为该操作设置了模型,那么How to get current model in action filter 应该会有所帮助。所以解决方案是从actionContext 获取模型对象,然后进行相应的修改。

    【讨论】:

    • 我认为这是诚实地总结了这个问题
    猜你喜欢
    • 2015-03-22
    • 1970-01-01
    • 2023-04-04
    • 2015-12-23
    • 2014-12-13
    • 1970-01-01
    • 2012-03-12
    • 1970-01-01
    相关资源
    最近更新 更多