1. ホーム
  2. c#

[解決済み] HttpClientの単一インスタンスで異なる認証ヘッダを使用する場合

2023-04-05 12:34:56

質問

.net の HttpClient は再利用を念頭に置いて設計されており、次のようなことを意図しています。 長寿 メモリリークが報告されている が報告されています。複数のユーザーのためにエンドポイントを呼び出すときに、異なるベアラートークン (または任意の認証ヘッダー) を使用して特定のエンドポイントに restful 呼び出しを行いたい場合、どのようなガイド ラインがありますか?

private void CallEndpoint(string resourceId, string bearerToken) {
  httpClient.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("bearer", bearerToken);
  var response = await httpClient.GetAsync($"resource/{resourceid}");
}

上記のコードがウェブアプリケーションの任意の数のスレッドによって呼び出される可能性があることを考えると、最初の行で設定されたヘッダーがリソースを呼び出すときに使用されるものと同じでないことは容易にあり得ます。

ロックを使用して競合を引き起こすことなく、ステートレスなWebアプリケーションを維持するために、単一のエンドポイントのHttpClientsを作成および廃棄する推奨されるアプローチは何ですか (私の現在の実践は、エンドポイントごとに単一のクライアントを作成することです)。


ライフサイクル

HttpClient は IDisposable インターフェースを間接的に実装しています。 インターフェイスを実装していますが、推奨される HttpClient の使用法は、リクエストのたびにそれを破棄しないことです。 を廃棄しないことです。HttpClient オブジェクトは、アプリケーションが HTTP 要求を行う必要がある限り オブジェクトは、アプリケーションが HTTP リクエストを作成する必要がある限り、存続させることを目的としています。オブジェクトを複数のリクエストにまたがって存在させることで オブジェクトが複数のリクエストにまたがって存在することで また、CredentialCache や CookieContainer などの設定を変更する必要がなくなります。 のようにリクエストごとに CredentialCache や CookieContainer などの設定をし直す必要がなくなります。 HttpWebRequestで必要だったように、リクエストごとにCredentialCacheやCookieContainerなどを再定義する必要がなくなります。

どのように解決するのですか?

ヘッダが通常同じになるのであれば、ヘッダに DefaultRequestHeaders . しかし、ヘッダを指定するためにそのプロパティを使用する必要はありません。あなたが判断したように、同じクライアントを複数のスレッドで使用する場合、 それはうまくいかないでしょう。あるスレッドで行われたデフォルトのヘッダへの変更は、他のスレッドで送信されたリクエストに影響を与えるでしょう。

クライアントでデフォルトのヘッダを設定し、各リクエストにそれを適用することはできますが、ヘッダは実際にはリクエストのプロパティです。そのため、ヘッダがリクエストに固有のものである場合は、リクエストに追加するだけでよいでしょう。

request.Headers.Authorization = new AuthenticationHeaderValue("bearer", bearerToken);

つまり HttpRequest . を使う必要があります。

public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)

文書化 ここで .


ヘッダを更新するコードをメソッドの残りの部分から分離するために、拡張メソッドを使用することが有用であることを発見した人もいます。

リクエストヘッダを操作できる拡張メソッドを通して行われるGETとPOSTメソッドの例と、より多くの HttpRequestMessage を送信する前に操作することができます。

public static Task<HttpResponseMessage> GetAsync
    (this HttpClient httpClient, string uri, Action<HttpRequestMessage> preAction)
{
    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri);

    preAction(httpRequestMessage);

    return httpClient.SendAsync(httpRequestMessage);
}

public static Task<HttpResponseMessage> PostAsJsonAsync<T>
    (this HttpClient httpClient, string uri, T value, Action<HttpRequestMessage> preAction)
{
    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, uri)
    {
        Content = new ObjectContent<T>
            (value, new JsonMediaTypeFormatter(), (MediaTypeHeaderValue)null)
    };
    preAction(httpRequestMessage);

    return httpClient.SendAsync(httpRequestMessage);
}

これらは、次のように使うことができる。

var response = await httpClient.GetAsync("token",
    x => x.Headers.Authorization = new AuthenticationHeaderValue("basic", clientSecret));