HTTP-клиент на Retrofit

Пакет ru.bgcrm.util.api содержит штатный механизм вызова внешних HTTP API из ERP. Разработчик описывает контракт интерфейсом Retrofit, помечает его @Api, а ApiClientFactory собирает синхронный клиент с общей конфигурацией, авторизацией, ограничением частоты, Circuit Breaker, логированием и телеметрией.

Новый код следует писать на этом механизме. Старый стек на основе ru.ufanet.retrofit.BasicClient помечен @Deprecated и закрыт от расширения архитектурным тестом.

Долгие пользовательские операции выполняйте по схеме асинхронной работы с внешними сервисами: после commit исходной команды, через AsyncWorker, без удержания JDBC-соединения во время сетевого ожидания.

Что делает фабрика

При каждом вызове ApiClientFactory.getClient() фабрика:

  1. читает @Api и секцию api.<name> из глобальной конфигурации ERP;

  2. выбирает JSON- или XML-маппер Jackson;

  3. создаёт OkHttp-клиент, его пул соединений и Dispatcher;

  4. подключает перехватчики, авторизацию и дополнительные расширения;

  5. добавляет Rate Limiter, Circuit Breaker и синхронный адаптер вызова;

  6. возвращает реализацию интерфейса, созданную Retrofit.

Фабрика не кэширует созданный Retrofit-прокси. Пулы и компоненты Resilience4j кэшируются отдельно по @Api.name. Поэтому один и тот же экземпляр интерфейса продолжает работать со старой конфигурацией, а новое значение настроек подхватывается только при следующем вызове getClient().

Быстрый старт

Описать контракт

import retrofit2.http.GET
import retrofit2.http.Path
import ru.bgcrm.util.api.annotation.Api

@Api(name = "catalog")
interface CatalogApi {
    @GET("api/products/{id}")
    fun getProduct(@Path("id") id: Long): ProductDto
}

data class ProductDto(
    val id: Long,
    val name: String
)

@Api имеет три параметра:

Параметр По умолчанию Назначение

name

нет

Ключ секции api.<name> и общий ключ кэшируемых компонентов клиента.

responseType

JSON

Основной формат тела: JSON или XML.

errorType

ru.ufanet.retrofit.Response

Модель JSON-ошибки. Должна наследовать ru.ufanet.retrofit.Response.

Методы используют стандартные аннотации Retrofit: @GET, @POST, @Path, @Query, @Body, @Header и другие. Они возвращают конечный тип, а не retrofit2.Call<T>: вызов выполняется синхронно в потоке вызывающего кода.

Добавить конфигурацию

api.catalog.name=Каталог товаров
api.catalog.url=https://catalog.example.test/
api.catalog.auth.type=STATIC_TOKEN
api.catalog.auth.header=Authorization
api.catalog.auth.token=<service-token>
api.catalog.http.connectTimeout=10000
api.catalog.http.readTimeout=10000

api.catalog совпадает с @Api(name = "catalog"). Base URL должен быть абсолютным и оканчиваться /, как требует Retrofit.

Полный перечень параметров приведён в разделе конфигурации.

Получить и вызвать клиент

import ru.bgcrm.context.core.ServerContext
import ru.bgcrm.context.core.getService
import ru.bgcrm.util.api.factory.ApiClientFactory

class CatalogService(
    apiClientFactory: ApiClientFactory = ServerContext.getService()
) {
    private val api = apiClientFactory.getClient(CatalogApi::class.java)

    fun getProduct(id: Long): ProductDto = api.getProduct(id)
}

Перегрузка getClient(CatalogApi::class.java, url) подменяет только base URL. Остальные настройки по-прежнему берутся из api.catalog.

Состав пакета

Область Ответственность

annotation, factory, config

Описание API, чтение настроек и сборка Retrofit/OkHttp-клиента.

auth

Выбор провайдера авторизации, получение и хранение внешних токенов.

adapter

Синхронное выполнение вызова и декораторы Rate Limiter/Circuit Breaker.

component

Кэшируемые пулы, Dispatcher, компоненты Resilience4j и их MBean.

converter

Подключение собственных конвертеров; встроенная обёртка SOAP 1.1.

interceptor, tag

Телеметрия, HTTP-логирование, методные теги и внешние перехватчики.

exception, method

Ошибки транспорта и отдельная обёртка над Rate Limiter для ручных сценариев.

Следующие разделы