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

W tym artykule dowiesz się, jak wysyłać żądania HTTP i obsługiwać odpowiedzi za pomocą HttpClient klasy .

Ważne

Wszystkie przykładowe żądania HTTP 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 udostępnia kilka metod HttpClient rozszerzeń i HttpContent które wykonują automatyczną serializacji i deserializacji przy użyciu polecenia System.Text.Json. Przykłady, które zwracają uwagę na miejsca, w których są dostępne te rozszerzenia.

Napiwek

Cały kod źródłowy z tego artykułu jest dostępny w repozytorium GitHub: .NET Docs .

Tworzenie elementu HttpClient

Większość poniższych przykładów ponownie używa tego samego HttpClient wystąpienia i dlatego należy skonfigurować tylko raz. Aby utworzyć obiekt HttpClient, użyj konstruktora HttpClient klasy. 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"),
};

Powyższy kod ma następujące działanie:

  • Tworzy wystąpienie nowego HttpClient wystąpienia jako zmiennej static . Zgodnie z wytycznymi zaleca się ponowne użycie HttpClient wystąpień podczas cyklu życia aplikacji.
  • Ustawia wartość HttpClient.BaseAddress na "https://jsonplaceholder.typicode.com".

To HttpClient wystąpienie używa adresu podstawowego podczas wykonywania kolejnych żądań. Aby zastosować inną konfigurację, rozważ następujące kwestie:

Napiwek

Alternatywnie można tworzyć HttpClient wystąpienia przy użyciu podejścia opartego na wzorcu fabryki, które umożliwia skonfigurowanie dowolnej liczby klientów i użycie ich jako usług 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ć dowolny z następujących interfejsów API:

Metoda HTTP interfejs 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

†A USER SPECIFIED żądanie wskazuje, że SendAsync metoda akceptuje wszystkie prawidłowe HttpMethod.

Ostrzeżenie

Wykonywanie żądań HTTP jest uznawane za pracę związaną z we/wy sieci. Chociaż istnieje metoda synchroniczna HttpClient.Send , zaleca się użycie asynchronicznych interfejsów API, chyba że nie masz dobrego powodu.

Zawartość 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, PUTi PATCH, należy użyć HttpContent klasy , 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 krotki nazwy/wartości zakodowane przy użyciu "application/x-www-form-urlencoded" typu MIME.
  • JsonContent: udostępnia zawartość HTTP opartą na formacie JSON.
  • MultipartContent: udostępnia kolekcję obiektów HttpContent, które są serializowane przy użyciu "multipart/*" specyfikacji typu MIME.
  • MultipartFormDataContent: udostępnia kontener dla zawartości zakodowanej przy użyciu "multipart/form-data" typu MIME.
  • ReadOnlyMemoryContent: udostępnia zawartość HTTP na podstawie elementu ReadOnlyMemory<T>.
  • StreamContent: udostępnia zawartość HTTP na podstawie strumienia.
  • StringContent: udostępnia zawartość HTTP na podstawie ciągu.

Klasa HttpContent jest również używana do reprezentowania treści HttpResponseMessageodpowiedzi obiektu , dostępnego we HttpResponseMessage.Content właściwości .

HTTP Get

Żądanie GET nie powinno wysyłać treści i jest używane (jak wskazuje nazwa metody) do pobierania (lub pobierania) danych z zasobu. Aby utworzyć żądanie HTTP GET , biorąc pod uwagę HttpClient identyfikator URI i, użyj HttpClient.GetAsync metody :

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

Powyższy kod ma następujące działanie:

  • GET Wysyła żądanie do "https://jsonplaceholder.typicode.com/todos/3".
  • Gwarantuje, że odpowiedź zakończy się pomyślnie.
  • Zapisuje szczegóły żądania w konsoli programu .
  • Odczytuje treść odpowiedzi jako ciąg.
  • Zapisuje treść odpowiedzi JSON w konsoli.

Jest WriteRequestToConsole to niestandardowa metoda rozszerzenia, która nie jest częścią struktury, ale jeśli interesuje Cię sposób implementacji, 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 GET żądanie do https://jsonplaceholder.typicode.com/todos/3 wyprowadzenia następującego komunikatu:

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

HTTP Get from JSON

Punkt https://jsonplaceholder.typicode.com/todos końcowy zwraca tablicę JSON obiektów "todo". Ich struktura JSON przypomina następujące elementy:

[
  {
    "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 record class typ z opcjonalnymi Idwłaściwościami , Title, Completedi UserId . Aby uzyskać więcej informacji na record temat typu, zobacz Wprowadzenie do typów rekordów w języku C#. Aby automatycznie deserializować GET żądania do silnie typizowanego obiektu języka C#, użyj GetFromJsonAsync metody rozszerzenia 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 }
}

Powyższy kod:

  • Żądanie GET jest wykonywane do "https://jsonplaceholder.typicode.com/todos?userId=1&completed=false".
    • Ciąg zapytania reprezentuje kryteria filtrowania dla żądania.
  • Odpowiedź jest automatycznie deserializowana w przypadku pomyślnego List<Todo> działania.
  • Szczegóły żądania są zapisywane w konsoli wraz z każdym Todo obiektem.

Wpis HTTP

Żądanie POST wysyła dane do serwera na potrzeby przetwarzania. Nagłówek Content-Type żądania oznacza typ MIME, który wysyła treść. Aby wysłać żądanie HTTP POST , biorąc pod uwagę element HttpClient i Uri, użyj HttpClient.PostAsync metody :

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

Powyższy kod ma następujące działanie:

  • StringContent Przygotowuje wystąpienie z treścią JSON żądania (typ MIME )."application/json"
  • POST Wysyła żądanie do "https://jsonplaceholder.typicode.com/todos".
  • Gwarantuje, że odpowiedź zakończy się pomyślnie i zapisze szczegóły żądania w konsoli.
  • Zapisuje treść odpowiedzi jako ciąg w konsoli.

Wpis HTTP jako JSON

Aby automatycznie serializować POST argumenty żądań i deserializować odpowiedzi na silnie typizowane obiekty języka C#, użyj PostAsJsonAsync metody rozszerzenia będącej 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 }
}

Powyższy kod ma następujące działanie:

  • Serializuje Todo wystąpienie w formacie JSON i wysyła POST żądanie do "https://jsonplaceholder.typicode.com/todos".
  • Gwarantuje, że odpowiedź zakończy się pomyślnie i zapisze szczegóły żądania w konsoli.
  • Deserializuje treść odpowiedzi w wystąpieniu Todo i zapisuje element Todo w konsoli programu .

Umieszczanie http

Metoda PUT żądania zastępuje istniejący zasób lub tworzy nowy za pomocą ładunku treści żądania. Aby utworzyć żądanie HTTP PUT , biorąc pod uwagę HttpClient identyfikator URI i, użyj HttpClient.PutAsync metody :

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

Powyższy kod ma następujące działanie:

  • StringContent Przygotowuje wystąpienie z treścią JSON żądania (typ MIME )."application/json"
  • PUT Wysyła żądanie do "https://jsonplaceholder.typicode.com/todos/1".
  • Gwarantuje, że odpowiedź zakończy się pomyślnie, a szczegóły żądania i treść odpowiedzi JSON zostaną zapisane w konsoli.

HTTP Put as JSON

Aby automatycznie serializować PUT argumenty żądań i deserializować odpowiedzi na silnie typizowane obiekty języka C#, użyj metody rozszerzenia będącej PutAsJsonAsync 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 }
}

Powyższy kod ma następujące działanie:

  • Serializuje Todo wystąpienie w formacie JSON i wysyła PUT żądanie do "https://jsonplaceholder.typicode.com/todos/5".
  • Gwarantuje, że odpowiedź zakończy się pomyślnie i zapisze szczegóły żądania w konsoli.
  • Deserializuje treść odpowiedzi w wystąpieniu Todo i zapisuje element Todo w konsoli programu .

Poprawka HTTP

Żądanie PATCH jest częściową aktualizacją istniejącego zasobu. Nie tworzy nowego zasobu i nie ma na celu zastąpienia istniejącego zasobu. Zamiast tego aktualizuje zasób tylko częściowo. Aby utworzyć żądanie HTTP PATCH , biorąc pod uwagę HttpClient identyfikator URI i, użyj HttpClient.PatchAsync metody :

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

Powyższy kod ma następujące działanie:

  • StringContent Przygotowuje wystąpienie z treścią JSON żądania (typ MIME )."application/json"
  • PATCH Wysyła żądanie do "https://jsonplaceholder.typicode.com/todos/1".
  • Gwarantuje, że odpowiedź zakończy się pomyślnie, a szczegóły żądania i treść odpowiedzi JSON zostaną zapisane w konsoli.

W pakiecie System.Net.Http.Json NuGet nie istnieją PATCH żadne metody rozszerzenia.

Usuwanie HTTP

Żądanie DELETE usuwa istniejący zasób. Żądanie DELETE jest idempotentne , ale nie bezpieczne, co oznacza, że wiele DELETE żądań do tych samych zasobów daje ten sam wynik, ale żądanie wpływa na stan zasobu. Aby utworzyć żądanie HTTP DELETE , biorąc pod uwagę HttpClient identyfikator URI i, użyj HttpClient.DeleteAsync metody :

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

Powyższy kod ma następujące działanie:

  • DELETE Wysyła żądanie do "https://jsonplaceholder.typicode.com/todos/1".
  • Gwarantuje, że odpowiedź zakończy się pomyślnie i zapisze szczegóły żądania w konsoli.

Napiwek

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

Nagłówek HTTP

Żądanie HEAD jest podobne do GET żądania. Zamiast zwracać zasób, zwraca on tylko nagłówki skojarzone z zasobem. Odpowiedź na HEAD żądanie nie zwraca treści. Aby wysłać żądanie HTTP HEAD , biorąc pod uwagę HttpClient identyfikator URI i, użyj HttpClient.SendAsync metody z ustawionym HttpMethod 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
}

Powyższy kod ma następujące działanie:

  • HEAD Wysyła żądanie do "https://www.example.com/".
  • Gwarantuje, że odpowiedź zakończy się pomyślnie i zapisze szczegóły żądania w konsoli.
  • Iteruje wszystkie nagłówki odpowiedzi, zapisuj je w konsoli.

Opcje protokołu HTTP

Żądanie służy do identyfikowania OPTIONS metod HTTP obsługiwanych przez serwer lub punkt końcowy. Aby wysłać żądanie HTTP OPTIONS , biorąc pod uwagę HttpClient identyfikator URI i, użyj HttpClient.SendAsync metody z ustawionym HttpMethod 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
}

Powyższy kod ma następujące działanie:

  • OPTIONS Wysyła żądanie HTTP do "https://www.example.com/".
  • Gwarantuje, że odpowiedź zakończy się pomyślnie i zapisze szczegóły żądania w konsoli.
  • Iteruje wszystkie nagłówki zawartości odpowiedzi, zapisuj je w konsoli.

Śledzenie HTTP

Żądanie TRACE może być przydatne do debugowania, ponieważ zapewnia pętlę na poziomie aplikacji z powrotem komunikatu żądania. Aby utworzyć żądanie HTTP TRACE , utwórz obiekt HttpRequestMessage przy użyciu polecenia HttpMethod.Trace:

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

Uwaga

Metoda HTTP nie jest obsługiwana TRACE przez wszystkie serwery HTTP. Może ona 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

Za każdym razem, gdy obsługujesz odpowiedź HTTP, wchodzisz w interakcję z typem HttpResponseMessage . Podczas oceniania ważności odpowiedzi jest używanych kilka elementów członkowskich. Kod stanu HTTP jest dostępny za pośrednictwem HttpResponseMessage.StatusCode właściwości . Załóżmy, że wysłano żądanie dla danego wystąpienia klienta:

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

Aby upewnić się, że element response to OK (kod stanu HTTP 200), możesz go ocenić, 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 musisz zgłosić strukturę 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.

Prawidłowe odpowiedzi dotyczące zawartości HTTP

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

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

W poprzednim kodzie responseStream można go użyć do odczytania treści odpowiedzi.

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

W poprzednim kodzie responseByteArray można go użyć do odczytania treści odpowiedzi.

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

W poprzednim kodzie responseString można go użyć do odczytania treści odpowiedzi.

Na koniec, gdy wiesz, że punkt końcowy HTTP zwraca kod JSON, można deserializować treść odpowiedzi do dowolnego prawidłowego obiektu języka C# przy użyciu pakietu NuGet System.Net.Http.Json :

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

W poprzednim kodzie result jest treść odpowiedzi deserializowana jako typ T.

Obsługa błędów HTTP

Gdy żądanie HTTP zakończy się niepowodzeniem, HttpRequestException zostanie zgłoszony. Przechwycenie tego wyjątku może nie być wystarczające, ponieważ istnieją inne potencjalne wyjątki, które mogą być zgłaszane, że warto rozważyć obsługę. Na przykład kod wywołujący mógł używać tokenu anulowania, który został anulowany przed ukończeniem żądania. W tym scenariuszu przechwycisz element 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 podczas tworzenia żądania HTTP, jeśli serwer nie odpowiada przed HttpClient.Timeout przekroczeniem tego samego wyjątku, zostanie zgłoszony ten sam wyjątek. Jednak w tym scenariuszu można odróżnić limit czasu, oceniając Exception.InnerException wartość podczas przechwytywania elementu 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 poprzednim kodzie, gdy wyjątek wewnętrzny jest przekroczeniem TimeoutException limitu czasu, a żądanie nie zostało anulowane przez token anulowania.

Aby ocenić kod stanu HTTP podczas przechwytywania HttpRequestExceptionelementu , możesz ocenić HttpRequestException.StatusCode właściwość :

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 poprzednim kodzie wywoływana EnsureSuccessStatusCode() jest metoda , aby zgłosić wyjątek, jeśli odpowiedź nie powiedzie się. Następnie HttpRequestException.StatusCode właściwość jest oceniana w celu określenia, czy odpowiedź była ( 404 kod stanu HTTP 404). Istnieje kilka metod HttpClient pomocnika, które niejawnie wywołuje EnsureSuccessStatusCode w Twoim imieniu, rozważ następujące interfejsy API:

Napiwek

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

Podczas wywoływania tych metod można obsłużyć HttpRequestException i ocenić HttpRequestException.StatusCode właściwość , aby określić kod 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 trzeba zgłosić element 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}");
}

Serwer 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 we HttpClientHandler.Proxy właściwości .

Globalny domyślny serwer proxy

Jest HttpClient.DefaultProxy to właściwość statyczna, która określa domyślny serwer proxy używany przez wszystkie HttpClient wystąpienia, jeśli żaden serwer proxy nie jest jawnie ustawiony w HttpClientHandler przekazanym konstruktorze.

Wystąpienie domyślne zwracane przez tę właściwość inicjuje inny zestaw reguł w zależności od platformy:

  • W przypadku systemu Windows: odczytuje konfigurację serwera proxy ze zmiennych środowiskowych lub, jeśli nie są one zdefiniowane, z ustawień serwera proxy użytkownika.
  • W przypadku systemu macOS: odczytuje konfigurację serwera proxy ze zmiennych środowiskowych lub, jeśli nie są one zdefiniowane, z ustawień serwera proxy systemu.
  • W przypadku systemu Linux: odczytuje konfigurację serwera proxy ze zmiennych środowiskowych lub, jeśli te nie są zdefiniowane, ta właściwość inicjuje nieskonfigurowane wystąpienie, które pomija wszystkie adresy.

Zmienne środowiskowe używane do DefaultProxy inicjowania na platformach opartych na systemach Windows i Unix to:

  • HTTP_PROXY: serwer proxy używany na żądaniach HTTP.
  • HTTPS_PROXY: serwer proxy używany w żądaniach HTTPS.
  • ALL_PROXY: serwer proxy używany na żądaniach HTTP i/lub HTTPS w przypadku, gdy HTTP_PROXY i/lub HTTPS_PROXY nie są zdefiniowane.
  • NO_PROXY: rozdzielona przecinkami lista nazw hostów, które powinny być wykluczone z serwera proxy. Gwiazdki nie są obsługiwane w przypadku symboli wieloznacznych; użyj wiodącej kropki w przypadku dopasowania poddomeny. Przykłady: NO_PROXY=.example.com (z kropką wiodącą) będzie zgodna www.example.comz wartością , ale nie będzie zgodna example.comz parametrem . NO_PROXY=example.com (bez wiodącej kropki) nie będzie pasuje www.example.comdo . To zachowanie może zostać ponownie zwrócone w przyszłości, aby lepiej dopasować inne ekosystemy.

W systemach, w których zmienne środowiskowe są uwzględniane wielkości liter, nazwy zmiennych mogą być małymi literami lub wielką literą. Najpierw sprawdzane są małe litery.

Serwer proxy może być nazwą hosta lub adresem IP, opcjonalnie, po którym następuje dwukropek i numer portu, lub może to być http adres URL, opcjonalnie, w tym nazwa użytkownika i hasło na potrzeby uwierzytelniania serwera proxy. Adres URL musi zaczynać się od http, a nie https, i nie może zawierać żadnego tekstu po nazwie hosta, adresie IP lub porcie.

Serwer proxy na klienta

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

Plik konfiguracji komputera lokalnego lub aplikacji może określać, że jest używany domyślny serwer proxy. Jeśli właściwość serwera proxy jest określona, ustawienia serwera proxy z właściwości serwera 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ść serwera proxy nie jest okreś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 dziedziczone z ustawień komputera lokalnego. Na przykład HttpClientHandler klasa analizuje listę pomijania "nt*" z przeglądarek jako wyrażenie regularne ."nt.*" Dlatego adres URL http://nt.com polecenia pomija serwer proxy przy użyciu HttpClientHandler klasy .

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:

  1. Miejsce docelowe zawiera płaską nazwę (bez kropek w adresie URL).
  2. Miejsce docelowe zawiera adres sprzężenia zwrotnego (Loopback lub ) lub IPv6Loopbackmiejsce docelowe zawiera przypisaną IPAddress do komputera lokalnego.
  3. Sufiks domeny miejsca docelowego jest zgodny z sufiksem domeny komputera lokalnego (DomainName).

Aby uzyskać więcej informacji na temat konfigurowania serwera proxy, zobacz:

Zobacz też