Создание 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
, которые выполняют автоматическую сериализацию и десериализацию с помощью пакета NuGet 📦 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
экземпляр использует базовый адрес для последующих запросов. Чтобы применить другие конфигурации, рассмотрите следующие API:
- Задайте свойство HttpClient.DefaultRequestHeaders.
- Примените свойство, отличное от HttpClient.Timeout.
- Укажите свойство HttpClient.DefaultRequestVersion.
Совет
Кроме того, можно создать экземпляры 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 |
†запрос
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>
Дополнительные сведения см. в разделе Включение текстового сетевого трафика для домена localhost.
Общие сведения о содержимом HTTP
Тип HttpContent используется для представления текста сущности HTTP и соответствующих заголовков содержимого. Для методов HTTP (или методов запроса), для которых требуется текст (POST
, PUT
, PATCH
), используется класс HttpContent для указания текста запроса. В большинстве примеров показано, как подготовить StringContent подкласс с JSON-данными, но другие подклассы существуют для различных типов содержимого (MIME).
- ByteArrayContent: предоставляет содержимое HTTP на основе массива байтов.
-
FormUrlEncodedContent: предоставляет http-содержимое для кортежей имен и значений, закодированных с помощью типа MIME
"application/x-www-form-urlencoded"
. - JsonContent: предоставляет содержимое HTTP на основе JSON.
-
MultipartContent: предоставляет коллекцию объектов HttpContent, сериализованных с помощью спецификации типа MIME
"multipart/*"
. -
MultipartFormDataContent: предоставляет контейнер для содержимого, закодированного с помощью типа MIME
"multipart/form-data"
. - ReadOnlyMemoryContent: обеспечивает HTTP-содержимое на основе значения ReadOnlyMemory<T>.
- StreamContent: предоставляет HTTP-содержимое, используя поток.
- StringContent: предоставляет содержимое HTTP на основе строки.
Класс HttpContent
также используется для представления текста отклика класса HttpResponseMessage, который доступен в свойстве HttpResponseMessage.Content.
Использование HTTP-запроса GET
Запрос GET
не должен отправлять текст. Этот запрос используется (как указывает имя метода) для получения (или получения) данных из ресурса. Чтобы выполнить запрос HTTP GET
с использованием экземпляра HttpClient
и объекта 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 из 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
, Title
Completed
и 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 запроса (тип MIME
"application/json"
). - Выполните запрос
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, используйте метод 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 запроса (тип MIME
"application/json"
). - Выполните запрос
PUT
к конечной точке"https://jsonplaceholder.typicode.com/todos/1"
. - Убедитесь, что ответ выполнен успешно, и запишите детали запроса вместе с телом ответа в формате JSON в консоль.
Создание HTTP-запроса PUT в формате 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
Запрос PATCH
является частичным обновлением существующего ресурса. Этот запрос не создает новый ресурс и не предназначен для замены существующего ресурса. Вместо этого этот метод частично обновляет ресурс. Чтобы выполнить запрос HTTP PATCH
с использованием экземпляра HttpClient
и объекта 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 запроса (тип MIME
"application/json"
). - Выполните запрос
PATCH
к конечной точке"https://jsonplaceholder.typicode.com/todos/1"
. - Убедитесь, что ответ успешен, и записывайте детали запроса с текстом ответа JSON в консоль.
Методы расширения не существуют для PATCH
запросов в пакете System.Net.Http.Json
NuGet.
Использование http DELETE-запроса
Запрос DELETE
удаляет существующий ресурс, и запрос идемпотентен, но не безопасен. Несколько DELETE
запросов к тем же ресурсам дают один и тот же результат, но запрос влияет на состояние ресурса. Чтобы выполнить запрос HTTP DELETE
с использованием экземпляра HttpClient
и объекта 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
Запрос 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
Запрос 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
}
Код выполняет следующие задачи:
- Отправьте HTTP-запрос
OPTIONS
в конечную точку"https://www.example.com/"
. - Убедитесь, что ответ успешно выполнен, и запишите сведения о запросе в консоль.
- Выполняет итерацию по всем заголовкам содержимого ответа и записывает каждый заголовок в консоль.
Изучение HTTP-запроса TRACE
Запрос TRACE
может быть полезен для отладки, так как он предоставляет обратную связь на уровне приложения для сообщения запроса. Чтобы сделать запрос HTTP TRACE
, создайте HttpRequestMessage с помощью типа HttpMethod.Trace
:
using HttpRequestMessage request = new(
HttpMethod.Trace,
"{ValidRequestUri}");
Внимание
Не все HTTP-серверы поддерживают метод TRACE
HTTP. Этот метод может предоставить уязвимость безопасности, если она используется неразумно. Дополнительные сведения см. в разделе Open Web Application Security Project (OWASP): Cross Site Tracing.
Обработка 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, который можно использовать для доступа к тексту в виде потока, массива байтов или строки.
Следующий код использует объект responseStream
для чтения текста ответа:
await using Stream responseStream =
await response.Content.ReadAsStreamAsync();
Для чтения текста ответа можно использовать различные объекты. Используйте объект responseByteArray
для чтения текста ответа:
byte[] responseByteArray = await response.Content.ReadAsByteArrayAsync();
Используйте объект responseString
для чтения текста ответа:
string responseString = await response.Content.ReadAsStringAsync();
Если вы знаете, что конечная точка HTTP возвращает JSON, вы можете десериализировать текст ответа в любой допустимый объект C# с помощью пакета NuGet System.Net.Http.Json:
T? result = await response.Content.ReadFromJsonAsync<T>();
В этом коде значение result
является десериализированным телом ответа в виде типа T
.
Использование обработки ошибок HTTP
При сбое HTTP-запроса система создает объект HttpRequestException. Перехват исключения может оказаться недостаточным. Существуют и другие потенциальные исключения, которые вы, возможно, захотите рассмотреть для обработки. Например, вызывающий код может использовать маркер отмены, который был отменен до завершения запроса. В этом сценарии можно зафиксировать ошибку TaskCanceledException.
using var cts = new CancellationTokenSource();
try
{
// Assuming:
// httpClient.Timeout = TimeSpan.FromSeconds(10)
using var response = await httpClient.GetAsync(
"http://localhost:5001/sleepFor?seconds=100", cts.Token);
}
catch (OperationCanceledException ex) when (cts.IsCancellationRequested)
{
// When the token has been canceled, it is not a timeout.
Console.WriteLine($"Canceled: {ex.Message}");
}
Аналогичным образом, при выполнении HTTP-запроса, если сервер не отвечает до превышения значения HttpClient.Timeout, возникает то же исключение. В этом сценарии можно определить, что произошёл тайм-аут, проанализировав свойство 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 оценивается, чтобы определить, был ли ответ кодом состояния HTTP 404 (404
). В объекте HttpClient
есть несколько вспомогательных методов, которые неявно вызывают метод EnsureSuccessStatusCode
от вашего имени.
Для передачи ошибок HTTP рассмотрим следующие API:
- метод HttpClient.GetByteArrayAsync
- метод HttpClient.GetStreamAsync
- метод HttpClient.GetStringAsync
Совет
Все методы 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
переопределяют локальный компьютер или файл конфигурации приложения, а обработчик использует указанные параметры прокси-сервера. Если прокси-сервер не указан в файле конфигурации, а свойство Proxy
не указано, обработчик использует параметры прокси-сервера, унаследованные от локального компьютера. Если параметры прокси-сервера отсутствуют, запрос отправляется непосредственно серверу.
Класс HttpClientHandler анализирует список обхода прокси-сервера с подстановочными знаками, унаследованными от параметров локального компьютера. Например, HttpClientHandler
класс анализирует список обходов "nt*"
из браузеров как регулярное выражение "nt.*"
. Поэтому URL-адрес http://nt.com
обходит прокси-сервер, используя класс HttpClientHandler
.
Класс HttpClientHandler
поддерживает обход локального прокси-сервера. Класс считает назначение локальным, если выполняются какие-либо из следующих условий:
- Назначение содержит плоское имя (без точек в URL-адресе).
- Назначение содержит адрес обратного цикла (Loopback или IPv6Loopback) или назначение содержит свойство IPAddress, назначенное локальному компьютеру.
- Суффикс домена назначения соответствует суффиксу домена локального компьютера, как определено в свойстве DomainName.
Дополнительные сведения о настройке прокси-сервера см. в следующих API:
- свойство WebProxy.Address
- свойство WebProxy.BypassProxyOnLocal
- свойство WebProxy.BypassArrayList