web-dev-qa-db-ja.com

ASP.NET WebApiコントローラーでRequest.Contentを読み取れません

TransferMode.Streamed HttpSelfHostConfiguration exeでWebApiを使用してプロキシを記述しています。

ApiControllerに投稿するためにフィドラーを使用すると、何らかの理由でRequest.Contentを読み取ることができません-データをPOSTした場合でも ""を返します

public class ApiProxyController : ApiController
{

    public Task<HttpResponseMessage> Post(string path)
    {
        return Request.Content.ReadAsStringAsync().ContinueWith(s =>
        {
            var content = new StringContent(s.Result); //s.Result is ""
                CopyHeaders(Request.Content.Headers, content.Headers);
            return Proxy(path, content);
        }).Unwrap();
    }

    private Task<HttpResponseMessage> Proxy(string path, HttpContent content)
    {
        ...
    }
}

これが私のウェブリクエストです

POST http://localhost:3001/api/values HTTP/1.1
Host: localhost:3001
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Content-Type: application/json
Content-Length: 26

{ "text":"dfsadfsadfsadf"}

私が間違っているのは何ですか? s.Resultが生のjsonではなく空の文字列として返されるのはなぜですか?

23
mcintyre321

私はこれをApiControllerの代わりにベースインターフェースから継承することで最後に機能しました-ApiControllerは応答を食べていたモデルバインディングだったと思います

編集:プロキシを構築するための正しいことは、ApiControllerではなく、MessageHandlerです

4
mcintyre321

私もこれに苦労しました。 ReadAsStringAsyncおよびReadAsAsyncは、タスクオブジェクトを返します。 Resultプロパティを参照すると、コンテンツが返されます。 Resultプロパティを参照しているため、非同期読み取り要求がブロックされている可能性があります。

例:

string str = response.Content.ReadAsStringAsync().Result;
35
JeffR

私はこれが古いことを知っており、回答されていますが、それが価値があるのは、ReadAsStringAsync()を使用できない理由は、提案されているように「データを食べる」ためではなく、コンテンツのためですはストリームとして処理されており、データはメッセージフォーマッタによって消費されているため、ストリームの位置は既に終了しています。

ReadAsStringAsync()を使用するには、最初にコンテンツストリームの位置を先頭にリセットする必要があります。

response.RequestMessage.Content.ReadAsStreamAsync().Result.Seek( 0, System.IO.SeekOrigin.Begin )はHttpResponseMessageしか持っていないためですが、HttpRequestMessageに直接アクセスできる場合(コントローラー内で行うように)、機能的にはRequest.Content.ReadAsStreamAsync().Result.Seek( 0, System.IO.SeekOrigin.Begin )を使用できます。同等だと思います。

後期編集

上記のResultを使用して非同期ストリームを読み取ると、多くの状況でデッドロックとブロックされたスレッドが発生します。非同期ストリームから同期的に読み取る必要がある場合は、次の形式を使用することをお勧めします。

_ new TaskFactory( CancellationToken.None, 
                  TaskCreationOptions.None, 
                  TaskContinuationOptions.None, 
                  TaskScheduler.Default )
      .StartNew<Task<TResult>>( func )
      .Unwrap<TResult>()
      .GetAwaiter()
      .GetResult();
_

ここで、funcは実行する非同期アクションなので、この場合はasync () => { await Request.Content.ReadAsStreamAsync(); }のようなものになります。このようにすると、メソッドの非同期部分をStartNew部分を使用して、同期コードにマーシャリングするときに発生する例外を適切にアンラップします。

さらに良いことに、スタック全体を非同期にします。

17
Richard Hauer

投稿のこのシグネチャは、投稿データを食いつぶします:

public HttpResponseMessage Post([FromBody]string postdata)

次のように変更します。

public HttpResponseMessage Post()

この呼び出しは投稿データを取得するために正常に機能します:

string str = response.Content.ReadAsStringAsync().Result;

それを自分でテストしました。最初の署名を使用します。strは空です。2番目のstrには投稿データがあります!

17
JJ_Coder4Hire

Request.Contentを食べるApiControllerについては正しいと思います。 ApiControllerに表示される「Request」オブジェクトは、実際にはSystem.Net.Http.HttpRequestMessageタイプです。私はこの問題を回避できましたが、次のようなSystem.Web.HttpRequestオブジェクトにバックアップしました。

Dim content as string
If HttpContext.Current.Request.InputStream.CanSeek Then
    HttpContext.Current.Request.InputStream.Seek(0, IO.SeekOrigin.Begin)
End If
Using reader As New System.IO.StreamReader(HttpContext.Current.Request.InputStream)
    content = reader.ReadToEnd()
End Using

シークの巻き戻しが必要かどうかはわかりませんが、念のために巻き戻します。

5
EverPresent
  1. Request.Content.ReadAsStreamAsync()。Result.Seek(0、System.IO.SeekOrigin.Begin)
  2. new System.IO.StreamReader(Request.Content.ReadAsStreamAsync()。Result).ReadToEnd()
2
HGMamaci

引数には複合型を使用し、本文では次のようなjsonを使用する必要があります

{パス: "c:..."}

使用する

コンテンツタイプ:application/json; charset = UTF-8

jsonが本文に含まれていることをWeb APIが認識できるように、投稿リクエストのヘッダー

0
Gertjan

ReadAsStringAsync()ReadAsAsync<string>()に置き換えてみてください。

0
David Peden