Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/4: Рейтинг темы: голосов - 4, средняя оценка - 5.00
 Аватар для anapshy
531 / 272 / 220
Регистрация: 14.11.2016
Сообщений: 1,052

Правильно ли использую типизированный HttpClient?

03.08.2023, 07:22. Показов 988. Ответов 8
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Windows Forms - .NET Framework 4.8 - C# 7.3
Установил NuGet пакеты: Microsoft.Extension.DependencyInjection, Microsoft.Extension.Http

Код на оценку:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public sealed class TrueServiceControl
{
    private static ServiceCollection _services { get; }
    private static ServiceProvider _serviceProvider { get; }
    /// URL адрес API
    private string _url { get; set; }
    /// Токен аутентификации
    private string _token { get; set; }
    /// ID соединения
    public string ConnectionId { get; set; }
 
    static TrueServiceControl()
    {
        // Правильно ли, что тут создаю коллекцию и провайдера, единых для всех объектов производных от данного control класса?
        _services = new ServiceCollection();
        _serviceProvider = _services.BuildServiceProvider();
    }
 
    public TrueServiceControl(string url, string connectionId)
    {
        _url = url;
        ConnectionId = connectionId;
 
        // TrueServiceClient определяем конструктор TrueServiceClient(HttpClient)
        // сохраняя переданного клиента в поле "private HttpClient _httpClient"
        _services.AddHttpClient<ITrueService, TrueServiceClient>(client =>
        {
            client.BaseAddress = new Uri($"https://{_url}");
            client.Timeout = TimeSpan.FromSeconds(30.0);
        });
    }
 
    /// Возвращает типизированного клиента
    private TrueServiceClient GetClient()
    {
        return _serviceProvider.GetRequiredService<ITrueService>() as TrueServiceClient;
    }
 
    public async Task<string> GetTokenAsync()
    {
        var client = GetClient(); // Получение типизированного клиента
        return await client.GetTokenAsync(ConnectionId);
    }
    // ...
}
И есть вопрос. В проекте используется NLog. Как мне его передать в TrueServiceClient?
До перехода на типизированных клиентов была реализация через singleton и конструктор выглядел следующим образом:
C#
1
TrueService(string url, string connectionId, ILogger logger)
Но т.к. теперь вся работа c сетью возложена на TrueServiceClient, а у него 1 конструктор для ловли объектов типа HttpClient, возникает вопрос, как это сделать? Пока надумал что-то такое:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class TrueServiceControl
{
    // ...
    public TrueServiceControl(string url, string connectionId, ILogger logger)
    {
         _url = url;
         ConnectionId = connectionId;
         _services.AddHttpClient<ITrueService, TrueServiceClient>((client, serviceProvider) =>
        {
            client.BaseAddress = new Uri($"https://{_url}");
            client.Timeout = TimeSpan.FromSeconds(30.0);
            return new TrueServiceClient(client, logger);
        });
    }
    // ...
}
 
class TrueServiceClient : ITrueService
{
    // ...
    public TrueServiceClient(HttpClient httpClient, ILogger logger) { /* ... */ }
    // ...
}
В планах использовать еще и вторую API (т.е. напишу отдельный класс), и ОК, если у каждого из этих классов будут свои статические сервисы и провайдеры.
Или стоит их вынести в Main программы, добавить там типизированных клиентов, а в класс передавать провайдера?!
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
03.08.2023, 07:22
Ответы с готовыми решениями:

Правильно ли использую openMP
Всем привет! подскажите пожалуйста, правильно ли я распаралелил программу используя openMP, вообще мне нужно записать все элементы матрицы...

Const_cast - правильно ли я его использую?
Доброго времени суток! #include &lt;string&gt; #include &lt;iostream&gt; #include &lt;typeinfo&gt; using namespace std; int main() { ...

Правильно ли я использую предикаты в List.FindAll
Добрый день, есть задача отфильтровать список объектов по заранее неизвестному списку условий. Класс MyFilter описывает одно условие. ...

8
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,227
03.08.2023, 09:14
Цитата Сообщение от anapshy Посмотреть сообщение
Пока надумал что-то такое:
Так и надо делать - это называется типизированный http client (то есть класс, который использует HttpClient с определенными настройками).
Цитата Сообщение от anapshy Посмотреть сообщение
Код на оценку:
Зачем тут нужен TrueServiceControl? В чем смысл существования этого класса? Почему он берет на себя обязанности DI-container-а?
Речь об этом:
C#
1
2
3
4
5
6
static TrueServiceControl()
    {
        // Правильно ли, что тут создаю коллекцию и провайдера, единых для всех объектов производных от данного control класса?
        _services = new ServiceCollection();
        _serviceProvider = _services.BuildServiceProvider();
    }
Добавлено через 2 минуты
Просто в целевом приложении стройте свой DI-container (ServiceCollection) и получайте свой TrueServiceClient.
1
 Аватар для anapshy
531 / 272 / 220
Регистрация: 14.11.2016
Сообщений: 1,052
03.08.2023, 10:11  [ТС]
Цитата Сообщение от IamRain Посмотреть сообщение
Просто в целевом приложении стройте свой DI-container (ServiceCollection) и получайте свой TrueServiceClient.
А как я смогу хранить token в объекте TrueServiceClient и время, когда он истекает, чтобы при запросе токена, он смог обновиться?

Мне же по сути фабрика возвращает каждый раз новый объект типа TrueServiceClient??? лишь разница в том, что HttpClientHandler в HttpClient, который хранит инфу о сокете будет неизменным, до случая, пока не истечет указанное время (которое я потом включу в Main, для борьбы с проблемами HttpClient и DNS)?

А так, я сейчас храню токен в TrueServiceControl, и при вызове метода, который его требует мне не придется его указывать, т.к. это скрыто:
C#
1
2
3
4
5
6
7
8
9
10
public async Task<string> GetTokenAsync()
{
    if (DateTime.Now >= TokenExpirationDate)
    {
        var client = GetClient();
        _token = await client.GetBearerTokenAsync(ConnectionId); // А тут например ConnectionId, который тоже может в любой момент измениться, и его бы тоже по хорошему храниться в TrueServiceClient
        TokenExpirationDate = DateTime.Now.AddHours(9.0).AddMinutes(55);
    }
    return _token;
}
C#
1
2
3
4
5
public async Task<ProductInfoResponse> GetProductInfoByProductGroupAsync(ProductGroup group)
{
    var client = GetClient();
    return await client.GetProductInfoByProductGroupAsync(await GetTokenAsync(), group); // А тут токен передаю
}
Пока не нашел решения проблемы этого костыля

Добавлено через 3 минуты
Я пытаюсь что-то искать, какие-то решения, примеры под .Net Framework, но всё попадается для ASP.NET, а там другой подход к проектированию

Добавлено через 5 минут
А изначально вообще всё на HttpWebRequest было, проект в ужасном состоянии, никакой архитектуры, паттернов, куча дубликатов, просто кучи огромных функций и там же запросы к БД... А надо на новую API переезжать, и щас кучу кода перебирать... Я почитал и в основном все разговоры о том, что "HttpWebRequest - прошлый век, HttpClient проще, HttpClient удобней..." и вот разгребаю проект
0
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,227
03.08.2023, 10:44
Цитата Сообщение от anapshy Посмотреть сообщение
и время, когда он истекает, чтобы при запросе токена, он смог обновиться?
Этот момент вы обрабатываете внутри вашего сервиса TrueServiceClient. Если токен истек, то сервис при очередном запросе словит ошибку и поймет это, самостоятельно перезапросит (если нужно) токен.

Цитата Сообщение от anapshy Посмотреть сообщение
А так, я сейчас храню токен в TrueServiceControl, и при вызове метода, к
Ну и у вас сейчас каша с созданием Transient-инстансов вашего typed-клиента.

C#
1
2
3
4
5
6
7
public async Task<ProductInfoResponse> GetProductInfoByProductGroupAsync(ProductGroup group)
{
    var client = GetClient(); //создает один экземпляр
    return await client.GetProductInfoByProductGroupAsync(await GetTokenAsync(), group); // GetTokenAsync создает уже другой экземпляр 
                                                                                         //клиента, который только и делает что получает токен
                                                                                         // зачем эта каша тут?
}
Токен - это атрибут вашего типизированного клиента, его можно вынести как public/protected в базовый класс и делать производные Rest-клиенты, которые вам нужны.
Класс должен проектироваться так, чтобы его можно было очень легко использовать правильно, и очень сложно использовать неправильно.
А сейчас у вас какая то нерациональная suboptimal каша. Имхо.

Добавлено через 2 минуты
TrueServiceControl тип вообще не нужен тут.

Добавлено через 11 минут
Проверка элементарная:
Хочешь сделать запрос? - выполняй примерные шаги:
1. Проверь пустой ли токен, если пустой - запроси новый.
2. Непустой - делай запрос.
3. Запрос упал по 401 - значит токен истек, запроси новый.
4. Делай запрос.

И так в каждом методе вашего клиента. Можно усложнить:
1. Через композицию в базовый Rest-клиент пробросить ITokenProvider (который синглетон, и который будет иметь IMemoryCache для хранения токенов)
2. При получении токена добавлять его в этот кэш со временем жизни, до Expiration Date самого токена.
3. И перед каждой операцией запрашивать по ключу этот токен.

Ну это уже мои фантазии, сам по себе запрос токена в ITokenProvider с кэшем может походить на легкий overhead по сравнению с простым хранением токена в поле класса. Тут уже вам решать.
1
 Аватар для anapshy
531 / 272 / 220
Регистрация: 14.11.2016
Сообщений: 1,052
03.08.2023, 10:52  [ТС]
IamRain,
Цитата Сообщение от IamRain Посмотреть сообщение
его можно вынести как public/protected в базовый класс и делать производные Rest-клиенты
Т.е. вынести в TrueServiceClient? Но он разве будет сохраняться в объекте, если объект буду получать так: serviceProvider.GetRequiredService<TrueServiceClient>()?
И мне в каждом методе где нужно выполнить запрос придется получать сервис через GetRequiredService?

Я сейчас могу сделать так:
C#
1
2
3
4
public static class ServiceManager
{
    public static TrueServiceControl True { get; } = new TrueServiceControl();
}
Потом в коде через свойства задать URL (TrueServiceControl.Url = "example.ru") и ConnectionId для объекта True.
И где нужно просто прописать:
C#
1
await ServiceManager.True.GetProductInfoByProductGroupAsync(someGroup);
Добавлено через 4 минуты
Цитата Сообщение от IamRain Посмотреть сообщение
Запрос упал по 401 - значит токен истек, запроси новый.
Само интересное что он не падает, а возвращает JSON с информацией об ошибке Это "Честный знак". True API

Добавлено через 3 минуты
А через Json за раз может передаваться много информации о продукции. А завод на месте не стоит, и мне кажется такая идея с ожиданием и повторной отправкой не подошла бы. Пока ещё это всё честный знак обработает....
0
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,227
03.08.2023, 10:57
Цитата Сообщение от anapshy Посмотреть сообщение
Но он разве будет сохраняться в объекте, если объект буду получать так:
Ну так клиент должен жить как минимум все то время, что будет жить токен. Зачем отрывать время жизни токена от времени жизни клиента, его использущего?

Добавлено через 1 минуту
Цитата Сообщение от anapshy Посмотреть сообщение
Т.е. вынести в TrueServiceClient?
Четко определить какое общее состояние (токен как минимум) и поведение должно быть у всех Rest-клиентов. Вынести это все в базовый класс и унаследовать TrueServiceClient от этого класса.

Добавлено через 1 минуту
Цитата Сообщение от anapshy Посмотреть сообщение
Это "Честный знак". True API
Бывает, значит будете проверять содержимое. Некоторые олды так пишут.)
1
 Аватар для anapshy
531 / 272 / 220
Регистрация: 14.11.2016
Сообщений: 1,052
03.08.2023, 11:02  [ТС]
Цитата Сообщение от IamRain Посмотреть сообщение
Зачем отрывать время жизни токена от времени жизни клиента, его использущего
Токен действителен 10 часов.
Но клиент то по сути 1 и тот же, объекты только разные через GetClient приходят.
И когда решу проблему с DNS, выставлю например время на 2 минуты, то Handler по сути будет изменяться каждые две минуты? и запрашивать у DNS актуальный IP.

Добавлено через 2 минуты
Мне ж не нужно каждый раз новый токен получать, если у меня ещё прошлый не истек

Добавлено через 1 минуту
Цитата Сообщение от IamRain Посмотреть сообщение
Вынести это все в базовый класс и унаследовать TrueServiceClient от этого класса.
Вот этот момент не понял
0
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,227
03.08.2023, 11:04
Цитата Сообщение от anapshy Посмотреть сообщение
Вот этот момент не понял
Это про ООП.
1
 Аватар для anapshy
531 / 272 / 220
Регистрация: 14.11.2016
Сообщений: 1,052
03.08.2023, 11:57  [ТС]
По сути у меня сейчас следующая зависимость:
C#
1
2
3
TrueServiceControl : ITokenProvider
TrueServiceClient : ITrueService
ITrueService
C#
1
2
3
4
public interface ITokenProvider
{
    Task<string> GetTokenAsync();
}
Он мне нужен для того, чтобы передать ServiceManager.True в класс работающий с другой API и принимающий объекты унаследованные от ITokenProvide . И той API нужно знать токен от TrueAPI и она просто будет дергать GetTokenAsync и получать уже определенный токен в системе и если нужно TrueAPI предоставит новый актуальный токен и он изменится во всей системе у меня. Как бы так.

Добавлено через 35 минут
IamRain, не знаете почему может не регистрировать клиентов?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
        static TrueServiceControl()
        {
            _services = new ServiceCollection();
            _serviceProvider = _services.BuildServiceProvider();
        }
 
        public static string Url
        {
            get
            {
                return _url;
            }
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    throw new InvalidEnumArgumentException("URL не может быть пустой строкой");
                }
                if (value != _url)
                {
                    _url = value;
                    _services.AddHttpClient<ITrueService, TrueServiceClient>((client, serviceProvider) =>
                    {
                        client.BaseAddress = new Uri($"https://{_url}");
                        client.Timeout = TimeSpan.FromSeconds(30.0);
                        return new TrueServiceClient(client, Logger);
                    });
                }
            }
        }
        
        private TrueServiceClient GetClient()
        {
            try
            {
                return _serviceProvider.GetRequiredService<ITrueService>() as TrueServiceClient;
            }
            catch(InvalidOperationException)
            {
                throw new InvalidOperationException("Ни один типизированный клиент не зарегистрирован в провайдере");
            }
        }
Задаю Url. Отладчик весь код в setter выполняет отлично, а при GetClient получаю исключение

Добавлено через 13 минут
Всё, разобрался, добавил после _services.AddHttpClient:
C#
1
_serviceProvider = _services.BuildServiceProvider();
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
03.08.2023, 11:57
Помогаю со студенческими работами здесь

Подскажите пожалуйста, правильно ли я использую операцию разыменования?
#include&lt;iostream.h&gt; #include&lt;stdlib.h&gt; void input(int **a,int,int); void output(int **a,int,int); int main() { int...

Как правильно создать типизированный вложенный класс через дженерики?
Допустим у меня есть класс Graph, внутри которого есть два класса Vertex и Edge. Мне необходимо сделать класс Vertex типизированным, чтобы...

Использую jsoup, пытаюсь парсить использую "select"
Задача получить значения номеров товароа (ASIN) на странице ...

Дан типизированный файл целых чисел. Переписать содержимое файла в новый типизированный файл
Дан типизированный файл целых чисел. Переписать содержимое файла в новый типизированный файл целых чисел, изменяя порядок ...

как в с++ создается типизированный файл? и как вообще понять типизированный?
вопрос в теме


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка.
Programma_Boinc 23.12.2025
Thinkpad X220 Tablet — это лучший бюджетный ноутбук для учёбы, точка. Рецензия / Мнение Это мой обзор планшета X220 с точки зрения школьника. Недавно я решила попытаться уменьшить свой. . .
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru