web-dev-qa-db-ja.com

入力ストリームを読み取れません

以下のように、コントローラーにアクセスする前に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/xmlまたはapplication/x-www-form-urlencodedに設定すると機能しますが、application/jsonに設定するとこのエラーが発生することに注意してください。

お知らせ下さい!

11
User7291

いくつかのポイントがあります:

まず、ストリームから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 を増やすことで問題が解決したようです。このリンクはさらに詳細を提供します ここ

3
Gauravsa

これは、モデルバインダー内で行う方法ですが、アクションフィルターでどのように機能するかわかりません。オンラインで確認しましたが、矛盾する情報があります。入力ストリームはシーク可能ではなく、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
}
2
Sal

私は間違っているかもしれませんが、ここに私が見つけたものがあります。アクションフィルターは、モデルバインディングの後に実行されます。つまり、要求ストリームは既に読み取られています。あなたの場合、それが何を意味するのかわかりません。 https://exceptionnotfound.net/the-asp-net-web-api-2-http-message-lifecycle-in-43-easy-steps-2/ ライフサイクルについて詳しく説明しています。コンテンツタイプを変更してもライフサイクルイベントは変更されませんが、リクエストコンテンツが変更され、モデルのバインドに影響を与える可能性があります。アクションにモデルを設定している場合は、 アクションフィルターで現在のモデルを取得する方法 が役立ちます。したがって、解決策は、actionContextからモデルオブジェクトを取得し、それに応じて変更することです。

1
Aamir Masood