Table of Contents

Документация

Библиотека Ascon.Polynom.Web.Api.Client

Клиентская библиотека .NET для Web API v1 ПОЛИНОМ:MDM. Содержит модели, интерфейсы, перечисления, JSON-настройки, сервисы вызова методов API и инфраструктуру авторизации (JWT: вход/выход/обновление токена).


1. Состав библиотеки

Папка / файл Содержимое
Models/ Классы-модели запросов и ответов API
Interfaces/ Интерфейсы моделей (ISignInRequest, ISearchRequest, IDocument, IPaginatedList<T> и др.)
Enums/ Перечисления (FilterType, AccessRights, PropertyType, TicketStatus и др.)
Services/ Клиенты методов API (DocumentApiClient, SearchApiClient, LoginApiClient и др.)
Json/ PolynomJsonOptions, PolymorphicTypeResolver и конвертеры (OptionalConverter, DateTimeConverter)
Auth/ Инфраструктура JWT-авторизации: AuthorizationService, AuthorizationMessageHandler, хранилища токенов, PolynomApiClientOptions
Optional<T> Структура для «частично заданных» значений
PolynomApiClientFactory Фабрика клиентов для использования без DI
AddPolynomApiClient Расширение для регистрации клиентов в DI

2. Установка

Библиотека распространяется как NuGet-пакет Ascon.Polynom.Web.Api.Client.

Требуется .NET SDK 8.0+.

2.1. Установка из локальной папки дистрибутива

Пакет поставляется как файл .nupkg в папке дистрибутива. Установка выполняется в кеш пакетов пользователя по умолчанию — создавать или править nuget.config не нужно:

dotnet add package Ascon.Polynom.Web.Api.Client --source C:\Distr\polynom-web\nuget

Укажите вместо C:\Distr\polynom-web\nuget путь к папке, в которой лежит .nupkg.

2.2. Куда попадает пакет

  • Кеш пакетов по умолчанию: %userprofile%\.nuget\packages (Windows), ~/.nuget/packages (Linux/macOS).
  • Пакет полностью распаковывается в <кеш>\ascon.polynom.web.api.client\<версия>\; рядом сохраняется копия исходного .nupkg.
  • В папку проекта содержимое пакета не копируется — проект ссылается на пакет из кеша.
  • Источник (--source) нужен только при первой установке: последующие dotnet restore берут пакет из кеша.

Проверить расположение кеша:

dotnet nuget locals global-packages --list

2.3. Ссылка на пакет

Команда dotnet add package добавляет в файл проекта ссылку на пакет:

<ItemGroup>
  <PackageReference Include="Ascon.Polynom.Web.Api.Client" />
</ItemGroup>

При необходимости версия фиксируется атрибутом Version:

<PackageReference Include="Ascon.Polynom.Web.Api.Client" Version="24.1.0.*" />

2.4. Дополнительное требование

Для работы с токенами (PersistTokens) требуется Microsoft.AspNetCore.DataProtection. В приложениях ASP.NET Core он доступен через FrameworkReference Microsoft.AspNetCore.App, который подключается автоматически при использовании пакета.


3. Быстрый старт: регистрация через DI

using Ascon.Polynom.Web.Api.Client;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddPolynomApiClient(options =>
{
    options.BaseAddress = new Uri("https://polynom-mdm.local:5100");
    options.SsoUrl = "https://sso.local";
    options.PersistTokens = true;
    options.TokenStoragePath = Path.Combine(AppContext.BaseDirectory, "tokens.protected");
    options.AutoRefreshToken = true;
    options.RefreshOnUnauthorized = true;
});

var app = builder.Build();

AddPolynomApiClient регистрирует:

  • AuthorizationService (singleton) — управление авторизацией;
  • ITokenRefresher, ILoginApiClientFactory, ITokenStorage, ITokenProtector;
  • именованный HttpClient (имя по умолчанию — PolynomApiClient) с AuthorizationMessageHandler;
  • все клиенты из Services/ (DocumentApiClient, SearchApiClient и т.д.) как transient.

Использование сервисов

using Ascon.Polynom.Web.Api.Client.Enums;
using Ascon.Polynom.Web.Api.Client.Models;
using Ascon.Polynom.Web.Api.Client.Services;
using Ascon.Polynom.Web.Api.Client.Auth;

app.MapPost("/search", async (
    AuthorizationService authorization,
    SearchApiClient search,
    CancellationToken cancellationToken) =>
{
    // Вход по логину и паролю (токен сохраняется и автоматически продлевается).
    if (!authorization.IsAuthenticated)
    {
        await authorization.SignInAsync(
            new SignInRequest
            {
                Login = "user",
                Password = "password",
            },
            cancellationToken);
    }

    var page = await search.ExecuteSearch(
        new SearchRequest
        {
            ObjectTypeFilter = FilterType.All,
            SearchText = "насос",
            PageNumber = 1,
            PageSize = 50,
        },
        cancellationToken);

    return Results.Ok(page?.Items);
});

4. Быстрый старт: без DI (фабрика)

Для консольных утилит и сценариев без IServiceCollection используйте PolynomApiClientFactory.

using Ascon.Polynom.Web.Api.Client;
using Ascon.Polynom.Web.Api.Client.Auth;
using Ascon.Polynom.Web.Api.Client.Enums;
using Ascon.Polynom.Web.Api.Client.Models;

// Протектор токенов на базе ASP.NET Data Protection (ключи — в файловом каталоге).
var protector = TokenProtectorFactory.CreateDataProtection(@"C:\ProgramData\Ascon\Keys");

using var factory = new PolynomApiClientFactory(
    new PolynomApiClientOptions
    {
        BaseAddress = new Uri("https://polynom-mdm.local:5100"),
        PersistTokens = true,
        TokenStoragePath = @"C:\ProgramData\Ascon\tokens.protected",
    },
    protector);

await factory.Authorization.SignInAsync(new SignInRequest
{
    Login = "user",
    Password = "password",
});

var search = factory.CreateSearchApiClient();
var page = await search.ExecuteSearch(new SearchRequest
{
    ObjectTypeFilter = FilterType.All,
    SearchText = "насос",
});

foreach (var item in page?.Items ?? [])
{
    Console.WriteLine(item);
}

Dispose() фабрики освобождает AuthorizationService и HTTP-обработчик. Для каждого клиента фабрика создаёт отдельный HttpClient с одним общим AuthorizationMessageHandler, поэтому токен подставляется автоматически.


5. Авторизация

Управление сессией выполняет AuthorizationService.

Элемент Назначение
IsAuthenticated Признак наличия действующего access-токена
SignInAsync(ISignInRequest, ...) Вход по логину и паролю
SignInBySystemAsync(ISignInBySystemRequest, ...) Системный вход
SignInByAccessTokenAsync(accessToken, ISignInRequest, tokenFromSso, ...) Вход по готовому access-токену (в т.ч. из SSO)
UpdateTokenAsync(...) Обновление пары токенов по refresh-токену
SignOutAsync(...) Выход из сессии (и из SSO, если настроено)
SignOutSsoAsync(...) Выход только из системы единой авторизации
SetSsoUrl(string?) Переопределение адреса SSO
AuthenticationStateChanged Событие EventHandler<bool>: изменилось состояние авторизации
SignedOut Событие EventHandler: выход выполнен
authorization.AuthenticationStateChanged += (_, isAuthenticated) =>
{
    Console.WriteLine($"Авторизован: {isAuthenticated}");
};

authorization.SignedOut += (_, _) => Console.WriteLine("Сессия закрыта.");

await authorization.SignInByAccessTokenAsync(
    accessToken,
    new SignInRequest
    {
        Login = "user",
        Password = "password",
    },
    tokenFromSso: true);

AuthorizationMessageHandler автоматически добавляет заголовок Authorization: Bearer <token> и при ответе 401 (если RefreshOnUnauthorized = true) однократно обновляет токен и повторяет запрос. Дополнительно AuthorizationService реализует IAsyncDisposable — обязательно освобождайте его (в DI это делает контейнер, при ручном создании — фабрика).


6. Параметры клиента

Все параметры задаются через PolynomApiClientOptions.

Свойство Тип По умолчанию Описание
BaseAddress Uri? — Базовый адрес Web API. Обязателен
SsoUrl string? — Адрес сервера SSO для выхода из единой сессии
ClientName string "PolynomApiClient" Имя именованного HttpClient
RefreshTokenBeforeExpiry TimeSpan 2 минуты За сколько до истечения продлевать токен
AutoRefreshToken bool true Автопродление токена по таймеру
RefreshOnUnauthorized bool true Обновлять токен и повторять запрос при ответе 401
ProtectTokensInMemory bool true Шифрование токенов в памяти
PersistTokens bool false Сохранять токены между запусками
TokenStoragePath string? — Путь к файлу токенов. Обязателен при PersistTokens = true

7. Работа с сервисами API

Каждый сервис — отдельный класс в Services/, соответствующий контроллеру Web API. Методы возвращают Task<T?> и принимают CancellationToken.

Сервис Назначение
LoginApiClient Авторизация, текущий пользователь/хранилище, обновление токена
DocumentApiClient Создание, удаление, чтение документов
ElementApiClient Работа с элементами
SearchApiClient Поиск: ExecuteSearch, ExecutePropertySearch, поиск заявок
PropertyApiClient Значения свойств
BackgroundTaskApiClient Фоновые задачи
TicketApiClient Заявки

Постраничный результат (ExecuteSearch) — это IPaginatedList<T> со свойством Items: List<T> и данными о странице.

// groupId — идентификатор группы документов.
var documents = await documentClient.GetByDocumentGroup(
    new IdentifierRequest { ObjectId = groupId },
    cancellationToken);

8. Optional

Структура Optional<T> различает «значение не задано» и «явно задано null». Используется в запросах частичного обновления, где важно передать только изменяемые поля.

using Ascon.Polynom.Web.Api.Client;

// Явно заданное значение.
Optional<int> pageSize = 50;

// Пустое (незаданное) значение.
var description = default(Optional<string>);
Optional<string> noValue = (string?)null;

if (pageSize.HasValue)
{
    Console.WriteLine(pageSize.Value); // 50
}

Console.WriteLine(description); // "unspecified"
Console.WriteLine(noValue);     // "unspecified"
Элемент Описание
HasValue true, если значение задано
Value Хранимое значение (T?)
implicit operator Optional<T>(T value) Неявное преобразование из T
ToString() Значение, "null" или "unspecified"

9. JSON и полиморфизм

Все клиенты сериализуют и десериализуют данные через единые настройки PolynomJsonOptions.Options:

  • camelCase-имена, регистронезависимый разбор, пропуск null;
  • конвертеры OptionalConverter и DateTimeConverter;
  • PolymorphicTypeResolver создаёт экземпляр конкретного типа при десериализации интерфейсов (ISignInRequest, ISearchRequest, IPaginatedList<T> и др.) по атрибуту [JsonDerivedType(...)].

Свои настройки можно получить через PolynomJsonOptions.GetJsonSerializerOptions() или применить к существующим: options.SetJsonSerializerOptions().

using System.Text.Json;
using Ascon.Polynom.Web.Api.Client.Json;

var json = JsonSerializer.Serialize(request, PolynomJsonOptions.Options);
var restored = JsonSerializer.Deserialize<ISearchRequest>(json, PolynomJsonOptions.Options);

10. Обработка ошибок

Методы API вызывают HttpResponseMessage.EnsureSuccessStatusCode(), поэтому при неуспешном HTTP-ответе выбрасывается System.Net.Http.HttpRequestException со статус-кодом в StatusCode.

using System.Net;
using System.Net.Http;

try
{
    var page = await search.ExecuteSearch(request, cancellationToken);
    return page?.Items ?? [];
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized)
{
    // Сессия истекла и не была продлена — требуется повторный вход.
    await authorization.SignInAsync(signInRequest, cancellationToken);
    throw;
}
catch (HttpRequestException ex)
{
    Console.WriteLine($"Ошибка запроса: {ex.StatusCode} — {ex.Message}");
    throw;
}