24

私の目標は、AuthorizationFilter または DelegatingHandler を使用して Web API リクエストを認証することです。リクエスト本文を含め、いくつかの場所でクライアント ID と認証トークンを探したいと考えています。最初はこれが簡単に思えた、私はこのようなことをすることができました

var task = _message.Content.ReadAsAsync<Credentials>();

task.Wait();

if (task.Result != null)
{
    // check if credentials are valid
}

問題は、HttpContent を 1 回しか読み取れないことです。これを Handler または Filter で行うと、アクション メソッドでコンテンツを利用できなくなります。私はいくつかの回答をStackOverflowで見つけました. これは、フィルターまたはハンドラーでクールな Web API コンテンツ解析コードを使用することを妨げる、かなり厳しい制限のように思えます。

技術的な制限ですか?私が見ていない非常に悪いことをしないようにしようとしているのですか?

死後:

フィリップが提案したように、ソースを調べました。ReadAsStreamAsync は内部ストリームを返し、ストリームがサポートしている場合はSeek を呼び出すことを妨げるものは何もありません。私のテストでは、ReadAsAsync を呼び出した場合、次のようにしました。

message.Content.ReadAsStreamAsync().ContinueWith(t => t.Result.Seek(0, SeekOrigin.Begin)).Wait();

自動モデル バインディング プロセスは、アクション メソッドにヒットすると正常に動作します。私はこれを使用しませんでしたが、より直接的なものを選択しました:

var buffer = new MemoryStream(_message.Content.ReadAsByteArrayAsync().WaitFor());
var formatters = _message.GetConfiguration().Formatters;
var reader = formatters.FindReader(typeof(Credentials), _message.Content.Headers.ContentType);
var credentials = reader.ReadFromStreamAsync(typeof(Credentials), buffer, _message.Content, null).WaitFor() as Credentials;

拡張メソッドを使用 (私は await キーワードなしで .NET 4.0 を使用しています)

public static class TaskExtensions
{
    public static T WaitFor<T>(this Task<T> task)
    {
        task.Wait();
        if (task.IsCanceled) { throw new ApplicationException(); }
        if (task.IsFaulted) { throw task.Exception; }
        return task.Result;
    }
}

最後のキャッチとして、HttpContent にはハードコードされた最大バッファー サイズがあります。

internal const int DefaultMaxBufferSize = 65536;

したがって、コンテンツがそれよりも大きくなる場合は、ReadAsByteArrayAsync を呼び出す前に、より大きなサイズで LoadIntoBufferAsync を手動で呼び出す必要があります。

4

2 に答える 2

26

あなたが指摘した答えは完全に正確ではありません。

要求を内部的にバッファリングするため、いつでも文字列 ( ReadAsStringAsync) または byte[] ( ) として読み取ることができます。ReadAsByteArrayAsync

たとえば、以下のダミー ハンドラー:

public class MyHandler : DelegatingHandler
{
    protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        var body = await request.Content.ReadAsStringAsync();
        //deserialize from string i.e. using JSON.NET

        return base.SendAsync(request, cancellationToken);
    }
}

byte[] にも同じことが当てはまります。

public class MessageHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var requestMessage = await request.Content.ReadAsByteArrayAsync();
        //do something with requestMessage - but you will have to deserialize from byte[]

        return base.SendAsync(request, cancellationToken);
    }
}

いずれも、投稿されたコンテンツがコントローラーに到達したときに null になることはありません。

于 2012-10-23T00:15:43.423 に答える
2

clientId と認証キーは、コンテンツではなくヘッダーに配置します。

そうすれば、何度でも読むことができます!

于 2013-05-09T08:49:19.210 に答える