Udostępnij za pośrednictwem


Wysyłaj żądania HTTP za pomocą klasy HttpClient

Z tego artykułu dowiesz się, jak wysyłać żądania HTTP i obsługiwać odpowiedzi za pomocą klasy HttpClient.

Ważne

Wszystkie przykładowe żądania HTTP w tym artykule są przeznaczone dla jednego z następujących adresów URL:

Punkty końcowe HTTP często zwracają dane JavaScript Object Notation (JSON), ale nie zawsze. Dla wygody opcjonalny pakiet NuGet System.Net.Http.Json zawiera kilka metod rozszerzenia dla obiektów HttpClient i HttpContent, które wykonują automatyczną serializacji i deserializacji przy użyciu pakietu 📦 System.Text.Json NuGet. Przykłady w tym artykule zwracają uwagę na miejsca, w których są dostępne te rozszerzenia.

Napiwek

Cały kod źródłowy, do których odwołuje się ten artykuł, jest dostępny w repozytorium GitHub: .NET Docs.

Tworzenie obiektu HttpClient

Większość przykładów w tym artykule używa ponownie tego samego wystąpienia HttpClient, aby można było skonfigurować wystąpienie raz i użyć go do pozostałych przykładów. Aby utworzyć obiekt HttpClient, użyj konstruktora klasy HttpClient. Aby uzyskać więcej informacji, zobacz Wytyczne dotyczące korzystania z obiektu 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"),
};

Kod wykonuje następujące zadania:

  • Utwórz nowe wystąpienie HttpClient jako zmienną static. Zgodnie z wytycznymi zalecaną metodą jest ponowne użycie wystąpień HttpClient w cyklu życia aplikacji.
  • Ustaw właściwość HttpClient.BaseAddress na wartość "https://jsonplaceholder.typicode.com".

To wystąpienie HttpClient używa adresu podstawowego do tworzenia kolejnych żądań. Aby zastosować inne konfiguracje, rozważ następujące interfejsy API:

Napiwek

Alternatywnie można utworzyć wystąpienia HttpClient przy użyciu podejścia opartego na wzorcu fabryki, które pozwala skonfigurować dowolną liczbę klientów i korzystać z nich jako usługi iniekcji zależności. Aby uzyskać więcej informacji, zobacz Http client factory with .NET (Fabryka klienta HTTP za pomocą platformy .NET).

Wysyłanie żądania HTTP

Aby wysłać żądanie HTTP, należy wywołać dowolną z następujących metod interfejsu API:

Metoda 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

żądanie USER SPECIFIED wskazuje, że metoda SendAsync akceptuje dowolny prawidłowy obiekt HttpMethod.

Ostrzeżenie

Wykonywanie żądań HTTP jest uznawane za pracę związaną z we/wy sieci. Istnieje synchroniczna metoda HttpClient.Send, ale zaleceniem jest użycie asynchronicznych interfejsów API, chyba że istnieje uzasadniony powód, aby tego nie zrobić.

Uwaga

Podczas celowania w urządzenia z systemem Android (jak w przypadku tworzenia aplikacji .NET MAUI) należy dodać definicję android:usesCleartextTraffic="true" do sekcji <application></application> w pliku AndroidManifest.xml. To ustawienie umożliwia ruch w postaci zwykłego tekstu, taki jak żądania HTTP, który jest domyślnie wyłączony z powodu zasad zabezpieczeń systemu Android. Rozważmy następujące przykładowe ustawienia 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>

Aby uzyskać więcej informacji, zobacz Włącz ruch sieciowy w formacie tekstu jawnego dla domeny localhost.

Omówienie zawartości HTTP

Typ HttpContent jest używany do reprezentowania treści jednostki HTTP i odpowiednich nagłówków zawartości. W przypadku metod HTTP (lub metod żądań), które wymagają treści (POST, PUT, PATCH), należy użyć klasy HttpContent, aby określić treść żądania. Większość przykładów pokazuje, jak przygotować podklasę StringContent z ładunkiem JSON, ale inne podklasy istnieją dla różnych typów zawartości (MIME).

  • ByteArrayContent: udostępnia zawartość HTTP na podstawie tablicy bajtów.
  • FormUrlEncodedContent: Udostępnia zawartość HTTP dla krotek nazwa/wartość zakodowanych przy użyciu typu "application/x-www-form-urlencoded" MIME.
  • JsonContent: udostępnia zawartość HTTP opartą na formacie JSON.
  • MultipartContent: udostępnia kolekcję obiektów HttpContent, które są serializowane przy użyciu specyfikacji typu MIME "multipart/*".
  • MultipartFormDataContent: udostępnia kontener dla zawartości zakodowanej przy użyciu typu miME "multipart/form-data".
  • ReadOnlyMemoryContent: udostępnia zawartość HTTP w oparciu o wartość ReadOnlyMemory<T>.
  • StreamContent: udostępnia zawartość HTTP na podstawie strumienia.
  • StringContent: udostępnia zawartość HTTP na podstawie ciągu znaków.

Klasa HttpContent jest również używana do reprezentowania treści odpowiedzi klasy HttpResponseMessage, która jest dostępna we właściwości HttpResponseMessage.Content.

Używanie żądania HTTP GET

Żądanie GET nie powinno wysyłać treści. To żądanie jest używane (jak wskazuje nazwa metody) do pobierania (lub pobierania) danych z zasobu. Aby utworzyć żądanie GET HTTP przy użyciu wystąpienia HttpClient i obiektu Uri, użyj metody 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
    //   }
}

Kod wykonuje następujące zadania:

  • Prześlij żądanie GET do punktu końcowego "https://jsonplaceholder.typicode.com/todos/3".
  • Upewnij się, że odpowiedź zakończyła się pomyślnie.
  • Zapisz szczegóły żądania w konsoli programu .
  • Odczytaj treść odpowiedzi jako ciąg.
  • Napisz treść odpowiedzi JSON do konsoli.

Metoda WriteRequestToConsole to rozszerzenie niestandardowe, które nie jest częścią struktury. Jeśli interesuje Cię implementacja, rozważ następujący kod języka 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}");        
    }
}

Ta funkcja służy do zapisywania szczegółów żądania w konsoli w następującym formularzu:

<HTTP Request Method> <Request URI> <HTTP/Version>

Na przykład żądanie GET do punktu końcowego "https://jsonplaceholder.typicode.com/todos/3" zwraca następujący komunikat:

GET https://jsonplaceholder.typicode.com/todos/3 HTTP/1.1

Tworzenie żądania HTTP GET na podstawie kodu JSON

Punkt końcowy https://jsonplaceholder.typicode.com/todos zwraca tablicę JSON obiektów Todo. Ich struktura JSON przypomina następującą formę:

[
  {
    "userId": 1,
    "id": 1,
    "title": "example title",
    "completed": false
  },
  {
    "userId": 1,
    "id": 2,
    "title": "another example title",
    "completed": true
  },
]

Obiekt języka C# Todo jest zdefiniowany w następujący sposób:

public record class Todo(
    int? UserId = null,
    int? Id = null,
    string? Title = null,
    bool? Completed = null);

Jest to typ record class z opcjonalnymi właściwościami Id, Title, Completed i UserId. Aby uzyskać więcej informacji na record temat typu, zobacz Wprowadzenie do typów rekordów w języku C#. Aby automatycznie deserializować żądania GET do silnie typizowanego obiektu C#, użyj metody rozszerzenia GetFromJsonAsync będącej częścią pakietu NuGet 📦 System.Net.Http.Json.

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

Kod wykonuje następujące zadania:

  • Utwórz żądanie GET do "https://jsonplaceholder.typicode.com/todos?userId=1&completed=false".

    Ciąg zapytania reprezentuje kryteria filtrowania dla żądania. Gdy polecenie zakończy się pomyślnie, odpowiedź zostanie automatycznie zdeserializowana w obiekt List<Todo>.

  • Zapisz szczegóły żądania w konsoli wraz z każdym obiektem Todo.

Używanie żądania HTTP POST

Żądanie POST wysyła dane do serwera na potrzeby przetwarzania. Nagłówek Content-Type żądania wskazuje, jaki typ MIME wysyła ciało. Aby utworzyć żądanie POST HTTP przy użyciu wystąpienia HttpClient i obiektu Uri, użyj metody 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
    //   }
}

Kod wykonuje następujące zadania:

  • Przygotuj wystąpienie StringContent z treścią JSON żądania (typ MIME "application/json").
  • Prześlij żądanie POST do punktu końcowego "https://jsonplaceholder.typicode.com/todos".
  • Upewnij się, że odpowiedź zakończyła się pomyślnie i zapisz szczegóły żądania w konsoli.
  • Napisz treść odpowiedzi jako ciąg w konsoli.

Tworzenie żądania HTTP POST jako kodu JSON

Aby automatycznie serializować argumenty żądań POST i deserializować odpowiedzi na silnie typizowane obiekty języka C#, użyj metody rozszerzenia PostAsJsonAsync, która jest częścią pakietu NuGet System.Net.Http.Json.

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

Kod wykonuje następujące zadania:

  • Serializuj wystąpienie Todo jako kod JSON i utwórz żądanie POST do punktu końcowego "https://jsonplaceholder.typicode.com/todos".
  • Upewnij się, że odpowiedź zakończyła się pomyślnie i zapisz szczegóły żądania w konsoli.
  • Deserializuj treść odpowiedzi w wystąpieniu Todo i zapisz obiekt Todo w konsoli.

Używanie żądania HTTP PUT

Metoda żądania PUT zastępuje istniejący zasób lub tworzy nowy za pomocą ładunku treści żądania. Aby utworzyć żądanie PUT HTTP przy użyciu wystąpienia HttpClient i obiektu Uri, użyj metody 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
    //   }
}

Kod wykonuje następujące zadania:

  • Przygotuj wystąpienie StringContent z treścią JSON żądania (typ MIME "application/json").
  • Prześlij żądanie PUT do punktu końcowego "https://jsonplaceholder.typicode.com/todos/1".
  • Upewnij się, że odpowiedź zakończyła się pomyślnie i zapisz szczegóły żądania przy użyciu treści odpowiedzi JSON do konsoli.

Tworzenie żądania HTTP PUT jako kodu JSON

Aby automatycznie serializować PUT argumenty żądań i deserializować odpowiedzi na silnie typizowane obiekty języka C#, użyj metody rozszerzenia PutAsJsonAsync, która jest częścią pakietu NuGet System.Net.Http.Json.

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

Kod wykonuje następujące zadania:

  • Serializuj wystąpienie Todo jako kod JSON i utwórz żądanie PUT do punktu końcowego "https://jsonplaceholder.typicode.com/todos/5".
  • Upewnij się, że odpowiedź zakończyła się pomyślnie i zapisz szczegóły żądania w konsoli.
  • Deserializuj treść odpowiedzi w wystąpieniu Todo i zapisz obiekty Todo w konsoli.

Użyj żądania HTTP PATCH

Żądanie PATCH jest częściową aktualizacją istniejącego zasobu. To żądanie nie tworzy nowego zasobu i nie ma na celu zastąpienia istniejącego zasobu. Zamiast tego ta metoda tylko częściowo aktualizuje zasób. Aby utworzyć żądanie PATCH HTTP przy użyciu wystąpienia HttpClient i obiektu Uri, użyj metody 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
    //   }
}

Kod wykonuje następujące zadania:

  • Przygotuj instancję StringContent z treści żądania w formacie JSON (typ MIME "application/json").
  • Prześlij żądanie PATCH do punktu końcowego "https://jsonplaceholder.typicode.com/todos/1".
  • Upewnij się, że odpowiedź zakończyła się pomyślnie i zapisz szczegóły żądania przy użyciu treści odpowiedzi JSON do konsoli.

Nie istnieją żadne metody rozszerzenia dla PATCH w pakiecie System.Net.Http.Json NuGet.

Używanie żądania HTTP DELETE

Żądanie DELETE usuwa istniejący zasób, a żądanie jest idempotentne, ale nie bezpieczne. Wiele DELETE żądań do tych samych zasobów daje ten sam wynik, ale żądanie wpływa na stan zasobu. Aby utworzyć żądanie DELETE HTTP przy użyciu wystąpienia HttpClient i obiektu Uri, użyj metody 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
    //   {}
}

Kod wykonuje następujące zadania:

  • Prześlij żądanie DELETE do punktu końcowego "https://jsonplaceholder.typicode.com/todos/1".
  • Upewnij się, że odpowiedź zakończyła się pomyślnie i zapisz szczegóły żądania w konsoli.

Napiwek

Odpowiedź na żądanie DELETE (podobnie jak żądanie PUT) może lub nie może zawierać treści.

Eksplorowanie żądania HTTP HEAD

Żądanie HEAD jest podobne do GET żądania. Zamiast zwracać zasób, to żądanie zwraca tylko nagłówki skojarzone z zasobem. Odpowiedź na HEAD żądanie nie zwraca treści. Aby wysłać żądanie HEAD HTTP przy użyciu wystąpienia HttpClient i obiektu Uri, użyj metody HttpClient.SendAsync z typem HttpMethod ustawionym na 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
}

Kod wykonuje następujące zadania:

  • Prześlij żądanie HEAD do punktu końcowego "https://www.example.com/".
  • Upewnij się, że odpowiedź zakończyła się pomyślnie i zapisz szczegóły żądania w konsoli.
  • Przejdź przez wszystkie nagłówki odpowiedzi i zapisuj każdy nagłówek do konsoli.

Eksplorowanie żądania HTTP OPTIONS

Żądanie służy do identyfikowania OPTIONS metod HTTP obsługiwanych przez serwer lub punkt końcowy. Aby wysłać żądanie OPTIONS HTTP przy użyciu wystąpienia HttpClient i obiektu Uri, użyj metody HttpClient.SendAsync z typem HttpMethod ustawionym na 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
}

Kod wykonuje następujące zadania:

  • Wyślij żądanie HTTP OPTIONS do punktu końcowego "https://www.example.com/".
  • Upewnij się, że odpowiedź zakończyła się pomyślnie i zapisz szczegóły żądania w konsoli.
  • Iteruj przez wszystkie nagłówki zawartości w odpowiedzi i zapisuj każdy z nich w konsoli.

Eksplorowanie żądania HTTP TRACE

Żądanie TRACE może być przydatne do debugowania, ponieważ zapewnia odbicie komunikatu żądania na poziomie aplikacji. Aby utworzyć żądanie TRACE HTTP, utwórz HttpRequestMessage przy użyciu typu HttpMethod.Trace:

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

Uwaga

Nie wszystkie serwery HTTP obsługują metodę http TRACE. Ta metoda może ujawnić lukę w zabezpieczeniach, jeśli jest używana nierozsądnie. Aby uzyskać więcej informacji, zobacz Open Web Application Security Project (OWASP): Cross Site Tracing (Open Web Application Security Project: Cross Site Tracing).

Obsługa odpowiedzi HTTP

Gdy obsługujesz odpowiedź HTTP, wchodzisz w interakcję z typem HttpResponseMessage. Kilku członków służy do oceny prawidłowości odpowiedzi. Kod stanu HTTP jest dostępny we właściwości HttpResponseMessage.StatusCode.

Załóżmy, że wysyłasz żądanie dla danego wystąpienia klienta:

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

Aby upewnić się, że response jest OK (kod stanu HTTP 200), możesz ocenić wartość, jak pokazano w poniższym przykładzie:

if (response is { StatusCode: HttpStatusCode.OK })
{
    // Omitted for brevity...
}

Istnieją inne kody stanu HTTP, które reprezentują pomyślną odpowiedź, takie jak CREATED (kod stanu HTTP 201), ACCEPTED (kod stanu HTTP 202), NO CONTENT (kod stanu HTTP 204) i RESET CONTENT (kod stanu HTTP 205). Za pomocą HttpResponseMessage.IsSuccessStatusCode właściwości można również ocenić te kody, co gwarantuje, że kod stanu odpowiedzi mieści się w zakresie 200–299:

if (response.IsSuccessStatusCode)
{
    // Omitted for brevity...
}

Jeśli chcesz, aby struktura zgłaszała błąd HttpRequestException, możesz wywołać metodę HttpResponseMessage.EnsureSuccessStatusCode():

response.EnsureSuccessStatusCode();

Ten kod zgłasza błąd HttpRequestException, jeśli kod stanu odpowiedzi nie mieści się w zakresie od 200 do 299.

Eksplorowanie prawidłowych odpowiedzi na zawartość HTTP

Można uzyskać dostęp do treści odpowiedzi przy użyciu właściwości Content, jeśli odpowiedź jest prawidłowa. Treść jest dostępna jako wystąpienie HttpContent, którego można użyć do uzyskiwania dostępu do treści jako strumienia, tablicy bajtów lub ciągu.

Poniższy kod używa obiektu responseStream do odczytania treści odpowiedzi:

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

Aby odczytać treść odpowiedzi, możesz użyć różnych obiektów. Użyj obiektu responseByteArray, aby odczytać treść odpowiedzi:

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

Użyj obiektu responseString, aby odczytać treść odpowiedzi:

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

Gdy wiesz, że punkt końcowy HTTP zwraca kod JSON, możesz wykonać deserializacji treści odpowiedzi do dowolnego prawidłowego obiektu języka C#, używając System.Net.Http.Json pakietu NuGet:

T? result = await response.Content.ReadFromJsonAsync<T>();

W tym kodzie wartość result jest treścią odpowiedzi, deserializowaną jako typ T.

Korzystanie z obsługi błędów HTTP

Gdy żądanie HTTP zakończy się niepowodzeniem, system zgłasza obiekt HttpRequestException. Przechwycenie samego wyjątku może nie być wystarczające. Istnieją inne potencjalne wyjątki, które warto rozważyć. Na przykład kod wywołujący może używać tokenu anulowania, który został anulowany przed ukończeniem żądania. W tym scenariuszu można przechwycić błąd 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}");
}

Podobnie, gdy wysyłasz żądanie HTTP, jeśli serwer nie odpowiada przed przekroczeniem wartości HttpClient.Timeout, zostanie zgłoszony ten sam wyjątek. W tym scenariuszu można stwierdzić, że wystąpił limit czasu, oceniając wartość właściwości Exception.InnerException podczas przechwytywania błędu TaskCanceledException.

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}");
}

W kodzie, gdy wyjątek wewnętrzny jest typem TimeoutException, wystąpił limit czasu, a token anulowania nie anuluje żądania.

Aby ocenić kod stanu HTTP podczas przechwytywania obiektu HttpRequestException, możesz ocenić właściwość 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}");
}

W kodzie wywoływana jest metoda EnsureSuccessStatusCode(), aby zgłosić wyjątek, jeśli odpowiedź nie powiedzie się. Następnie właściwość HttpRequestException.StatusCode zostaje oceniona w celu określenia, czy odpowiedź była (404 kod stanu HTTP 404). Istnieje kilka metod pomocniczych w obiekcie HttpClient, które niejawnie wywołują metodę EnsureSuccessStatusCode w Twoim imieniu.

W przypadku przekazywania błędów HTTP rozważ następujące interfejsy API:

Napiwek

Wszystkie metody HttpClient używane do wykonywania żądań HTTP, które nie zwracają typu HttpResponseMessage, niejawnie wywołują metodę EnsureSuccessStatusCode automatycznie w Twoim imieniu.

Podczas wywoływania tych metod można obsłużyć obiekt HttpRequestException i ocenić właściwość HttpRequestException.StatusCode w celu określenia kodu stanu HTTP odpowiedzi:

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}");
}

Mogą istnieć scenariusze, w których należy wyrzucić obiekt HttpRequestException w kodzie. Konstruktor HttpRequestException() jest publiczny i można go użyć do zgłaszania wyjątku z niestandardowym komunikatem:

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}");
}

Konfigurowanie serwera proxy HTTP

Serwer proxy HTTP można skonfigurować na jeden z dwóch sposobów. Wartość domyślna jest określona we HttpClient.DefaultProxy właściwości . Alternatywnie można określić serwer proxy w HttpClientHandler.Proxy właściwości.

Używanie globalnego domyślnego serwera proxy

Właściwość HttpClient.DefaultProxy jest właściwością statyczną, która określa domyślny serwer proxy używany przez wszystkie wystąpienia HttpClient, jeśli żaden serwer proxy nie jest jawnie ustawiony w obiekcie HttpClientHandler przekazywanym przez jego konstruktora.

Domyślne wystąpienie zwrócone przez tę właściwość jest inicjalizowane zgodnie z innym zestawem reguł w zależności od platformy.

  • Windows: Odczytaj konfigurację proxy ze zmiennych środowiskowych lub, jeśli zmienne nie są zdefiniowane, z ustawień proxy użytkownika.
  • systemu macOS: odczyt konfiguracji serwera proxy ze zmiennych środowiskowych lub jeśli zmienne nie są zdefiniowane, odczyt z ustawień serwera proxy systemu.
  • Systemu Linux: odczyt konfiguracji serwera proxy ze zmiennych środowiskowych lub jeśli zmienne nie są zdefiniowane, zainicjuj wystąpienie niekonfigurowane w celu obejścia wszystkich adresów.

Inicjowanie właściwości DefaultProxy na platformach opartych na systemach Windows i Unix używa następujących zmiennych środowiskowych:

  • HTTP_PROXY: serwer proxy używany na żądaniach HTTP.
  • HTTPS_PROXY: serwer proxy używany na żądaniach HTTPS.
  • ALL_PROXY: serwer proxy używany w żądaniach HTTP i/lub HTTPS, gdy zmienne HTTP_PROXY i/lub HTTPS_PROXY nie są zdefiniowane.
  • NO_PROXY: Rozdzielona przecinkami lista nazw hostów do wykluczenia z proxy'owania. Gwiazdki nie są obsługiwane w przypadku symboli wieloznacznych. Użyj kropki wiodącej (.), jeśli chcesz dopasować poddomenę. Przykłady: NO_PROXY=.example.com (z wiodącą kropką) pasuje do www.example.com, ale nie pasuje do example.com. NO_PROXY=example.com (bez wiodącej kropki) nie odpowiada www.example.com. To zachowanie może zostać ponownie przeanalizowane w przyszłości, aby lepiej dopasować się do innych ekosystemów.

W systemach, w których wielkość liter ma znaczenie, nazwy zmiennych mogą być małymi literami lub wielkimi literami. Najpierw sprawdzane są nazwy zapisane małymi literami.

Serwer proxy może być nazwą hosta lub adresem IP, ewentualnie z dwukropkiem i numerem portu, lub może to być adres URL http z możliwością uwzględnienia nazwy użytkownika i hasła na potrzeby uwierzytelniania serwera proxy. Adres URL musi zaczynać się od http, a nie httpsi nie może zawierać żadnego tekstu po nazwie hosta, adresie IP lub porcie.

Konfigurowanie serwera proxy na klienta

Właściwość HttpClientHandler.Proxy identyfikuje obiekt WebProxy używany do przetwarzania żądań do zasobów internetowych. Aby określić, że nie należy używać żadnego proxy, ustaw właściwość Proxy na instancję proxy zwróconą przez metodę GlobalProxySelection.GetEmptyWebProxy().

Plik konfiguracji komputera lokalnego lub aplikacji może określać, że jest używany domyślny serwer proxy. Jeśli określono właściwość Proxy, ustawienia serwera proxy z właściwości Proxy zastępują plik konfiguracji komputera lokalnego lub aplikacji, a program obsługi używa określonych ustawień serwera proxy. Jeśli w pliku konfiguracji nie określono żadnego serwera proxy, a właściwość Proxy jest nieokreślona, program obsługi używa ustawień serwera proxy dziedziczonych z komputera lokalnego. Jeśli nie ma żadnych ustawień serwera proxy, żądanie jest wysyłane bezpośrednio do serwera.

Klasa HttpClientHandler analizuje listę obejścia serwera proxy z symbolami wieloznacznymi, które są dziedziczone z ustawień komputera lokalnego. Na przykład klasa HttpClientHandler analizuje listę pomijania "nt*" z przeglądarek jako wyrażenie regularne "nt.*". W związku z tym adres URL http://nt.com pomija serwer proxy przy użyciu klasy HttpClientHandler.

Klasa HttpClientHandler obsługuje obejście lokalnego serwera proxy. Klasa uważa miejsce docelowe za lokalizację lokalną, jeśli zostaną spełnione jakiekolwiek z następujących warunków:

  • Miejsce docelowe zawiera płaską nazwę (bez kropek (.) w adresie URL.
  • Miejsce docelowe zawiera adres zwrotny (Loopback lub IPv6Loopback) lub właściwość IPAddress przypisaną do komputera lokalnego.
  • Sufiks domeny miejsca docelowego jest zgodny z sufiksem domeny komputera lokalnego, zgodnie z definicją we właściwości DomainName.

Aby uzyskać więcej informacji na temat konfigurowania serwera proxy, zobacz następujące interfejsy API:

Następne kroki