HttpClient クラスを使用して HTTP 要求を行う
この記事では、http 要求を行い、HttpClient
クラスで応答を処理する方法について説明します。
重要
この記事の HTTP 要求の例はすべて、次のいずれかの URL を対象とします。
- https://jsonplaceholder.typicode.com: テストとプロトタイプ作成のための無料の偽の API プラットフォームを提供するサイト。
- https://www.example.com: ドキュメント内の例示の例で使用できるドメイン。
通常、HTTP エンドポイントは、JavaScript Object Notation (JSON) データを返しますが、必ずしもそうであるとは限りません。 便宜上、省略可能な System.Net.Http.Json NuGet パッケージには、📦 System.Text.Json NuGet パッケージを使用して自動シリアル化と逆シリアル化を実行する HttpClient
オブジェクトと HttpContent
オブジェクト用のいくつかの拡張メソッドが用意されています。 この記事の例では、これらの拡張機能を使用できる場所に注意を向けます。
ヒント
この記事で参照されているすべてのソース コードは、GitHub: .NET Docs リポジトリで入手できます。
HttpClient オブジェクトを作成する
この記事の例のほとんどは、同じ HttpClient
インスタンスを再利用するため、インスタンスを 1 回構成し、残りの例で使用できます。 HttpClient
オブジェクトを作成するには、HttpClient
クラス コンストラクターを使用します。 詳細については、「HttpClient の使用に関するガイドライン」を参照してください。
// HttpClient lifecycle management best practices:
// https://learn.microsoft.com/dotnet/fundamentals/networking/http/httpclient-guidelines#recommended-use
private static HttpClient sharedClient = new()
{
BaseAddress = new Uri("https://jsonplaceholder.typicode.com"),
};
このコードでは、次のタスクが完了します。
- 新しい
HttpClient
インスタンスをstatic
変数としてインスタンス化します。 ガイドラインに従って、アプリケーションのライフサイクル中にHttpClient
インスタンスを再利用することをお勧めします。 - HttpClient.BaseAddress プロパティを
"https://jsonplaceholder.typicode.com"
に設定します。
この HttpClient
インスタンスは、ベース アドレスを使用して後続の要求を行います。 他の構成を適用するには、次の API を検討してください。
- HttpClient.DefaultRequestHeaders プロパティを設定します。
- 既定以外の HttpClient.Timeout プロパティを適用します。
- HttpClient.DefaultRequestVersion プロパティを指定します。
ヒント
または、ファクトリーパターンのアプローチを用いて、任意の数のクライアントを設定し、依存性注入サービスとして利用できるような HttpClient
インスタンスを作成することができます。 詳細については、「.NET を使用した HTTP クライアント ファクトリ」を参照してください。
HTTP 要求を行う
HTTP 要求を行うには、次のいずれかの API メソッドを呼び出します。
HTTP メソッド | API (アプリケーション・プログラミング・インターフェース) |
---|---|
GET |
HttpClient.GetAsync |
GET |
HttpClient.GetByteArrayAsync |
GET |
HttpClient.GetStreamAsync |
GET |
HttpClient.GetStringAsync |
POST |
HttpClient.PostAsync |
PUT |
HttpClient.PutAsync |
PATCH |
HttpClient.PatchAsync |
DELETE |
HttpClient.DeleteAsync |
†USER SPECIFIED |
HttpClient.SendAsync |
†
USER SPECIFIED
要求は、SendAsync
メソッドが有効な HttpMethod オブジェクトを受け入れることを示します。
警告
HTTP 要求を行うことは、ネットワーク I/O バウンドの作業と見なされます。 同期 HttpClient.Send メソッドが存在しますが、適切な理由がない限り、代わりに非同期 API を使用することをお勧めします。
Note
Android デバイス (.NET MAUI 開発など) をターゲットにしているときに、AndroidManifest.xml ファイルの <application></application>
セクションに android:usesCleartextTraffic="true"
定義を追加する必要があります。 この設定により、HTTP 要求などのクリア テキスト トラフィックが有効になります。このトラフィックは、Android セキュリティ ポリシーのために既定で無効になっています。 次の XML 設定例をご覧ください。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:usesCleartextTraffic="true"></application>
<!-- omitted for brevity -->
</manifest>
詳細については、「localhost ドメインのクリアテキスト ネットワーク トラフィックを有効にする」を参照してください。
HTTP コンテンツについて
HttpContent 型は、HTTP エンティティ本文および対応するコンテンツ ヘッダーを表すために使用されます。 本文 (POST
、PUT
、PATCH
) を必要とする HTTP メソッド (または要求メソッド) の場合は、HttpContent クラスを使用して要求の本文を指定します。 ほとんどの例は、JSON ペイロードを使用して StringContent サブクラスを準備する方法を示していますが、さまざまなコンテンツ (MIME) の種類用の他のサブクラスが存在します。
- ByteArrayContent: バイト配列に基づく HTTP コンテンツを提供します。
- FormUrlEncodedContent:
"application/x-www-form-urlencoded"
MIME タイプを使用してエンコードされた名前/値タプルの HTTP コンテンツを提供します。 - JsonContent: JSON に基づく HTTP コンテンツを提供します。
- MultipartContent:
"multipart/*"
MIME の種類の仕様を使用してシリアル化される HttpContent オブジェクトのコレクションを提供します。 - MultipartFormDataContent:
"multipart/form-data"
MIME の種類を使用してエンコードされたコンテンツのコンテナーを提供します。 - ReadOnlyMemoryContent: ReadOnlyMemory<T> 値に基づいて HTTP コンテンツを提供します。
- StreamContent: ストリームに基づく HTTP コンテンツを提供します。
- StringContent: 文字列に基づく HTTP コンテンツを提供します。
HttpContent
クラスは、HttpResponseMessage.Content プロパティでアクセスできる HttpResponseMessage クラスの応答本文を表すためにも使用されます。
HTTP GET 要求を使用する
GET
要求では本文を送信しないでください。 この要求は、(メソッド名が示すように) リソースからデータを取得 (または取得) するために使用されます。 HttpClient
インスタンスと Uri オブジェクトを指定して HTTP GET
要求を行うには、HttpClient.GetAsync メソッドを使用します。
static async Task GetAsync(HttpClient httpClient)
{
using HttpResponseMessage response = await httpClient.GetAsync("todos/3");
response.EnsureSuccessStatusCode()
.WriteRequestToConsole();
var jsonResponse = await response.Content.ReadAsStringAsync();
Console.WriteLine($"{jsonResponse}\n");
// Expected output:
// GET https://jsonplaceholder.typicode.com/todos/3 HTTP/1.1
// {
// "userId": 1,
// "id": 3,
// "title": "fugiat veniam minus",
// "completed": false
// }
}
このコードでは、次のタスクが完了します。
- エンドポイント
"https://jsonplaceholder.typicode.com/todos/3"
にGET
の要求を行います。 - 応答が成功したことを確認します。
- 要求の詳細をコンソールに書き込みます。
- 応答本文を文字列として読み取ります。
- JSON 応答本文をコンソールに書き込みます。
WriteRequestToConsole
メソッドは、フレームワークの一部ではないカスタム拡張機能です。 実装について興味がある場合は、次の C# コードを検討してください。
static class HttpResponseMessageExtensions
{
internal static void WriteRequestToConsole(this HttpResponseMessage response)
{
if (response is null)
{
return;
}
var request = response.RequestMessage;
Console.Write($"{request?.Method} ");
Console.Write($"{request?.RequestUri} ");
Console.WriteLine($"HTTP/{request?.Version}");
}
}
この機能は、要求の詳細を次の形式でコンソールに書き込むために使用されます。
<HTTP Request Method> <Request URI> <HTTP/Version>
たとえば、"https://jsonplaceholder.typicode.com/todos/3"
エンドポイントへの GET
要求では、次のメッセージが出力されます。
GET https://jsonplaceholder.typicode.com/todos/3 HTTP/1.1
JSON から HTTP GET 要求を作成する
https://jsonplaceholder.typicode.com/todos エンドポイントは、Todo
オブジェクトの JSON 配列を返します。 JSON 構造は次の形式のようになります。
[
{
"userId": 1,
"id": 1,
"title": "example title",
"completed": false
},
{
"userId": 1,
"id": 2,
"title": "another example title",
"completed": true
},
]
C# Todo
オブジェクトは、次のように定義されます。
public record class Todo(
int? UserId = null,
int? Id = null,
string? Title = null,
bool? Completed = null);
これは、省略可能な record class
、Id
、Title
、Completed
プロパティを持つ UserId
型です。 record
型の詳細については、「C# のレコード型の概要」を参照してください。 GET
要求を厳密に型指定された C# オブジェクトに自動的に逆シリアル化するには、📦 System.Net.Http.Json NuGet パッケージの一部である GetFromJsonAsync 拡張メソッドを使用します。
static async Task GetFromJsonAsync(HttpClient httpClient)
{
var todos = await httpClient.GetFromJsonAsync<List<Todo>>(
"todos?userId=1&completed=false");
Console.WriteLine("GET https://jsonplaceholder.typicode.com/todos?userId=1&completed=false HTTP/1.1");
todos?.ForEach(Console.WriteLine);
Console.WriteLine();
// Expected output:
// GET https://jsonplaceholder.typicode.com/todos?userId=1&completed=false HTTP/1.1
// Todo { UserId = 1, Id = 1, Title = delectus aut autem, Completed = False }
// Todo { UserId = 1, Id = 2, Title = quis ut nam facilis et officia qui, Completed = False }
// Todo { UserId = 1, Id = 3, Title = fugiat veniam minus, Completed = False }
// Todo { UserId = 1, Id = 5, Title = laboriosam mollitia et enim quasi adipisci quia provident illum, Completed = False }
// Todo { UserId = 1, Id = 6, Title = qui ullam ratione quibusdam voluptatem quia omnis, Completed = False }
// Todo { UserId = 1, Id = 7, Title = illo expedita consequatur quia in, Completed = False }
// Todo { UserId = 1, Id = 9, Title = molestiae perspiciatis ipsa, Completed = False }
// Todo { UserId = 1, Id = 13, Title = et doloremque nulla, Completed = False }
// Todo { UserId = 1, Id = 18, Title = dolorum est consequatur ea mollitia in culpa, Completed = False }
}
このコードでは、次のタスクが完了します。
"https://jsonplaceholder.typicode.com/todos?userId=1&completed=false"
に対してGET
要求を行います。クエリ文字列は、要求のフィルター条件を表します。 コマンドが成功すると、応答は自動的に
List<Todo>
オブジェクトに逆シリアル化されます。各
Todo
オブジェクトと共に、要求の詳細をコンソールに書き込みます。
HTTP POST 要求を使用する
POST
要求は、処理のためにデータをサーバーに送信します。 要求の Content-Type
ヘッダーは、本文で送信されている MIME の種類を示します。 HttpClient
インスタンスと Uri オブジェクトを指定して HTTP POST
要求を行うには、HttpClient.PostAsync メソッドを使用します。
static async Task PostAsync(HttpClient httpClient)
{
using StringContent jsonContent = new(
JsonSerializer.Serialize(new
{
userId = 77,
id = 1,
title = "write code sample",
completed = false
}),
Encoding.UTF8,
"application/json");
using HttpResponseMessage response = await httpClient.PostAsync(
"todos",
jsonContent);
response.EnsureSuccessStatusCode()
.WriteRequestToConsole();
var jsonResponse = await response.Content.ReadAsStringAsync();
Console.WriteLine($"{jsonResponse}\n");
// Expected output:
// POST https://jsonplaceholder.typicode.com/todos HTTP/1.1
// {
// "userId": 77,
// "id": 201,
// "title": "write code sample",
// "completed": false
// }
}
このコードでは、次のタスクが完了します。
- 要求の JSON 本文 (
"application/json"
の MIME の種類) を使用して、StringContent インスタンスを準備します。 POST
要求を"https://jsonplaceholder.typicode.com/todos"
エンドポイントに送信します。- 応答が成功したことを確認し、要求の詳細をコンソールに書き込みます。
- 応答本文を文字列としてコンソールに書き込みます。
HTTP POST 要求を JSON として作成する
POST
要求引数を自動的にシリアル化し、厳密に型指定された C# オブジェクトに応答を逆シリアル化するには、PostAsJsonAsync NuGet パッケージの一部である 拡張メソッドを使用します。
static async Task PostAsJsonAsync(HttpClient httpClient)
{
using HttpResponseMessage response = await httpClient.PostAsJsonAsync(
"todos",
new Todo(UserId: 9, Id: 99, Title: "Show extensions", Completed: false));
response.EnsureSuccessStatusCode()
.WriteRequestToConsole();
var todo = await response.Content.ReadFromJsonAsync<Todo>();
Console.WriteLine($"{todo}\n");
// Expected output:
// POST https://jsonplaceholder.typicode.com/todos HTTP/1.1
// Todo { UserId = 9, Id = 201, Title = Show extensions, Completed = False }
}
このコードでは、次のタスクが完了します。
Todo
インスタンスを JSON としてシリアル化し、"https://jsonplaceholder.typicode.com/todos"
エンドポイントにPOST
要求を行います。- 応答が成功したことを確認し、要求の詳細をコンソールに書き込みます。
- 応答本文を
Todo
インスタンスに逆シリアル化し、Todo
オブジェクトをコンソールに書き込みます。
HTTP PUT 要求を使用する
PUT
要求メソッドは、既存のリソースを置き換えるか、要求本文ペイロードを使用して新しいリソースを作成します。 HttpClient
インスタンスと Uri オブジェクトを指定して HTTP PUT
要求を行うには、HttpClient.PutAsync メソッドを使用します。
static async Task PutAsync(HttpClient httpClient)
{
using StringContent jsonContent = new(
JsonSerializer.Serialize(new
{
userId = 1,
id = 1,
title = "foo bar",
completed = false
}),
Encoding.UTF8,
"application/json");
using HttpResponseMessage response = await httpClient.PutAsync(
"todos/1",
jsonContent);
response.EnsureSuccessStatusCode()
.WriteRequestToConsole();
var jsonResponse = await response.Content.ReadAsStringAsync();
Console.WriteLine($"{jsonResponse}\n");
// Expected output:
// PUT https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
// {
// "userId": 1,
// "id": 1,
// "title": "foo bar",
// "completed": false
// }
}
このコードでは、次のタスクが完了します。
- 要求の JSON 本文 (
"application/json"
の MIME の種類) を使用して、StringContent インスタンスを準備します。 PUT
要求を"https://jsonplaceholder.typicode.com/todos/1"
エンドポイントに行います。- 応答が成功したことを確認し、JSON 応答本文を含む要求の詳細をコンソールに書き込みます。
HTTP PUT 要求を JSON として作成する
PUT
要求引数を自動的にシリアル化し、厳密に型指定された C# オブジェクトに応答を逆シリアル化するには、PutAsJsonAsync NuGet パッケージの一部である 拡張メソッドを使用します。
static async Task PutAsJsonAsync(HttpClient httpClient)
{
using HttpResponseMessage response = await httpClient.PutAsJsonAsync(
"todos/5",
new Todo(Title: "partially update todo", Completed: true));
response.EnsureSuccessStatusCode()
.WriteRequestToConsole();
var todo = await response.Content.ReadFromJsonAsync<Todo>();
Console.WriteLine($"{todo}\n");
// Expected output:
// PUT https://jsonplaceholder.typicode.com/todos/5 HTTP/1.1
// Todo { UserId = , Id = 5, Title = partially update todo, Completed = True }
}
このコードでは、次のタスクが完了します。
Todo
インスタンスを JSON としてシリアル化し、"https://jsonplaceholder.typicode.com/todos/5"
エンドポイントにPUT
要求を行います。- 応答が成功したことを確認し、要求の詳細をコンソールに書き込みます。
- 応答本文を
Todo
インスタンスに逆シリアル化し、Todo
オブジェクトをコンソールに書き込みます。
HTTP PATCH 要求を使用する
PATCH
要求は、既存のリソースに対する部分的な更新です。 この要求では、新しいリソースは作成されず、既存のリソースを置き換えるものではありません。 代わりに、このメソッドはリソースを部分的にのみ更新します。 HttpClient
インスタンスと Uri オブジェクトを指定して HTTP PATCH
要求を行うには、HttpClient.PatchAsync メソッドを使用します。
static async Task PatchAsync(HttpClient httpClient)
{
using StringContent jsonContent = new(
JsonSerializer.Serialize(new
{
completed = true
}),
Encoding.UTF8,
"application/json");
using HttpResponseMessage response = await httpClient.PatchAsync(
"todos/1",
jsonContent);
response.EnsureSuccessStatusCode()
.WriteRequestToConsole();
var jsonResponse = await response.Content.ReadAsStringAsync();
Console.WriteLine($"{jsonResponse}\n");
// Expected output
// PATCH https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
// {
// "userId": 1,
// "id": 1,
// "title": "delectus aut autem",
// "completed": true
// }
}
このコードでは、次のタスクが完了します。
- 要求の JSON 本文 (
"application/json"
の MIME の種類) を使用して、StringContent インスタンスを準備します。 PATCH
要求を"https://jsonplaceholder.typicode.com/todos/1"
エンドポイントに行います。- 応答が成功したことを確認し、JSON 応答本文を含む要求の詳細をコンソールに書き込みます。
PATCH
NuGet パッケージには、System.Net.Http.Json
要求用の拡張メソッドはありません。
HTTP DELETE 要求を使用する
DELETE
要求は既存のリソースを削除します。この要求はべき等ですが、安全ではありません。 同じリソースに対する複数の DELETE
要求によって同じ結果が得られますが、要求はリソースの状態に影響します。 HttpClient
インスタンスと Uri オブジェクトを指定して HTTP DELETE
要求を行うには、HttpClient.DeleteAsync メソッドを使用します。
static async Task DeleteAsync(HttpClient httpClient)
{
using HttpResponseMessage response = await httpClient.DeleteAsync("todos/1");
response.EnsureSuccessStatusCode()
.WriteRequestToConsole();
var jsonResponse = await response.Content.ReadAsStringAsync();
Console.WriteLine($"{jsonResponse}\n");
// Expected output
// DELETE https://jsonplaceholder.typicode.com/todos/1 HTTP/1.1
// {}
}
このコードでは、次のタスクが完了します。
DELETE
リクエストを"https://jsonplaceholder.typicode.com/todos/1"
エンドポイントに送信します。- 応答が成功したことを確認し、要求の詳細をコンソールに書き込みます。
ヒント
DELETE
要求に対する応答には、(PUT
要求と同様に) 本文が含まれる場合も含まれない場合もあります。
HTTP HEAD 要求を調べる
HEAD
要求は、GET
要求と似ています。 この要求は、リソースを返す代わりに、リソースに関連付けられているヘッダーのみを返します。 HEAD
要求に対する応答は本文を返しません。 HttpClient
インスタンスと Uri オブジェクトを指定して HTTP HEAD
要求を行うには、HttpMethod 型が HttpMethod.Head
に設定された HttpClient.SendAsync メソッドを使用します。
static async Task HeadAsync(HttpClient httpClient)
{
using HttpRequestMessage request = new(
HttpMethod.Head,
"https://www.example.com");
using HttpResponseMessage response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode()
.WriteRequestToConsole();
foreach (var header in response.Headers)
{
Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
}
Console.WriteLine();
// Expected output:
// HEAD https://www.example.com/ HTTP/1.1
// Accept-Ranges: bytes
// Age: 550374
// Cache-Control: max-age=604800
// Date: Wed, 10 Aug 2022 17:24:55 GMT
// ETag: "3147526947"
// Server: ECS, (cha / 80E2)
// X-Cache: HIT
}
このコードでは、次のタスクが完了します。
HEAD
のリクエストを"https://www.example.com/"
エンドポイントに送信します。- 応答が成功したことを確認し、要求の詳細をコンソールに書き込みます。
- すべての応答ヘッダーを反復処理し、各ヘッダーをコンソールに書き込みます。
HTTP OPTIONS 要求を調べる
OPTIONS
要求は、サーバーまたはエンドポイントでサポートされる HTTP メソッドを識別するために使用されます。 HttpClient
インスタンスと Uri オブジェクトを指定して HTTP OPTIONS
要求を行うには、HttpMethod 型が HttpMethod.Options
に設定された HttpClient.SendAsync メソッドを使用します。
static async Task OptionsAsync(HttpClient httpClient)
{
using HttpRequestMessage request = new(
HttpMethod.Options,
"https://www.example.com");
using HttpResponseMessage response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode()
.WriteRequestToConsole();
foreach (var header in response.Content.Headers)
{
Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
}
Console.WriteLine();
// Expected output
// OPTIONS https://www.example.com/ HTTP/1.1
// Allow: OPTIONS, GET, HEAD, POST
// Content-Type: text/html; charset=utf-8
// Expires: Wed, 17 Aug 2022 17:28:42 GMT
// Content-Length: 0
}
このコードでは、次のタスクが完了します。
"https://www.example.com/"
エンドポイントにOPTIONS
HTTP 要求を送信します。- 応答が成功したことを確認し、要求の詳細をコンソールに書き込みます。
- すべての応答コンテンツ ヘッダーを反復処理し、各ヘッダーをコンソールに書き込みます。
HTTP TRACE 要求を調べる
TRACE
要求は、要求メッセージのアプリケーションレベルのループバックを提供するため、デバッグに役立ちます。 HTTP TRACE
要求を行うには、HttpMethod.Trace
型を使用して HttpRequestMessage を作成します。
using HttpRequestMessage request = new(
HttpMethod.Trace,
"{ValidRequestUri}");
注意事項
すべての HTTP サーバーが TRACE
HTTP メソッドをサポートしているわけではありません。 この方法では、不正に使用すると、セキュリティの脆弱性が公開される可能性があります。 詳細については、「Open Web Application Security Project (OWASP): クロス サイト トレーシング」を参照してください。
HTTP 応答を処理する
HTTP 応答を処理するときは、HttpResponseMessage 型と対話します。 応答の有効性を評価するために、複数のメンバーが使用されます。 HTTP 状態コードは、HttpResponseMessage.StatusCode プロパティで使用できます。
クライアント インスタンスを指定して要求を送信するとします。
using HttpResponseMessage response = await httpClient.SendAsync(request);
response
が OK
(HTTP 状態コード 200) であることを確認するには、次の例に示すように値を評価します。
if (response is { StatusCode: HttpStatusCode.OK })
{
// Omitted for brevity...
}
成功した応答を表す他の HTTP 状態コードがあります。たとえば、CREATED
(HTTP 状態コード 201), ACCEPTED
(HTTP 状態コード 202), NO CONTENT
(HTTP 状態コード 204)、RESET CONTENT
(HTTP 状態コード 205) などです。 HttpResponseMessage.IsSuccessStatusCode プロパティを使用して、これらのコードを評価することもできます。これにより、応答状態コードは、200 から 299 の範囲内であることが保証されます。
if (response.IsSuccessStatusCode)
{
// Omitted for brevity...
}
フレームワークで HttpRequestException エラーをスローする必要がある場合は、HttpResponseMessage.EnsureSuccessStatusCode() メソッドを呼び出すことができます。
response.EnsureSuccessStatusCode();
応答状態コードが 200 から 299 の範囲内にない場合、このコードは HttpRequestException
エラーをスローします。
HTTP の有効なコンテンツ応答を調べる
有効な応答では、Content プロパティを使用して応答本文にアクセスできます。 本文は、ストリーム、バイト配列、または文字列として本文にアクセスするために使用できる、HttpContent インスタンスとして使用できます。
次のコードでは、responseStream
オブジェクトを使用して応答本文を読み取ります。
await using Stream responseStream =
await response.Content.ReadAsStreamAsync();
さまざまなオブジェクトを使用して、応答本文を読み取ることができます。 responseByteArray
オブジェクトを使用して、応答本文を読み取ります。
byte[] responseByteArray = await response.Content.ReadAsByteArrayAsync();
responseString
オブジェクトを使用して、応答本文を読み取ります。
string responseString = await response.Content.ReadAsStringAsync();
HTTP エンドポイントが JSON を返すことがわかっている場合は、System.Net.Http.Json NuGet パッケージを使用して、応答本文を任意の有効な C# オブジェクトに逆シリアル化できます。
T? result = await response.Content.ReadFromJsonAsync<T>();
このコードでは、result
値は、型 T
として逆シリアル化された応答本文です。
HTTP エラー処理を使用する
HTTP 要求が失敗すると、システムは HttpRequestException オブジェクトをスローします。 例外をキャッチするだけでは不十分な場合があります。 他にもスローされる可能性のある例外があり、その処理を検討する必要があります。 たとえば、呼び出し元のコードでは、要求が完了する前に取り消されたキャンセル トークンを使用できます。 このシナリオでは、TaskCanceledException エラーをキャッチできます。
using var cts = new CancellationTokenSource();
try
{
// Assuming:
// httpClient.Timeout = TimeSpan.FromSeconds(10)
using var response = await httpClient.GetAsync(
"http://localhost:5001/sleepFor?seconds=100", cts.Token);
}
catch (OperationCanceledException ex) when (cts.IsCancellationRequested)
{
// When the token has been canceled, it is not a timeout.
Console.WriteLine($"Canceled: {ex.Message}");
}
同様に、HTTP 要求を行うときに、HttpClient.Timeout 値を超える前にサーバーが応答しない場合は、同じ例外がスローされます。 このシナリオでは、TaskCanceledException エラーをキャッチするときに、Exception.InnerException プロパティを評価することでタイムアウトが発生したことを識別できます。
try
{
// Assuming:
// httpClient.Timeout = TimeSpan.FromSeconds(10)
using var response = await httpClient.GetAsync(
"http://localhost:5001/sleepFor?seconds=100");
}
catch (OperationCanceledException ex) when (ex.InnerException is TimeoutException tex)
{
Console.WriteLine($"Timed out: {ex.Message}, {tex.Message}");
}
コードでは、内部例外が TimeoutException 型の場合、タイムアウトが発生し、キャンセル トークンは要求を取り消しません。
HttpRequestException オブジェクトをキャッチしたときに HTTP 状態コードを評価するには、HttpRequestException.StatusCode プロパティを評価します。
try
{
// Assuming:
// httpClient.Timeout = TimeSpan.FromSeconds(10)
using var response = await httpClient.GetAsync(
"http://localhost:5001/doesNotExist");
response.EnsureSuccessStatusCode();
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
// Handle 404
Console.WriteLine($"Not found: {ex.Message}");
}
コードでは、応答が成功しなかった場合に例外をスローするために、EnsureSuccessStatusCode() メソッドが呼び出されます。 その後、HttpRequestException.StatusCode プロパティが評価され、応答が 404
(HTTP 状態コード 404) であったかどうかが判断されます。 HttpClient
オブジェクトには、代わりに EnsureSuccessStatusCode
メソッドを暗黙的に呼び出すヘルパー メソッドがいくつかあります。
HTTP エラーの受け渡しについては、次の API を検討してください。
ヒント
HttpResponseMessage
型を返さない HTTP 要求を行うために使用されるすべての HttpClient
メソッドは、ユーザーに代わって EnsureSuccessStatusCode
メソッドを暗黙的に呼び出します。
これらのメソッドを呼び出すと、HttpRequestException
オブジェクトを処理し、HttpRequestException.StatusCode プロパティを評価して、応答の HTTP 状態コードを決定できます。
try
{
// These extension methods will throw HttpRequestException
// with StatusCode set when the HTTP request status code isn't 2xx:
//
// GetByteArrayAsync
// GetStreamAsync
// GetStringAsync
using var stream = await httpClient.GetStreamAsync(
"https://localhost:5001/doesNotExists");
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
// Handle 404
Console.WriteLine($"Not found: {ex.Message}");
}
コード内で HttpRequestException オブジェクトを投げる必要があるような状況が発生することがあります。 HttpRequestException() コンストラクターはパブリックであり、それを使用してカスタム メッセージで例外をスローできます。
try
{
using var response = await httpClient.GetAsync(
"https://localhost:5001/doesNotExists");
// Throw for anything higher than 400.
if (response is { StatusCode: >= HttpStatusCode.BadRequest })
{
throw new HttpRequestException(
"Something went wrong", inner: null, response.StatusCode);
}
}
catch (HttpRequestException ex) when (ex is { StatusCode: HttpStatusCode.NotFound })
{
Console.WriteLine($"Not found: {ex.Message}");
}
HTTP プロキシを構成する
HTTP プロキシは、次の 2 つの方法のいずれかで構成できます。 既定値が HttpClient.DefaultProxy プロパティで指定されます。 または、HttpClientHandler.Proxy プロパティでプロキシを指定することもできます。
グローバルな既定のプロキシを使用する
HttpClient.DefaultProxy
プロパティは、コンストラクターを介して渡される HttpClientHandler オブジェクトにプロキシが明示的に設定されていない場合に、すべての HttpClient
インスタンスが使用する既定のプロキシを決定する静的プロパティです。
このプロパティによって返される既定のインスタンスは、プラットフォームに応じて異なる規則のセットに従って初期化されます。
- Windows: 環境変数からプロキシ構成を読み取るか、変数が定義されていない場合は、ユーザー プロキシ設定から読み取ります。
- macOS: 環境変数からプロキシ構成を読み取るか、変数が定義されていない場合は、システム プロキシ設定から読み取ります。
- Linux: 環境変数からプロキシ構成を読み取るか、変数が定義されていない場合は、すべてのアドレスをバイパスするように構成されていないインスタンスを初期化します。
Windows および Unix ベースのプラットフォームでの DefaultProxy
プロパティの初期化では、次の環境変数が使用されます。
HTTP_PROXY
: HTTP 要求で使用されるプロキシ サーバー。HTTPS_PROXY
: HTTPS 要求で使用されるプロキシ サーバー。ALL_PROXY
:HTTP_PROXY
変数またはHTTPS_PROXY
変数が定義されていない場合に、HTTP 要求または HTTPS 要求で使用されるプロキシ サーバー。NO_PROXY
: プロキシから除外するホスト名のコンマ区切りのリスト。 ワイルドカードではアスタリスクはサポートされていません。 サブドメインと一致させる場合は、先頭のピリオド (.) を使用します。 例:NO_PROXY=.example.com
(先頭のピリオドを持つ) はwww.example.com
と一致しますが、example.com
と一致しません。NO_PROXY=example.com
(先頭のピリオドなし) がwww.example.com
と一致しません。 この動作は、今後、他のエコシステムに合わせる目的で再検討される可能性があります。
環境変数で大文字と小文字が区別されるシステムでは、変数名はすべて小文字でもすべて大文字でもかまいません。 小文字の名前が最初にチェックされます。
プロキシ サーバーには、ホスト名または IP アドレス、必要に応じてコロンとポート番号、またはプロキシ認証用のユーザー名とパスワードを含む http
URL を指定できます。 URL は、https
ではなく http
で始まる必要があり、ホスト名、IP、またはポートの後にテキストを含めることはできません。
クライアントごとにプロキシを構成する
HttpClientHandler.Proxy プロパティは、インターネット リソースへの要求の処理に使用する WebProxy オブジェクトを識別します。 プロキシを使わない場合は、Proxy
プロパティを GlobalProxySelection.GetEmptyWebProxy() メソッドから返されるプロキシ インスタンスに設定します。
ローカル コンピューターまたはアプリケーション構成ファイルで、既定のプロキシが使用されることを指定する場合があります。 Proxy
プロパティが指定されている場合、Proxy
プロパティのプロキシ設定はローカル コンピューターまたはアプリケーション構成ファイルをオーバーライドし、ハンドラーは指定されたプロキシ設定を使用します。 構成ファイルにプロキシが指定されておらず、Proxy
プロパティが指定されていない場合、ハンドラーはローカル コンピューターから継承されたプロキシ設定を使用します。 プロキシ設定が存在しない場合、要求はサーバーに直接送信されます。
HttpClientHandler クラスは、ローカル コンピューターの設定から継承されたワイルドカード文字を使用してプロキシ バイパス リストを解析します。 たとえば、HttpClientHandler
クラスは、ブラウザーからの "nt*"
というバイパス リストを、"nt.*"
という正規表現として解析します。 そのため、http://nt.com
の URL は、HttpClientHandler
クラスを使用してプロキシをバイパスします。
HttpClientHandler
クラスでは、ローカル プロキシのバイパスがサポートされています。 次のいずれかの条件が満たされている場合、クラスは宛先をローカルと見なします。
- 宛先にフラット名が含まれています (URL にピリオド (.) はありません)。
- 宛先にループバック アドレス (Loopback または IPv6Loopback) が含まれているか、ローカル コンピューターに割り当てられた IPAddress プロパティが宛先に含まれています。
- 宛先のドメイン サフィックスは、DomainName プロパティで定義されているローカル コンピューターのドメイン サフィックスと一致します。
プロキシの構成の詳細については、次の API を参照してください。
- WebProxy.Address プロパティ
- WebProxy.BypassProxyOnLocal プロパティ
- WebProxy.BypassArrayList プロパティ
次の手順
.NET