Поделиться через


Создание HTTP-запросов с помощью класса HttpClient

В этой статье вы узнаете, как выполнять HTTP-запросы и обрабатывать ответы с HttpClient помощью класса.

Внимание

Все примеры HTTP-запросов предназначены для одного из следующих URL-адресов:

  • https://jsonplaceholder.typicode.com: бесплатный поддельный API для тестирования и прототипирования.
  • https://www.example.com: этот домен предназначен для использования в иллюстрирующих примерах в документах.

Конечные точки HTTP обычно возвращают данные нотации объектов JavaScript (JSON), но не всегда. Для удобства необязательный пакет NuGet System.Net.Http.Json предоставляет несколько методов HttpClient расширения и HttpContent выполняет автоматическую сериализацию и десериализацию с помощьюSystem.Text.Json. Примеры, которые следуют за вниманием, относятся к местам, где доступны эти расширения.

Совет

Весь исходный код из этой статьи доступен в репозитории GitHub: Документация .NET.

Создайте 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 экземпляры с помощью подхода к шаблону фабрики, который позволяет настроить любое количество клиентов и использовать их в качестве служб внедрения зависимостей. Дополнительные сведения см. в разделе "Фабрика клиентов HTTP" с помощью .NET.

Создание 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

†A-запрос USER SPECIFIED указывает, что SendAsync метод принимает любой допустимыйHttpMethod.

Предупреждение

Выполнение HTTP-запросов считается работой с привязкой к сети ввода-вывода. Хотя существует синхронный метод, рекомендуется использовать асинхронные HttpClient.Send API вместо этого, если у вас нет хорошей причины.

Примечание.

Нацелив устройства Android (например, на разработку .NET MAUI), необходимо добавить android:usesCleartextTraffic="true" <application></application> в AndroidManifest.xml. Это позволяет использовать чистый текстовый трафик, например 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>

Дополнительные сведения см. в разделе Enable clear-text network traffic for the localhost domain.

Содержимое HTTP

Тип HttpContent используется для представления текста сущности HTTP и соответствующих заголовков содержимого. Для методов HTTP (или методов запроса), для которых требуется текст, POSTPUTи PATCH, используйте HttpContent класс для указания текста запроса. В большинстве примеров показано, как подготовить StringContent подкласс с полезными данными JSON, но другие подклассы существуют для различных типов содержимого (MIME).

  • ByteArrayContent: предоставляет содержимое HTTP на основе массива байтов.
  • FormUrlEncodedContent: предоставляет http-содержимое для кортежей имен и значений, закодированных с помощью "application/x-www-form-urlencoded" типа MIME.
  • JsonContent: предоставляет содержимое HTTP на основе JSON.
  • MultipartContent: предоставляет коллекцию объектов HttpContent, сериализованных с помощью "multipart/*" спецификации типа MIME.
  • MultipartFormDataContent: предоставляет контейнер для содержимого, закодированного с помощью "multipart/form-data" типа MIME.
  • ReadOnlyMemoryContent: предоставляет содержимое HTTP на ReadOnlyMemory<T>основе .
  • StreamContent: предоставляет содержимое HTTP на основе потока.
  • StringContent: предоставляет содержимое HTTP на основе строки.

Класс HttpContent также используется для представления текста отклика HttpResponseMessageсвойства, доступного для HttpResponseMessage.Content свойства.

HTTP Get

Запрос GET не должен отправлять текст и используется (как указывает имя метода) для получения (или получения) данных из ресурса. Чтобы выполнить HTTP-запрос GET с учетом HttpClient URI и URI, используйте 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
    //   }
}

Предыдущий код:

  • GET Выполняет запрос"https://jsonplaceholder.typicode.com/todos/3".
  • Гарантирует успешность ответа.
  • Записывает сведения о запросе в консоль.
  • Считывает текст ответа в виде строки.
  • Записывает текст ответа 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>

Например, GET запрос на https://jsonplaceholder.typicode.com/todos/3 вывод следующего сообщения:

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

HTTP Get from JSON

Конечная https://jsonplaceholder.typicode.com/todos точка возвращает массив JSON объектов todo. Их структура 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, CompletedTitleи UserId свойствами. Дополнительные сведения о типе record см. в разделе "Общие сведения о типах записей в C#". Чтобы автоматически десериализировать запросы в строго типизированный GET объект C#, используйте GetFromJsonAsync метод расширения, который является частью пакета 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 }
}

В предыдущем коде:

  • Запрос GET выполняется "https://jsonplaceholder.typicode.com/todos?userId=1&completed=false".
    • Строка запроса представляет критерии фильтрации для запроса.
  • Ответ автоматически десериализируется в случае успешного List<Todo> выполнения.
  • Сведения о запросе записываются в консоль вместе с каждым Todo объектом.

HTTP Post

Запрос POST отправляет данные на сервер для обработки. Заголовок Content-Type запроса означает, какой тип MIME отправляет текст. Чтобы выполнить HTTP-запрос POST , учитывая HttpClient и а Uri, используйте 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
    //   }
}

Предыдущий код:

  • Подготавливает StringContent экземпляр с текстом JSON запроса (тип "application/json"MIME).
  • POST Выполняет запрос"https://jsonplaceholder.typicode.com/todos".
  • Гарантирует успешность ответа и запись сведений о запросе в консоль.
  • Записывает текст ответа в виде строки в консоль.

HTTP Post в формате JSON

Для автоматической сериализации аргументов запроса и десериализации ответов в строго типизированные POST объекты C# используйте PostAsJsonAsync метод расширения, который входит в пакет 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 }
}

Предыдущий код:

  • Сериализует Todo экземпляр в формате JSON и отправляет POST запрос "https://jsonplaceholder.typicode.com/todos".
  • Гарантирует успешность ответа и запись сведений о запросе в консоль.
  • Десериализирует текст ответа в Todo экземпляр и записывает его в Todo консоль.

HTTP Put

PUT Метод запроса заменяет существующий ресурс или создает новый с помощью полезных данных текста запроса. Чтобы выполнить HTTP-запрос PUT с учетом HttpClient URI и URI, используйте 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
    //   }
}

Предыдущий код:

  • Подготавливает StringContent экземпляр с текстом JSON запроса (тип "application/json"MIME).
  • PUT Выполняет запрос"https://jsonplaceholder.typicode.com/todos/1".
  • Гарантирует успешность ответа и запись сведений о запросе и текст ответа JSON в консоль.

HTTP Put as JSON

Чтобы автоматически сериализовать аргументы запроса и десериализовать ответы в строго типизированные PUT объекты C#, используйте PutAsJsonAsync метод расширения, который входит в пакет 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 }
}

Предыдущий код:

  • Сериализует Todo экземпляр в формате JSON и отправляет PUT запрос "https://jsonplaceholder.typicode.com/todos/5".
  • Гарантирует успешность ответа и запись сведений о запросе в консоль.
  • Десериализирует текст ответа в Todo экземпляр и записывает его в Todo консоль.

Исправление HTTP

Запрос PATCH является частичным обновлением существующего ресурса. Он не создает новый ресурс, и он не предназначен для замены существующего ресурса. Вместо этого он обновляет ресурс только частично. Чтобы выполнить HTTP-запрос PATCH с учетом HttpClient URI и URI, используйте 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
    //   }
}

Предыдущий код:

  • Подготавливает StringContent экземпляр с текстом JSON запроса (тип "application/json"MIME).
  • PATCH Выполняет запрос"https://jsonplaceholder.typicode.com/todos/1".
  • Гарантирует успешность ответа и запись сведений о запросе и текст ответа JSON в консоль.

Методы расширения не существуют для PATCH запросов в пакете System.Net.Http.Json NuGet.

HTTP Delete

Запрос DELETE удаляет существующий ресурс. Запрос DELETE является идемпотентным, но небезопасным, то есть несколько DELETE запросов к тем же ресурсам дают один и тот же результат, но запрос влияет на состояние ресурса. Чтобы выполнить HTTP-запрос DELETE с учетом HttpClient URI и URI, используйте 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 похож на GET запрос. Вместо возврата ресурса он возвращает только заголовки, связанные с ресурсом. Ответ на HEAD запрос не возвращает текст. Чтобы выполнить HTTP-запрос HEAD , заданный HttpClient и универсальный код ресурса (URI), используйте 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
}

Предыдущий код:

  • HEAD Выполняет запрос"https://www.example.com/".
  • Гарантирует успешность ответа и запись сведений о запросе в консоль.
  • Выполняет итерацию по всем заголовкам ответа, записывая каждую из них в консоль.

Параметры HTTP

Запрос OPTIONS используется для определения методов HTTP, поддерживаемых сервером или конечной точкой. Чтобы выполнить HTTP-запрос OPTIONS , заданный HttpClient и универсальный код ресурса (URI), используйте 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 может быть полезен для отладки, так как он предоставляет цикл на уровне приложения для сообщения запроса. Чтобы выполнить HTTP-запрос TRACE , создайте с помощью следующего HttpRequestMessage HttpMethod.Traceэлемента:

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

Внимание

Метод TRACE HTTP не поддерживается всеми 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();

Этот код создает исключение, HttpRequestException если код состояния ответа не относится к диапазону 200-299.

Ответы на допустимое содержимое 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, можно десериализировать текст ответа в любой допустимый объект C# с помощью пакета NuGet System.Net.Http.Json :

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

В приведенном выше коде result текст ответа десериализирован как тип T.

Обработка ошибок HTTP

При сбое HttpRequestException HTTP-запроса вызывается. Перехват этого исключения может быть недостаточно, так как могут возникнуть другие потенциальные исключения, которые может потребоваться рассмотреть. Например, вызывающий код может использовать маркер отмены, который был отменен до завершения запроса. В этом сценарии вы поймаете следующее 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 превышения того же исключения. Однако в этом сценарии можно отличить время ожидания, оценивая Exception.InnerException время ожидания при перехвате 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}");
}

В приведенном выше коде, когда внутреннее исключение является TimeoutException временем ожидания, и запрос не был отменен маркером отмены.

Чтобы оценить код состояния HTTP при перехвате HttpRequestException, можно оценить 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:

Совет

Все HttpClient методы, используемые для выполнения HTTP-запросов, которые не возвращают неявный HttpResponseMessage вызов 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 можно настроить одним из двух способов. Значение по умолчанию указывается в свойстве HttpClient.DefaultProxy . Кроме того, можно указать прокси-сервер для HttpClientHandler.Proxy свойства.

Глобальный прокси-сервер по умолчанию

Это HttpClient.DefaultProxy статическое свойство, определяющее прокси-сервер по умолчанию, используемый всеми HttpClient экземплярами, если прокси-сервер не задан явным образом в переданном конструкторе HttpClientHandler .

Экземпляр по умолчанию, возвращаемый этим свойством, инициализирует следующий набор правил в зависимости от платформы:

  • Для Windows: считывает конфигурацию прокси-сервера из переменных среды или, если они не определены, из параметров прокси-сервера пользователя.
  • Для macOS: считывает конфигурацию прокси-сервера из переменных среды или, если они не определены, из параметров прокси-сервера системы.
  • Для Linux: считывает конфигурацию прокси-сервера из переменных среды или, если они не определены, это свойство инициализирует не настроенный экземпляр, который проходит все адреса.

Переменные среды, используемые для DefaultProxy инициализации на платформах под управлением Windows и Unix, являются следующими:

  • HTTP_PROXY: прокси-сервер, используемый в HTTP-запросах.
  • HTTPS_PROXY: прокси-сервер, используемый в HTTPS-запросах.
  • ALL_PROXY: прокси-сервер, используемый в HTTP-запросах и (или) HTTPS в случае HTTP_PROXY и /или HTTPS_PROXY не определен.
  • NO_PROXY: список имен узлов, разделенных запятыми, которые следует исключить из прокси-сервера. Звездочки не поддерживаются для подстановочных знаков; используйте ведущую точку в случае, если вы хотите сопоставить поддомен. Примеры: NO_PROXY=.example.com (с начальной точкой) будет соответствовать www.example.com, но не будет соответствовать example.com. NO_PROXY=example.com (без ведущих точек) не будет соответствовать www.example.com. Это поведение может быть пересмотрено в будущем, чтобы соответствовать другим экосистемам лучше.

В системах, где для переменных среды учитывается регистр, имена таких переменных могут быть полностью в нижнем или верхнем регистре. Сначала проверяются имена в нижнем регистре.

Прокси-сервер может быть именем узла или IP-адресом, а также двоеточием и номером порта или URL-адресом, http в том числе именем пользователя и паролем для проверки подлинности прокси-сервера. URL-адрес должен начинаться с http, а не httpsвключать текст после имени узла, IP-адреса или порта.

Прокси-сервер на клиент

Свойство HttpClientHandler.Proxy определяет объект, используемый WebProxy для обработки запросов к интернет-ресурсам. Чтобы указать, что прокси-сервер не должен использоваться, задайте Proxy для свойства экземпляр прокси, возвращаемый методом GlobalProxySelection.GetEmptyWebProxy() .

Локальный компьютер или файл конфигурации приложения может указать, что используется прокси-сервер по умолчанию. Если задано свойство Proxy, то параметры прокси из свойства Прокси переопределяют локальный компьютер или файл конфигурации приложения, а обработчик использует указанные параметры прокси. Если прокси-сервер не указан в файле конфигурации, а свойство Proxy не указано, обработчик использует параметры прокси-сервера, унаследованные от локального компьютера. Если параметры прокси-сервера отсутствуют, запрос отправляется непосредственно серверу.

Класс HttpClientHandler анализирует список обхода прокси-сервера с подстановочными знаками, унаследованными от параметров локального компьютера. Например, HttpClientHandler класс анализирует список обходов "nt*" из браузеров как регулярное выражение "nt.*". Поэтому URL-адрес http://nt.com обхода прокси-сервера с помощью HttpClientHandler класса.

Класс HttpClientHandler поддерживает обход локального прокси-сервера. Класс считает назначение локальным, если выполняются какие-либо из следующих условий:

  1. Назначение содержит неструктурированное имя (нет точек в URL-адресе).
  2. Назначение содержит адрес обратного цикла (Loopback или) или IPv6Loopbackназначение содержит назначенный IPAddress локальному компьютеру адрес.
  3. Суффикс домена назначения соответствует суффиксу домена локального компьютера (DomainName).

Дополнительные сведения о настройке прокси-сервера см. в следующем разделе:

См. также