共用方式為


使用 HttpClient 類別提出 HTTP 要求

在本文中,您將了解如何使用 HttpClient 類別提出 HTTP 要求並處理回應。

重要

所有 HTTP 要求範例都會以下列其中一個 URL 為目標:

HTTP 端點通常會傳回 JavaScript 物件標記法 (JSON) 資料,但不一定都是如此。 為了方便起見,選擇性 System.Net.Http.Json NuGet 套件會提供 HttpClientHttpContent 的數個擴充方法,以使用 System.Text.Json 執行自動序列化和還原序列化。 下列範例會讓您注意到可使用這些擴充功能的地方。

提示

本文中的所有原始程式碼都可在 GitHub:.NET Docs 存放庫中取得。

建立 HttpClient

下列大部分範例都會重複使用相同的 HttpClient 執行個體,因此只需要設定一次。 若要建立 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 執行個體會使用基底位址。 若要套用其他設定,請考慮:

提示

或者,您可以使用處理站模式方法來建立 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
HttpClient.SendAsync

USER SPECIFIED 要求表示 SendAsync 方法接受任何有效的 HttpMethod

警告

提出 HTTP 要求會視為網路 I/O 繫結工作。 雖然有同步 HttpClient.Send 方法,但建議您改用非同步 API,除非您有不要這麼做的好理由。

注意

雖然以 Android 裝置為目標 (例如使用 .NET MAUI 開發),但您必須在 AndroidManifest.xml 中將 android:usesCleartextTraffic="true" 新增至 <application></application>。 這會啟用純文字流量,例如 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 實體本文和對應的內容標頭。 對於需要本文的 HTTP 方法 (或要求方法),也就是 POSTPUTPATCH,您可以使用 HttpContent 類別來指定要求的本文。 大部分範例都會示範如何使用 JSON 承載來準備 StringContent 子類別,但不同內容 (MIME) 型別會有其他子類別。

HttpContent 類別也可用來表示 HttpResponseMessage 的回應本文,並且可在 HttpResponseMessage.Content 屬性上存取。

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/3GET 要求會輸出下列訊息的要求:

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,具有選擇性的 IdTitleCompletedUserId 屬性。 如需 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 型別。 若要提出 HTTP POST 要求,在 HttpClientUri 的情況下,請使用 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 本文 (MIME 型別為 "application/json") 來準備 StringContent 執行個體。
  • "https://jsonplaceholder.typicode.com/todos" 提出 POST 要求。
  • 確定回應成功,並將要求詳細資料寫入主控台。
  • 以字串形式將回應本文寫入主控台。

JSON 形式的 HTTP Post

若要自動序列化 POST 要求引數,並將回應還原序列化為強型別 C# 物件,請使用屬於 System.Net.Http.Json NuGet 套件的 PostAsJsonAsync 擴充方法。

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,並對 POST 提出 "https://jsonplaceholder.typicode.com/todos" 要求。
  • 確定回應成功,並將要求詳細資料寫入主控台。
  • 將回應本文還原序列化為 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 本文 (MIME 型別為 "application/json") 來準備 StringContent 執行個體。
  • "https://jsonplaceholder.typicode.com/todos/1" 提出 PUT 要求。
  • 確定回應成功,並將要求詳細資料和 JSON 回應本文寫入主控台。

JSON 形式的 HTTP Put

若要自動序列化 PUT 要求引數,並將回應還原序列化為強型別 C# 物件,請使用屬於 System.Net.Http.Json NuGet 套件的 PutAsJsonAsync 擴充方法。

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,並對 PUT 提出 "https://jsonplaceholder.typicode.com/todos/5" 要求。
  • 確定回應成功,並將要求詳細資料寫入主控台。
  • 將回應本文還原序列化為 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 本文 (MIME 型別為 "application/json") 來準備 StringContent 執行個體。
  • "https://jsonplaceholder.typicode.com/todos/1" 提出 PATCH 要求。
  • 確定回應成功,並將要求詳細資料和 JSON 回應本文寫入主控台。

System.Net.Http.Json NuGet 套件中沒有 PATCH 要求的擴充方法。

HTTP Delete

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
    //   {}
}

上述 程式碼:

  • "https://jsonplaceholder.typicode.com/todos/1" 提出 DELETE 要求。
  • 確定回應成功,並將要求詳細資料寫入主控台。

提示

DELETE 要求的回應 (就像 PUT 要求) 可能會或可能不會包含本文。

HTTP Head

HEAD 要求類似於 GET 要求。 只會傳回與資源相關聯的標頭,而不是傳回資源。 HEAD 要求的回應不會傳回本文。 若要在指定 HttpClient 和 URI 的情況下提出 HTTP HEAD 要求,請使用 HttpClient.SendAsync 方法,並將 HttpMethod 設定為 HttpMethod.Head

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
}

上述 程式碼:

  • "https://www.example.com/" 提出 HEAD 要求。
  • 確定回應成功,並將要求詳細資料寫入主控台。
  • 逐一查看所有回應標頭,將每一個標頭寫入主控台。

HTTP Options

OPTIONS 要求會用來識別伺服器或端點支援的 HTTP 方法。 若要在指定 HttpClient 和 URI 的情況下提出 HTTP OPTIONS 要求,請使用 HttpClient.SendAsync 方法,並將 HttpMethod 設定為 HttpMethod.Options

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
}

上述 程式碼:

  • OPTIONS HTTP 要求傳送至 "https://www.example.com/"
  • 確定回應成功,並將要求詳細資料寫入主控台。
  • 逐一查看所有回應內容標頭,將每一個標頭寫入主控台。

HTTP Trace

TRACE 要求對於偵錯很有用,因為其提供要求訊息的應用層級回送。 若要提出 HTTP TRACE 要求,請使用 HttpMethod.Trace 建立 HttpRequestMessage

using HttpRequestMessage request = new(
    HttpMethod.Trace, 
    "{ValidRequestUri}");

警告

所有 HTTP 伺服器都不支援 TRACE HTTP 方法。 如果不小心使用,可能會暴露安全性弱點。 如需詳細資訊,請參閱 pen Web Application Security Project (OWASP):跨網站追蹤

處理 HTTP 回應

每次處理 HTTP 回應時,都須與 HttpResponseMessage 型別互動。 評估回應的有效性時會使用數個成員。 HTTP 狀態碼可透過 HttpResponseMessage.StatusCode 屬性取得。 假設您已在具有指定用戶端執行個體的情況下傳送要求:

using HttpResponseMessage response = await httpClient.SendAsync(request);

若要確保 responseOK (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 執行個體使用,您可以使用此執行個體,以資料流、位元組陣列或字串形式存取本文:

await using Stream responseStream =
    await response.Content.ReadAsStreamAsync();

在上述程式碼中,responseStream 可用來讀取回應本文。

byte[] responseByteArray = await response.Content.ReadAsByteArrayAsync();

在上述程式碼中,responseByteArray 可用來讀取回應本文。

string responseString = await response.Content.ReadAsStringAsync();

在上述程式碼中,responseString 可用來讀取回應本文。

最後,當您知道 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,請考慮下列 API:

提示

用來提出 HTTP 要求但不回傳回 HttpResponseMessage 的所有 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}");
}

在某些情況下,您可能需要在程式碼中擲回 HttpRequestExceptionHttpRequestException() 建構函式是公用的,而且可用來擲回具有自訂訊息的例外狀況:

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 Proxy

HTTP Proxy 可以透過下列兩種方式之一來設定。 HttpClient.DefaultProxy 屬性上會指定預設值。 或者,您可以在 HttpClientHandler.Proxy 屬性上指定 Proxy。

全域預設 Proxy

HttpClient.DefaultProxy 是靜態屬性,如果透過建構函式傳遞的 HttpClientHandler 中未明確設定 Proxy,該屬性可決定所有 HttpClient 執行個體使用的預設 Proxy。

此屬性所傳回的預設執行個體會根據您的平台,初始化下列一組不同的規則:

  • 針對 Windows:從環境變數讀取 Proxy 組態,或者,如果未定義,則從使用者的 Proxy 設定中讀取。
  • 針對 macOS:從環境變數讀取 Proxy 組態,或者,如果未定義,則從系統的 Proxy 設定中讀取。
  • 針對 Linux:從環境變數讀取 Proxy 組態,或者,如果未定義,此屬性會初始化略過所有位址的非設定執行個體。

Windows 和 Unix 平台上用於 DefaultProxy 初始化的環境變數如下:

  • HTTP_PROXY:用於 HTTP 要求的 Proxy 伺服器。
  • HTTPS_PROXY:用於 HTTPS 要求的 Proxy 伺服器。
  • ALL_PROXY:在未定義 HTTP_PROXY 和/或 HTTPS_PROXY 的情況下,用於 HTTP 和/或 HTTPS 要求的 Proxy 伺服器。
  • NO_PROXY:應從 Proxy 處理中排除的主機名稱清單 (以逗號分隔)。 不支援以星號作為萬用字元;如果您想要比對子網域,請使用前置點。 範例:NO_PROXY=.example.com (具有前置點) 會符合 www.example.com,但將不會符合 example.comNO_PROXY=example.com (沒有前置點) 將不會符合 www.example.com。 未來可能會重新檢視此行為,以更符合其他生態系統。

在環境變數區分大小寫的系統上,變數名稱可以是全部小寫或全部大寫。 系統會先檢查小寫名稱。

Proxy 伺服器可能是主機名稱或 IP 位址,後面可選擇性地接著冒號和連接埠號碼,或者可能是 http URL,可選擇性地包含用於 Proxy 驗證的使用者名稱和密碼。 URL 必須以 http 開頭 (不是 https),而且不能在主機名稱、IP 或連接埠之後包含任何文字。

每個用戶端的 Proxy

HttpClientHandler.Proxy 屬性會識別 WebProxy 物件,以用來處理向網際網路資源發出的要求。 若要指定不應使用 Proxy,請將 Proxy 屬性設定為 GlobalProxySelection.GetEmptyWebProxy() 方法所傳回的 Proxy 執行個體。

本機電腦或應用程式組態檔可能會指定要使用的預設 Proxy。 如果指定 Proxy 屬性,則 Proxy 屬性中的 Proxy 設定會覆寫本機電腦或應用程式組態檔,而處理常式會使用指定的 Proxy 設定。 如果未在組態檔中指定 Proxy,且未指定 Proxy 屬性,則處理常式會使用繼承自本機電腦的 Proxy 設定。 如果沒有 Proxy 設定,則會將要求直接傳送至伺服器。

HttpClientHandler 類別會剖析繼承自本機電腦設定的 Proxy 略過清單 (具有萬用字元)。 例如,HttpClientHandler 類別會將瀏覽器中的 "nt*" 略過清單剖析為 "nt.*" 的規則運算式。 因此,http://nt.com URL 會略過使用 HttpClientHandler 類別的 Proxy。

HttpClientHandler 類別支援本機 Proxy 略過。 如果符合下列任何條件,該類別會將目的地視為本機:

  1. 目的地包含一般名稱 (URL 中沒有點)。
  2. 目的地包含回送位址 (LoopbackIPv6Loopback),或目的地包含指派給本機電腦的 IPAddress
  3. 目的地的網域尾碼符合本機電腦的網域尾碼 (DomainName)。

如需設定 Proxy 的詳細資訊,請參閱:

另請參閱