Slide 1

Slide 1 text

HttpClient основные ошибки и способы их избежать Риваль Абдрахманов Positive Technologies SpbDotNet, 2019 Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 1 / 90

Slide 2

Slide 2 text

Содержание 1 HttpClient - базовая информация 2 Неочевидные проблемы 3 Интерфейс IHttpClientFactory 4 Дополнительные улучшения в .NET Core 2.1 5 HttpRequestMessage и HttpResponseMessage 6 Новое в .NET Core 3.0 Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 2 / 90

Slide 3

Slide 3 text

HttpClient - базовая информация Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 3 / 90

Slide 4

Slide 4 text

HttpClient Class Базовый класс для отправки HTTP-запросов и получения HTTP-ответов; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 4 / 90

Slide 5

Slide 5 text

HttpClient Class Базовый класс для отправки HTTP-запросов и получения HTTP-ответов; GetAsync(. . .), PostAsync(. . .), SendAsync(. . .) и др.; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 4 / 90

Slide 6

Slide 6 text

HttpClient Class Базовый класс для отправки HTTP-запросов и получения HTTP-ответов; GetAsync(. . .), PostAsync(. . .), SendAsync(. . .) и др.; HttpClient реализует IDisposable. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 4 / 90

Slide 7

Slide 7 text

IDisposable Interface Предоставляет механизм для освобождения неуправляемых ресурсов; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 5 / 90

Slide 8

Slide 8 text

IDisposable Interface Предоставляет механизм для освобождения неуправляемых ресурсов; public void Dispose(); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 5 / 90

Slide 9

Slide 9 text

IDisposable Interface Предоставляет механизм для освобождения неуправляемых ресурсов; public void Dispose(); Конструкция using(. . .); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 5 / 90

Slide 10

Slide 10 text

IDisposable Interface Предоставляет механизм для освобождения неуправляемых ресурсов; public void Dispose(); Конструкция using(. . .); Диспозится → диспозь Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 5 / 90

Slide 11

Slide 11 text

IDisposable Interface Предоставляет механизм для освобождения неуправляемых ресурсов; public void Dispose(); Конструкция using(. . .); Диспозится → диспозь Диспозится → будь внимательней Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 6 / 90

Slide 12

Slide 12 text

Disposable HttpClient Мотивация: using(var client = new HttpClient ()) { var response = await client.GetStringAsync (...); } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 7 / 90

Slide 13

Slide 13 text

Неочевидные проблемы Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 8 / 90

Slide 14

Slide 14 text

Проблема socket exhaustion https://aspnetmonsters.com/2016/08/2016-08-27- httpclientwrong/ Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 9 / 90

Slide 15

Slide 15 text

Проблема socket exhaustion for(int i = 0; i < 10; i++) { using (var client = new HttpClient ()) { await client .GetStringAsync("https :// api.github.com"); } } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 10 / 90

Slide 16

Slide 16 text

Проблема socket exhaustion Проверяем через netstat: Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 11 / 90

Slide 17

Slide 17 text

TCP Finite State Machine Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 12 / 90

Slide 18

Slide 18 text

Проблема socket exhaustion 10 сокетов в состоянии TIME WAIT; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 13 / 90

Slide 19

Slide 19 text

Проблема socket exhaustion 10 сокетов в состоянии TIME WAIT; Приводит к SocketException; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 13 / 90

Slide 20

Slide 20 text

Проблема socket exhaustion 10 сокетов в состоянии TIME WAIT; Приводит к SocketException; Актуально не только для Windows и .NET Framework. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 13 / 90

Slide 21

Slide 21 text

Проблема socket exhaustion “HttpClient предназначен для однократного создания экземпляра и повторного использования в течение всего жизненного цикла приложения.” https://docs.microsoft.com/en- us/dotnet/api/system.net.http.httpclient Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 14 / 90

Slide 22

Slide 22 text

Проблема socket exhaustion Решение проблемы - переиспользование клиента: public static HttpClient Client = new HttpClient (); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 15 / 90

Slide 23

Slide 23 text

Проблема кеширования DNS https://byterot.blogspot.com/2016/07/singleton-httpclient- dns.html Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 16 / 90

Slide 24

Slide 24 text

DNS Server Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 17 / 90

Slide 25

Slide 25 text

Проблема кеширования DNS Не учитываются изменения DNS; Соединение не закрывается длительное время. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 18 / 90

Slide 26

Slide 26 text

Решение при балансировщике Решение для .NET Framework: Класс ServicePointManager; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 19 / 90

Slide 27

Slide 27 text

Решение при балансировщике Решение для .NET Framework: Класс ServicePointManager; ServicePointManager.DnsRefreshTimeout (2 мин); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 19 / 90

Slide 28

Slide 28 text

Решение при балансировщике Решение для .NET Framework: Класс ServicePointManager; ServicePointManager.DnsRefreshTimeout (2 мин); ServicePoint.ConnectionLeaseTimeout (не ограничено); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 19 / 90

Slide 29

Slide 29 text

Решение при балансировщике Решение для .NET Framework: Класс ServicePointManager; ServicePointManager.DnsRefreshTimeout (2 мин); ServicePoint.ConnectionLeaseTimeout (не ограничено); ServicePoint.MaxIdleTime (100 сек). Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 19 / 90

Slide 30

Slide 30 text

Проблема кеширования DNS Пример: ServicePointManager.DnsRefreshTimeout = 60000; var sp = ServicePointManager .FindServicePoint( new Uri("https :// api.github.com")); sp.ConnectionLeaseTimeout = 60000; sp.MaxIdleTime = 60000; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 20 / 90

Slide 31

Slide 31 text

Quiz var client = new HttpClient (); var tasks = new List (); for (var i = 0; i < 20; i++) { tasks.Add(client .GetStringAsync("https :// api.github.com")); } await Task.WhenAll(tasks); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 21 / 90

Slide 32

Slide 32 text

Quiz Сколько одновременных соединений будет установлено? 1 20 2 Зависит Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 22 / 90

Slide 33

Slide 33 text

Quiz Сколько одновременных соединений будет установлено? 2 Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 22 / 90

Slide 34

Slide 34 text

Лимит одновременных соединений https://habr.com/ru/post/424873/ Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 23 / 90

Slide 35

Slide 35 text

Лимит одновременных соединений Лимит по умолчанию равен 2; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 24 / 90

Slide 36

Slide 36 text

Лимит одновременных соединений Лимит по умолчанию равен 2; ServicePointManager.DefaultConnectionLimit; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 24 / 90

Slide 37

Slide 37 text

Лимит одновременных соединений Лимит по умолчанию равен 2; ServicePointManager.DefaultConnectionLimit; Для localhost по умолчанию равен int.MaxValue; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 24 / 90

Slide 38

Slide 38 text

Лимит одновременных соединений Лимит по умолчанию равен 2; ServicePointManager.DefaultConnectionLimit; Для localhost по умолчанию равен int.MaxValue; Только для .NET Framework. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 24 / 90

Slide 39

Slide 39 text

Выводы Нельзя на каждый запрос переоткрывать соединение; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 25 / 90

Slide 40

Slide 40 text

Выводы Нельзя на каждый запрос переоткрывать соединение; Хочется переиспользовать соединение; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 25 / 90

Slide 41

Slide 41 text

Выводы Нельзя на каждый запрос переоткрывать соединение; Хочется переиспользовать соединение; Нельзя постоянно держать соединение открытым; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 25 / 90

Slide 42

Slide 42 text

Выводы Нельзя на каждый запрос переоткрывать соединение; Хочется переиспользовать соединение; Нельзя постоянно держать соединение открытым; Вручную управлять сложно. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 25 / 90

Slide 43

Slide 43 text

Интерфейс IHttpClientFactory Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 26 / 90

Slide 44

Slide 44 text

IHttpClientFactory Позволяет создавать и конфигурировать HttpClient; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 27 / 90

Slide 45

Slide 45 text

IHttpClientFactory Позволяет создавать и конфигурировать HttpClient; Был добавлен в ASP.NET Core 2.1; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 27 / 90

Slide 46

Slide 46 text

IHttpClientFactory Позволяет создавать и конфигурировать HttpClient; Был добавлен в ASP.NET Core 2.1; Для консольного приложения необходимо добавить Microsoft.Extensions.Hosting и Microsoft.Extensions.Http Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 27 / 90

Slide 47

Slide 47 text

IHttpClientFactory Регистрация через метод расширения IServiceCollection: services.AddHttpClient (); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 28 / 90

Slide 48

Slide 48 text

IHttpClientFactory Добавление в конструктор с помощью DI: public GitHubService(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 29 / 90

Slide 49

Slide 49 text

IHttpClientFactory Создание клиента: var client = _clientFactory.CreateClient (); var response = await client.SendAsync(request); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 30 / 90

Slide 50

Slide 50 text

Named clients Регистрация через метод расширения IServiceCollection: services.AddHttpClient("github", c => { c.BaseAddress = new Uri("https :// api.github.com"); c.DefaultRequestHeaders .Add("Accept", "application/json"); }); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 31 / 90

Slide 51

Slide 51 text

Named clients Добавление в конструктор с помощью DI: public SomeService(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 32 / 90

Slide 52

Slide 52 text

Named clients Создание клиента: var client = _clientFactory.CreateClient("github"); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 33 / 90

Slide 53

Slide 53 text

Typed clients Класс типизированного клиента: public class GitHubClient { ... } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 34 / 90

Slide 54

Slide 54 text

Typed clients private readonly HttpClient _client; public GitHubClient(HttpClient client) { client.BaseAddress = new Uri("https :// api.github.com"); client.DefaultRequestHeaders.Add("Accept", "application/json"); _client = client; } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 35 / 90

Slide 55

Slide 55 text

Typed clients public async Task GetRepo(string repo) { var response = await _client .GetAsync($"/repos/octocat /{repo}"); ... return repository; } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 36 / 90

Slide 56

Slide 56 text

Typed clients Регистрация типизированного клиента: services.AddHttpClient (); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 37 / 90

Slide 57

Slide 57 text

Typed clients Добавление в конструктор через DI: public GitHubService(GitHubClient gitHubClient) { _gitHubClient = gitHubClient; } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 38 / 90

Slide 58

Slide 58 text

Refit Библиотека Refit для REST API (https://github.com/reactiveui/refit) Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 39 / 90

Slide 59

Slide 59 text

Refit public interface IGitHubClient { [Get("/repos/octocat /{repo}")] Task GetRepo(string repo); } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 40 / 90

Slide 60

Slide 60 text

Refit Регистрация клиента: services .AddRefitClient () .ConfigureHttpClient(c => c.BaseAddress = new Uri("https :// api.github.com")); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 41 / 90

Slide 61

Slide 61 text

Refit Добавление в конструктор через DI: public GitHubService(IGitHubClient gitHubClient) { _gitHubClient = gitHubClient; } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 42 / 90

Slide 62

Slide 62 text

Создание HttpClient Посмотрим глубже, как происходит создание HttpClient Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 43 / 90

Slide 63

Slide 63 text

Создание HttpClient public class HttpClient : HttpMessageInvoker public class HttpMessageInvoker : IDisposable private HttpMessageHandler _handler; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 44 / 90

Slide 64

Slide 64 text

Конструкторы HttpMessageInvoker public HttpMessageInvoker(HttpMessageHandler handler , bool disposeHandler) public HttpMessageInvoker(HttpMessageHandler handler) : this(handler , true) Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 45 / 90

Slide 65

Slide 65 text

Конструкторы HttpClient public HttpClient(HttpMessageHandler handler , bool disposeHandler) : base(handler , disposeHandler) public HttpClient(HttpMessageHandler handler) : this(handler , true) public HttpClient () : this(( HttpMessageHandler) new HttpClientHandler ()) Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 46 / 90

Slide 66

Slide 66 text

Конструкторы HttpClient 3 конструктора; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 47 / 90

Slide 67

Slide 67 text

Конструкторы HttpClient 3 конструктора; Есть возможность передать HttpMessageHandler; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 47 / 90

Slide 68

Slide 68 text

Конструкторы HttpClient 3 конструктора; Есть возможность передать HttpMessageHandler; В стандартном конструкторе disposeHandler равен true. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 47 / 90

Slide 69

Slide 69 text

Dispose HttpClient protected override void Dispose(bool disposing) { if (disposing && !this._disposed) { this._disposed = true; this._pendingRequestsCts.Cancel (); this._pendingRequestsCts.Dispose (); } base.Dispose(disposing); } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 48 / 90

Slide 70

Slide 70 text

Dispose HttpMessageInvoker protected virtual void Dispose(bool disposing) { if (! disposing || this._disposed) return; this._disposed = true; if (!this._disposeHandler) return; this._handler.Dispose (); } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 49 / 90

Slide 71

Slide 71 text

Dispose HttpClient Отменяются все запросы ⇒ могут отменяться чужие запросы; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 50 / 90

Slide 72

Slide 72 text

Dispose HttpClient Отменяются все запросы ⇒ могут отменяться чужие запросы; Не стоит шарить HttpClient; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 50 / 90

Slide 73

Slide 73 text

Dispose HttpClient Отменяются все запросы ⇒ могут отменяться чужие запросы; Не стоит шарить HttpClient; После Dispose использовать нельзя; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 50 / 90

Slide 74

Slide 74 text

Dispose HttpClient Отменяются все запросы ⇒ могут отменяться чужие запросы; Не стоит шарить HttpClient; После Dispose использовать нельзя; Dispose вызывается у HttpMessageHandler только в случае disposeHandler = true. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 50 / 90

Slide 75

Slide 75 text

IHttpClientFactory public static HttpClient CreateClient( this IHttpClientFactory factory) { return factory .CreateClient(DefaultName); } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 51 / 90

Slide 76

Slide 76 text

DefaultHttpClientFactory public HttpClient CreateClient(string name) { HttpClient httpClient = new HttpClient(this.CreateHandler(name), false); return httpClient; } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 52 / 90

Slide 77

Slide 77 text

DefaultHttpClientFactory public HttpMessageHandler CreateHandler( string name) { ActiveHandlerTrackingEntry entry = this._activeHandlers.GetOrAdd(name , this._entryFactory).Value; this.StartHandlerEntryTimer(entry); return (HttpMessageHandler) entry.Handler; } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 53 / 90

Slide 78

Slide 78 text

Создание с помощью HttpClientFactory Dispose не трогает HttpMessageHandler; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 54 / 90

Slide 79

Slide 79 text

Создание с помощью HttpClientFactory Dispose не трогает HttpMessageHandler; HttpClientFactory переиспользует Handler; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 54 / 90

Slide 80

Slide 80 text

Создание с помощью HttpClientFactory Dispose не трогает HttpMessageHandler; HttpClientFactory переиспользует Handler; HttpClientFactory выставляет таймер для Handler; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 54 / 90

Slide 81

Slide 81 text

Создание с помощью HttpClientFactory Dispose не трогает HttpMessageHandler; HttpClientFactory переиспользует Handler; HttpClientFactory выставляет таймер для Handler; Получаем переиспользование соединений. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 54 / 90

Slide 82

Slide 82 text

Выводы HttpClientFactory, Named clients, Typed clients, Refit; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 55 / 90

Slide 83

Slide 83 text

Выводы HttpClientFactory, Named clients, Typed clients, Refit; Переиспользуют HttpMessageHandler; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 55 / 90

Slide 84

Slide 84 text

Выводы HttpClientFactory, Named clients, Typed clients, Refit; Переиспользуют HttpMessageHandler; Закрывают HttpMessageHandler спустя время. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 55 / 90

Slide 85

Slide 85 text

Дополнительные улучшения в .NET Core 2.1 Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 56 / 90

Slide 86

Slide 86 text

DelegatingHandler DelegatingHandler позволяют создать цепочку обработки исходящих запросов; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 57 / 90

Slide 87

Slide 87 text

DelegatingHandler DelegatingHandler позволяют создать цепочку обработки исходящих запросов; Схоже с middleware в ASP.NET Core; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 57 / 90

Slide 88

Slide 88 text

DelegatingHandler DelegatingHandler позволяют создать цепочку обработки исходящих запросов; Схоже с middleware в ASP.NET Core; Функциональность была, но с IHttpClientFactory стало проще использовать. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 57 / 90

Slide 89

Slide 89 text

DelegatingHandler Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 58 / 90

Slide 90

Slide 90 text

Создание DelegatingHandler public class SomeHandler : DelegatingHandler { override SendAsync (...) { ... var response = await base.SendAsync( request , cancellationToken); ... } } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 59 / 90

Slide 91

Slide 91 text

Пример override SendAsync (...) { var sw = Stopwatch.StartNew (); var resp = await base.SendAsync(request , ct); sw.Stop(); } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 60 / 90

Slide 92

Slide 92 text

Пример override SendAsync (...) { if (! request.Headers.Contains("Key")) { return new HttpResponseMessage (); } return await base.SendAsync(request , ct); } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 61 / 90

Slide 93

Slide 93 text

Пример override SendAsync (...) { return new HttpResponseMessage (); } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 62 / 90

Slide 94

Slide 94 text

Регистрация DelegatingHandler services.AddHttpClient("github") // first .AddHttpMessageHandler () // second .AddHttpMessageHandler () Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 63 / 90

Slide 95

Slide 95 text

Polly Библиотека Polly для обработки ошибок (https://github.com/App-vNext/Polly) Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 64 / 90

Slide 96

Slide 96 text

Polly Подходит не только для HttpClient; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 65 / 90

Slide 97

Slide 97 text

Polly Подходит не только для HttpClient; Содержит различные политики: Retry, Circuit Breaker, Timeout, . . . Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 65 / 90

Slide 98

Slide 98 text

Polly Подходит не только для HttpClient; Содержит различные политики: Retry, Circuit Breaker, Timeout, . . . Необходимо установить Microsoft.Extensions.Http.Polly. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 65 / 90

Slide 99

Slide 99 text

Добавление политик Обработка всех ответов со статус кодами 5xx и 408 services.AddHttpClient("github") .AddTransientHttpErrorPolicy(p => p.RetryAsync (3)) .AddTransientHttpErrorPolicy( p => p.CircuitBreakerAsync (5, TimeSpan.FromSeconds (30))); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 66 / 90

Slide 100

Slide 100 text

Настройка внутреннего HttpMessageHandler services.AddHttpClient("github") .ConfigurePrimaryHttpMessageHandler (() => { return new SocketsHttpHandler () { AutomaticDecompression = DecompressionMethods.GZip }; }); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 67 / 90

Slide 101

Slide 101 text

Настройка внутреннего HttpMessageHandler AllowAutoRedirect; AutomaticDecompression; MaxAutomaticRedirections; MaxResponseHeadersLength; . . . Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 68 / 90

Slide 102

Slide 102 text

Время жизни HttpMessageHandler services.AddHttpClient("github") .SetHandlerLifetime(TimeSpan.FromMinutes (5)); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 69 / 90

Slide 103

Slide 103 text

Выводы Добавились методы для удобной настройки HttpClient. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 70 / 90

Slide 104

Slide 104 text

HttpRequestMessage и HttpResponseMessage Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 71 / 90

Slide 105

Slide 105 text

HttpRequestMessage Представляет сообщение HTTP-запроса. HttpMethod method; Uri requestUri; HttpRequestHeaders headers; Version version, значение по умолчанию Version(2, 0); HttpContent content, который является IDisposable; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 72 / 90

Slide 106

Slide 106 text

Диспозить или нет? 1 Не диспозить var req = new HttpRequestMessage (); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 73 / 90

Slide 107

Slide 107 text

Диспозить или нет? 1 Не диспозить var req = new HttpRequestMessage (); 2 Диспозить using(var req = new HttpRequestMessage ()) { ... } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 73 / 90

Slide 108

Slide 108 text

Диспозить или нет? Зависит от HttpContent; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 74 / 90

Slide 109

Slide 109 text

Диспозить или нет? Зависит от HttpContent; Чаще всего это StringContent ⇒ можно не диспозить∗. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 74 / 90

Slide 110

Slide 110 text

HttpResponseMessage Представляет ответное сообщение HTTP. HttpStatusCode statusCode (есть проверка value > 0 и value < 999); HttpResponseHeaders headers; string reasonPhrase HttpRequestMessage requestMessage Version version, значение по умолчанию Version(1, 1); HttpContent content, который является IDisposable; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 75 / 90

Slide 111

Slide 111 text

Диспозить или нет? 1 Не диспозить var resp = await client.GetAsync (...); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 76 / 90

Slide 112

Slide 112 text

Диспозить или нет? 1 Не диспозить var resp = await client.GetAsync (...); 2 Диспозить using(var resp = await client.GetAsync (...)) { ... } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 76 / 90

Slide 113

Slide 113 text

Диспозить или нет? Для GetAsync и SendAsync; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 77 / 90

Slide 114

Slide 114 text

Диспозить или нет? Для GetAsync и SendAsync; HttpCompletionOption: ResponseContentRead, ResponseHeadersRead; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 77 / 90

Slide 115

Slide 115 text

Диспозить или нет? Для GetAsync и SendAsync; HttpCompletionOption: ResponseContentRead, ResponseHeadersRead; Если ResponseContentRead, то данные сохраняются в MemoryStream ⇒ можно без диспоза∗; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 77 / 90

Slide 116

Slide 116 text

Диспозить или нет? Для GetAsync и SendAsync; HttpCompletionOption: ResponseContentRead, ResponseHeadersRead; Если ResponseContentRead, то данные сохраняются в MemoryStream ⇒ можно без диспоза∗; Иначе стоит диспозить. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 77 / 90

Slide 117

Slide 117 text

∗Всё-таки диспозить или нет? Это внутрення реализация, которая может измениться; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 78 / 90

Slide 118

Slide 118 text

∗Всё-таки диспозить или нет? Это внутрення реализация, которая может измениться; Лучше всегда диспозить; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 78 / 90

Slide 119

Slide 119 text

∗Всё-таки диспозить или нет? Это внутрення реализация, которая может измениться; Лучше всегда диспозить; using declarations. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 78 / 90

Slide 120

Slide 120 text

Using declarations using var req = new HttpRequestMessage (); using var resp = await client.GetAsync (...) Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 79 / 90

Slide 121

Slide 121 text

Антипаттерн чтения в строку var response = await client.GetAsync("/repos/octocat/Hello -World"); var str = await response.Content.ReadAsStringAsync (); var repository = JsonConvert.DeserializeObject (str); return repository; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 80 / 90

Slide 122

Slide 122 text

Десериализуем из stream var srz = new JsonSerializer (); var response = await client.GetAsync("/repos/octocat/Hello -World"); var stream = await response.Content.ReadAsStreamAsync (); using (var sr = new StreamReader(stream)) using (var jsonReader = new JsonTextReader(sr)) { return srz.Deserialize ( jsonReader); } Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 81 / 90

Slide 123

Slide 123 text

Десериализуем из stream в .net core 3.0 var response = await client.GetAsync("/repos/octocat/Hello -World"); var stream = await response.Content.ReadAsStreamAsync (); var repository = await JsonSerializer.DeserializeAsync ( stream); return repository; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 82 / 90

Slide 124

Slide 124 text

Выводы HttpRequestMessage и HttpResponseMessage лучше объявлять, используя using declarations; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 83 / 90

Slide 125

Slide 125 text

Выводы HttpRequestMessage и HttpResponseMessage лучше объявлять, используя using declarations; Лучше не использовать промежуточную строку при десериализации. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 83 / 90

Slide 126

Slide 126 text

Новое в .NET Core 3.0 Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 84 / 90

Slide 127

Slide 127 text

Поддержка HTTP/2 using (var request = new HttpRequestMessage(HttpMethod.Get , "/") { Version = new Version(2, 0) }) Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 85 / 90

Slide 128

Slide 128 text

Поддержка HTTP/2 var client = new HttpClient () { BaseAddress = new Uri("https :// localhost :80"), DefaultRequestVersion = new Version(2, 0) }; Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 86 / 90

Slide 129

Slide 129 text

Регистрация gRPC Client Схожий шаблон с HttpClient: services.AddGrpcClient ( options => { options.BaseAddress = new Uri("https :// localhost :5001"); }); Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 87 / 90

Slide 130

Slide 130 text

Общий вывод В .NET Framework используйте ServicePointManager; В .NET Core используйте IHttpClientFactory. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 88 / 90

Slide 131

Slide 131 text

Ссылки You’re using HttpClient wrong and it is destabilizing your software; Singleton HttpClient? Beware of this serious behaviour and how to fix it; Beware of the .NET HttpClient; Подводные камни HttpClient в .NET; Make HTTP requests using IHttpClientFactory in ASP.NET Core; Steve Gordon https://www.stevejgordon.co.uk. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 89 / 90

Slide 132

Slide 132 text

Контакты https://github.com/rafaelldi/SpbDotNet-HttpClient; @rafaelldi; https://arcadeprogramming.com/. Риваль Абдрахманов (PT) HttpClient SpbDotNet, 2019 90 / 90