Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
В этой статье вы узнаете, как выполнять 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, TitleCompletedи 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# используйте PostAsJsonAsyncReadFromJsonAsync методы расширения соответственно, которые являются частью пакета 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# используйте PutAsJsonAsyncReadFromJsonAsync методы расширения соответственно, которые являются частью пакета 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.
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 (ex.InnerException is TimeoutException tex)
{
// when the time-out occurred. Here the cancellation token has not been canceled.
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 methods will throw HttpRequestException
// with StatusCode set when the HTTP response 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