You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Refit中使用双基地址并配置X509证书的实现方案

如何用Refit实现多基地址+X509证书的HTTP请求

我之前用IHttpClientFactory实现了带X509证书的HTTP请求,运行正常。现在想切换到Refit库,但遇到了需要切换两个基地址(登录地址https://serverlogin.com和数据获取地址https://server2.com)的问题,尝试用DelegatingHandler在运行时修改基地址失败,请问正确实现方式是什么?


当前IHttpClientFactory实现代码

DI配置

.AddHttpClient(
    "name_client",
    c =>
    {
        c.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0");
    }
)
.ConfigurePrimaryHttpMessageHandler(() =>
{
    // 网站需要通过路径中的数字证书登录
    CookieContainer? cookieContainer = new();
    HttpClientHandler? handler =
        new() { CookieContainer = cookieContainer };
    handler.ClientCertificates.Add(
        DigitalCert.GetCert(context.Configuration)
    );
    return handler;
});

登录方法

public async Task Connect(string url)
{
    HttpClient httpclient = clientFactory.CreateClient(clientName);
    var res = await httpclient!.GetAsync(
        Path.Combine(
            string.Format(
                "https://serverlogin.com/cgi_AUT2000/CAutInicio.cgi?reference={0}",
                url // https://server2.com/
            )
        )
    );
}

数据获取方法

public async Task GetInfo(HttpRequestMessage message)
{
    message.RequestUri = new Uri("https://server2.com/cve_cgi/api/users/");
    HttpClient httpclient = clientFactory.CreateClient(clientName);
    HttpResponseMessage? res = await httpclient!.SendAsync(message);
}

当前Refit尝试实现代码

DI配置

services
    .AddRefitClient<IInterfaceRefit>()
    .ConfigureHttpClient(c =>
    {
        c.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0");
        c.BaseAddress = new Uri("https://serverlogin.com");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {        
        // 获取证书
    });

接口定义

public interface IInterfaceRefit
 {
     [Get("/cgi_AUT2000/CAutInicio.cgi")]
     Task Connect(string reference);

     [Get("/cvc_cgi/dte/rf_reobtencion2_folios/")]
     Task<string> GetInfo(
         [Body(BodySerializationMethod.UrlEncoded)] Dictionary<string, object> data
     );   
 }

解决方案

方案1:为不同基地址创建独立Refit客户端(推荐)

两个地址对应独立服务,拆分接口并分别注册客户端,逻辑清晰且避免基地址切换问题。

定义独立接口

// 登录专用接口
public interface ILoginApi
{
    [Get("/cgi_AUT2000/CAutInicio.cgi")]
    Task Connect([Query] string reference);
}

// 数据获取专用接口
public interface IDataApi
{
    [Get("/cvc_cgi/dte/rf_reobtencion2_folios/")]
    Task<string> GetInfo([Body(BodySerializationMethod.UrlEncoded)] Dictionary<string, object> data);
}

DI注册配置

// 注册登录客户端
services.AddRefitClient<ILoginApi>()
        .ConfigureHttpClient(c =>
        {
            c.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0");
            c.BaseAddress = new Uri("https://serverlogin.com");
        })
        .ConfigurePrimaryHttpMessageHandler(() =>
        {
            CookieContainer cookieContainer = new();
            HttpClientHandler handler = new() { CookieContainer = cookieContainer };
            handler.ClientCertificates.Add(DigitalCert.GetCert(context.Configuration));
            return handler;
        });

// 注册数据客户端
services.AddRefitClient<IDataApi>()
        .ConfigureHttpClient(c =>
        {
            c.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0");
            c.BaseAddress = new Uri("https://server2.com");
        })
        .ConfigurePrimaryHttpMessageHandler(() =>
        {
            CookieContainer cookieContainer = new();
            HttpClientHandler handler = new() { CookieContainer = cookieContainer };
            handler.ClientCertificates.Add(DigitalCert.GetCert(context.Configuration));
            return handler;
        });

使用示例

public class MyService
{
    private readonly ILoginApi _loginApi;
    private readonly IDataApi _dataApi;

    public MyService(ILoginApi loginApi, IDataApi dataApi)
    {
        _loginApi = loginApi;
        _dataApi = dataApi;
    }

    public async Task LoginAndFetchData()
    {
        await _loginApi.Connect("https://server2.com/");
        var dataResult = await _dataApi.GetInfo(new Dictionary<string, object>());
    }
}

方案2:用BaseAddressAttribute指定方法级基地址

无需拆分接口,直接在方法上标记专属基地址,覆盖全局配置。

修改接口定义

public interface ICombinedApi
{
    [Get("/cgi_AUT2000/CAutInicio.cgi")]
    [BaseAddress("https://serverlogin.com")]
    Task Connect([Query] string reference);

    [Get("/cvc_cgi/dte/rf_reobtencion2_folios/")]
    [BaseAddress("https://server2.com")]
    Task<string> GetInfo([Body(BodySerializationMethod.UrlEncoded)] Dictionary<string, object> data);
}

DI注册配置

services.AddRefitClient<ICombinedApi>()
        .ConfigureHttpClient(c =>
        {
            c.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0");
            // 全局基地址会被方法上的BaseAddressAttribute覆盖,可设占位值
            c.BaseAddress = new Uri("https://placeholder.com");
        })
        .ConfigurePrimaryHttpMessageHandler(() =>
        {
            CookieContainer cookieContainer = new();
            HttpClientHandler handler = new() { CookieContainer = cookieContainer };
            handler.ClientCertificates.Add(DigitalCert.GetCert(context.Configuration));
            return handler;
        });

方案3:自定义DelegatingHandler实现运行时切换

适合需要动态根据业务逻辑切换基地址的场景,修复之前的Handler失效问题。

实现自定义Handler

public class BaseAddressSwitchingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // 根据请求路径特征切换基地址
        if (request.RequestUri.AbsolutePath.StartsWith("/cvc_cgi/"))
        {
            var newUri = new Uri(new Uri("https://server2.com"), request.RequestUri.PathAndQuery);
            request.RequestUri = newUri;
        }
        else
        {
            var newUri = new Uri(new Uri("https://serverlogin.com"), request.RequestUri.PathAndQuery);
            request.RequestUri = newUri;
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

DI注册配置

// 注册自定义Handler
services.AddTransient<BaseAddressSwitchingHandler>();

// 注册Refit客户端并添加Handler
services.AddRefitClient<IInterfaceRefit>()
        .ConfigureHttpClient(c =>
        {
            c.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0");
            c.BaseAddress = new Uri("https://placeholder.com");
        })
        .AddHttpMessageHandler<BaseAddressSwitchingHandler>()
        .ConfigurePrimaryHttpMessageHandler(() =>
        {
            CookieContainer cookieContainer = new();
            HttpClientHandler handler = new() { CookieContainer = cookieContainer };
            handler.ClientCertificates.Add(DigitalCert.GetCert(context.Configuration));
            return handler;
        });

注意事项

  • 方案1符合单一职责原则,是最推荐的实现方式,维护成本最低。
  • 方案2适合接口方法较少、基地址固定的场景,代码简洁。
  • 方案3需注意Uri拼接的正确性,避免生成无效地址。

内容的提问来源于stack exchange,提问作者sergioocode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 09:33:20