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

ASP.NET Core中HttpClient调用SendAsync前如何执行异步方法

官方原生支持方案

.NET 从.NET Core 2.1 引入IHttpClientFactory开始就提供了原生能力支持这类异步操作需求,不需要任何第三方库,也不需要魔改框架逻辑,最推荐使用DelegatingHandler 请求管道拦截的方案实现,完全适配异步获取令牌、自动注入请求头的场景。

方案1:自定义DelegatingHandler(推荐,在SendAsync环节执行逻辑)

这个方案是官方专门为HttpClient请求前置/后置拦截设计的机制,DelegatingHandler的核心方法SendAsync本身就是异步签名,天然支持await执行异步的令牌获取、缓存读写逻辑,不会有同步阻塞的问题。

实现步骤

  • 编写自定义令牌注入Handler
using System.Net.Http;
using System.Net.Http.Headers;

public class BearerTokenDelegatingHandler : DelegatingHandler
{
    // 直接通过构造函数注入你需要的服务,不管是缓存服务还是OAuth令牌请求服务都可以
    private readonly ITokenCache _tokenCache;
    private readonly IOAuthTokenClient _oAuthTokenClient;

    public BearerTokenDelegatingHandler(ITokenCache tokenCache, IOAuthTokenClient oAuthTokenClient)
    {
        _tokenCache = tokenCache;
        _oAuthTokenClient = oAuthTokenClient;
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, 
        CancellationToken cancellationToken)
    {
        // 先读缓存中的有效令牌,缓存 miss 则异步调用OAuth端点获取新令牌
        var accessToken = await _tokenCache.GetValidAccessTokenAsync(cancellationToken);
        if (string.IsNullOrWhiteSpace(accessToken))
        {
            var tokenResponse = await _oAuthTokenClient.GetTokenAsync(cancellationToken);
            accessToken = tokenResponse.AccessToken;
            // 把新令牌写入缓存,过期时间按OAuth返回的expires_in配置即可
            await _tokenCache.SetTokenAsync(accessToken, tokenResponse.ExpiresIn, cancellationToken);
        }

        // 注意:只给当前请求对象加Authorization头,不要修改HttpClient的DefaultRequestHeaders
        // 因为IHttpClientFactory会池化复用HttpClient实例,修改默认头会导致多请求串令牌、令牌过期不更新的问题
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

        // 执行后续请求管道逻辑
        return await base.SendAsync(request, cancellationToken);
    }
}
  • 注册服务到DI容器
// 注意:挂到HttpClient管道的DelegatingHandler必须注册为Transient生命周期
services.AddTransient<BearerTokenDelegatingHandler>();

// 注册命名HttpClient时,把自定义Handler加入请求管道
services.AddHttpClient("YourBusinessApiClient")
    .AddHttpMessageHandler<BearerTokenDelegatingHandler>();

方案优势

  • 完全原生实现,无额外依赖
  • 业务层使用时完全无感知,和普通用IHttpClientFactory创建Client的写法完全一致,不需要写任何重复的令牌处理代码
  • 每次发请求都会自动检查令牌有效性,自动处理过期刷新逻辑
  • 完美适配HttpClient池化机制,不会出现实例复用导致的头污染、令牌过期问题

方案2:类型化客户端封装(适配客户端获取环节的定制需求)

如果你需要在获取客户端实例的环节就完成相关配置,可以使用官方提供的类型化客户端(Typed Client)能力做一层封装。注意IHttpClientFactory.CreateClient本身是同步方法,不要在同步流程里阻塞等待异步逻辑,把令牌注入逻辑放到类型化客户端的异步请求方法里即可。

简单实现示例

public class BusinessApiClient
{
    private readonly HttpClient _httpClient;
    private readonly ITokenCache _tokenCache;
    private readonly IOAuthTokenClient _oAuthTokenClient;

    // IHttpClientFactory会自动按照配置注入对应的HttpClient实例
    public BusinessApiClient(
        HttpClient httpClient,
        ITokenCache tokenCache,
        IOAuthTokenClient oAuthTokenClient)
    {
        _httpClient = httpClient;
        _tokenCache = tokenCache;
        _oAuthTokenClient = oAuthTokenClient;
    }

    // 封装统一的请求发送方法,内部自动处理令牌注入
    public async Task<HttpResponseMessage> SendAsync(
        HttpMethod method,
        string requestUrl,
        HttpContent? content = null,
        CancellationToken cancellationToken = default)
    {
        var request = new HttpRequestMessage(method, requestUrl)
        {
            Content = content
        };

        // 令牌获取逻辑和DelegatingHandler里的逻辑完全一致
        var accessToken = await _tokenCache.GetValidAccessTokenAsync(cancellationToken);
        if (string.IsNullOrWhiteSpace(accessToken))
        {
            var tokenResponse = await _oAuthTokenClient.GetTokenAsync(cancellationToken);
            accessToken = tokenResponse.AccessToken;
            await _tokenCache.SetTokenAsync(accessToken, tokenResponse.ExpiresIn, cancellationToken);
        }
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

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

注册代码非常简单:

// 直接注册类型化客户端即可,内部自动走IHttpClientFactory的池化逻辑
services.AddHttpClient<BusinessApiClient>();

业务使用时直接注入BusinessApiClient调用对应方法即可,不需要手动创建HttpClient。

避坑提醒
  • 不要在AddHttpClient的同步配置委托(Action<IServiceProvider, HttpClient>)里用.Result、.Wait()这类方式阻塞等待异步令牌获取,不仅会造成线程池浪费,还很容易引发死锁;另外HttpClient实例会被池化长期复用,创建时写入的令牌过期后不会自动更新,会导致所有请求鉴权失败。
  • 注册DelegatingHandler时不要用Scoped、Singleton生命周期,必须用Transient,否则会出现生命周期不匹配的问题。
  • 不要把动态变化的令牌写入HttpClient.DefaultRequestHeaders,这个属性是HttpClient实例级别的,池化复用时会被所有请求共享,很容易出现令牌串用、过期的问题,令牌应该加到每次发送的HttpRequestMessage的Headers上。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 18:14:51