Zeek.Http.Refit 10.0.39

Zeek.Http.Refit

Zeek.Http.RefitZeek.Http 的可选 Refit 薄适配模块包。

它只提供 RefitModule 和 Refit 注册捷径,复用 Zeek.Http 的 frozen endpoint、非敏感全局 header 和 authorization handler;Refit API 与普通 typed client 共用 Zeek.Http 的 build-time identity guard,避免同一个 unkeyed contract 在两个入口之间静默覆盖。它不扫描程序集、不生成代码、不定义 REST DSL,也不改变 Refit 自身行为。

源码结构

模块源码按薄适配边界分目录,但 public API namespace 不变:

Extensions/      # ModuleContext 注册入口与原生 builder 接缝
Building/        # Refit API builder
Internal/        # 构建期注册标记与 contract identity guard

安装

<ItemGroup>
  <PackageReference Include="Zeek.Http.Refit" />
</ItemGroup>

业务模块通过依赖 RefitModule 接入;RefitModule 会声明对 HttpModule 的依赖。

注册 Refit API

模块依赖:

public static IReadOnlyList<ModuleDependency> Dependencies =>
    [ModuleDependency.Of<RefitModule>()];

默认 endpoint:

context.AddZeekRefitApi<OrderAuthorizationProvider>(refitSettings, api => api
    .Add<IOrderApi>()
    .Add<ICustomerApi>()
    .Add<IProductApi>());

指定 endpoint:

context.AddZeekRefitApi<ReportAuthorizationProvider>("ReportApi", refitSettings, api => api
    .Add<IReportApi>());

每个 API client 都应绑定自己的 provider 类型,不要集中到一个 provider 里按 endpoint switch:

context.AddZeekRefitApi<IdentityAuthorizationProvider>("IdentityApi", refitSettings, api => api
    .Add<IIdentityApi>());

context.AddZeekRefitApi<OrderAuthorizationProvider>("OrderApi", refitSettings, api => api
    .Add<IOrderApi>()
    .Add<ICustomerApi>());

context.AddZeekRefitApi<ReportAuthorizationProvider>("ReportApi", refitSettings, api => api
    .Add<IReportApi>());

AddZeekRefitApi<TAuthorizationProvider>(...) 只是把多个 AddZeekRefitClient<TApi, TAuthorizationProvider>(...) 聚合到同一 endpoint 和同一 provider 下;每个 Refit interface 仍然显式注册,不扫描程序集。identity 是 (TApi, endpointName),并与 Zeek.Http 普通 typed client 共用同一个构建期 guard;同一 unkeyed interface 跨 endpoint 或跨两个入口重复注册都会在组合期失败。

TAuthorizationProvider 必须是具体 provider 类型;接口或抽象类型会在注册阶段失败。

JWT refresh 推荐结构

Zeek.Http.Refit 不内置 JWT refresh。登录态、bearer token、ERP header credential、refresh token rotation、401 retry 和 UI 跳转都属于应用自己的认证 workflow 或业务 API 模块。

推荐对外暴露后端级入口:

context.AddGtrApis(refitSettings);
context.AddErpApis(refitSettings);

推荐请求链路:

flowchart TD UI["UI / ViewModel"] Api["IGtrOrderApi protected Refit client"] Provider["GtrAuthProvider"] Refresh["GtrAuthService"] AuthApi["IGtrAuthApi anonymous Refit client"] Retry["Gtr401RetryHandler"] Server["Server"] UI --> Api Api --> Provider Provider --> Refresh Refresh --> AuthApi Api --> Retry Retry --> Server Retry -->|401 forced refresh once| Refresh

核心伪代码:

public sealed class GtrAuthProvider(
    GtrAuthService authService) : IZeekHttpAuthorizationProvider
{
    public async ValueTask ApplyAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken = default)
    {
        string? token = await authService.EnsureAccessTokenAsync(cancellationToken);
        if (!string.IsNullOrWhiteSpace(token))
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        }
    }
}

EnsureAccessTokenAsync(...) 应使用“过期时间驱动懒刷新”:access token 未临近过期时直接返回;即将过期时进入 single-flight 锁,调用匿名 IAuthApi.RefreshAsync(...),保存新 token 后返回。401 retry 应放在应用自定义 DelegatingHandler,retry 只做一次,并且只对可重放请求自动执行。

完整的两种刷新方案、流程图、状态机、single-flight 伪代码和 request body 重放注意事项见:

docs/topics/Zeek.Http 客户端授权与刷新指南.md

响应 envelope 解包

Zeek.Http.Refit 不内置统一业务返回值解包。GTR { code, message, data }、ERP { success, msg, result } 或其它业务 envelope 应由应用 API 模块自己解释。

推荐模式是应用侧自定义 response processor handler:HTTP 非成功状态码原样交给 401 retry 或错误分类;业务成功时把 response content 替换为 data / result JSON;业务失败时抛出后端自己的业务异常。这样 Refit interface 可以直接返回 DTO,同时不会把 GTR 或 ERP 的协议塞进 Zeek.Http.Refit

IZeekHttpResponseProcessorGtrResponseProcessorErpResponseProcessor 在专题中只是伪代码命名,不是当前框架 public API。完整流程图和伪代码见:

docs/topics/Zeek.Http 客户端授权与刷新指南.md

匿名请求

匿名 API 和受保护 API 使用不同的 Refit interface,并在组合期显式注册:

public interface IAuthApi
{
    [Post("/auth/login")]
    Task<LoginResponse> LoginAsync(
        [Body] LoginRequest request,
        CancellationToken cancellationToken = default);
}

context.AddZeekRefitAnonymousClient<IAuthApi>(refitSettings);
context.AddZeekRefitApi<BearerAuthorizationProvider>(refitSettings, api => api
    .Add<IOrdersApi>());

AddZeekRefitAnonymousClient<TApi>(...) 不接入 authorization provider;受保护 client 的每个请求都接入注册时绑定的 provider。Zeek.Http.Refit 不提供方法级匿名 attribute bridge,避免依赖 Refit request metadata 和运行时反射。登录、刷新和公开 health API 应归入独立匿名 interface。

边界

  • Zeek.Http.Refit 依赖 Refit;Zeek.Http 主包不依赖 Refit。
  • IZeekHttpGlobalHeaderProvider 只适合非敏感、后端无关 header;Authorization、Cookie、API key 和签名使用授权 provider 或应用 handler。
  • 不自动发现 API interface。
  • 不负责 token 保存、refresh 或 UI 跳转。
  • 不提供 AddZeekRefitBearerClient、JWT scheme registry 或 refresh token store。
  • 不提供统一业务 response unwrap 平台。
  • Refit serializer、attribute、ApiException 行为仍由 Refit 负责。

AOT / Trim

  • AOT 支持等级:Limited。
  • Trim 策略:使用 Refit source-generated client 和 generated request building,显式注册 interface,不扫描程序集发现 API interface。
  • 反射策略:Zeek 适配层不引入运行时扫描或方法级 attribute bridge;Refit 自身 generator、serializer 和 NativeAOT warning 由 Refit 与应用配置承担。

NativeAOT 场景应显式配置 source-generated System.Text.Json serializer;本模块不承诺 Refit upstream warning-clean。

Sample

  • ../../samples/Zeek.HttpRefitSample/
  • ../../samples/Zeek.HttpRefitAuthSample/
  • ../../samples/Zeek.HttpRefitSample.Server/

Zeek.HttpRefitSampleZeek.HttpRefitAuthSample 当前按发布态 sample 处理,通过 PackageReference 消费已发布的 Zeek.Http.Refit 包;Zeek.HttpRefitSample.Server 只是 sample-only HTTP 验收载体。

完整文档

  • ../../docs/modules/Zeek.Http.Refit.md
  • ../../docs/modules/Zeek.Http.md
  • ../../docs/topics/Zeek.Http 使用指南.md
  • ../../docs/topics/Zeek.Http 客户端授权与刷新指南.md

当前边界

  • Zeek.Http.Refit 是已发布的可选 Refit 薄适配包;业务项目不使用 Refit 时无需引用。
  • Refit 的 NativeAOT、接口代理、attribute、ApiException 和 serializer 行为不由 Zeek 重新封装。
  • 本模块不提供 token refresh、业务 envelope 解包平台、接口自动发现、上传下载任务管理或传输历史。

No packages depend on Zeek.Http.Refit.

.NET 10.0

Version Downloads Last updated
10.0.42 4 07/29/2026
10.0.41 15 07/22/2026
10.0.40 10 07/22/2026
10.0.39 19 07/19/2026
10.0.38 12 07/17/2026
10.0.37 1 07/16/2026
10.0.36 5 06/29/2026
10.0.35 1 06/29/2026
10.0.31 5 06/27/2026
10.0.30 2 06/27/2026
10.0.29 2 06/26/2026
10.0.28 2 06/26/2026
10.0.27 2 06/26/2026
10.0.26 4 06/25/2026
10.0.25 3 06/24/2026