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

WPF应用中使用IHttpClientFactory创建HttpClient时如何访问ProgressMessageHandler

问题描述

我原本有一段能正常读取下载进度的HttpClient代码:

var handler = new HttpClientHandler() { AllowAutoRedirect = true };
var ph = new ProgressMessageHandler(handler);

ph.HttpReceiveProgress += (_, args) =>
{
    // 处理下载进度的逻辑
};

var client = new HttpClient(ph);
client.BaseAddress = new Uri("https://localhost:7145/");
HttpRequestMessage requestMessage = new(HttpMethod.Get, "DownloadFile");
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _tokenConfig.AccessToken);
var response = await client.SendAsync(requestMessage);

现在我想移除类内的HttpClient初始化逻辑,改用微软的IHttpClientFactory注入,已经在启动类中完成了如下配置:

services.AddTransient<ProgressMessageHandler>();
services.AddHttpClient("api", opt =>
{
    opt.BaseAddress = new Uri(configuration.GetValue<string>("HttpClientConfig:BaseUrl"));
})
.AddHttpMessageHandler<ProgressMessageHandler>()
.ConfigurePrimaryHttpMessageHandler(() =>
{
    return new HttpClientHandler()
    {
        AllowAutoRedirect = false,
        UseDefaultCredentials = true
    };
});

现在可以通过_httpClientFactory.CreateClient("api")获取HttpClient,但不知道该如何及在何处访问HttpReceiveProgress事件。

解决方案

方法一:自定义可订阅进度的Handler

通过创建ProgressMessageHandler的子类,暴露进度事件供外部订阅,是最稳定可靠的方案:

  1. 定义自定义Handler类:
public class SubscribableProgressMessageHandler : ProgressMessageHandler
{
    // 暴露自定义的进度事件
    public new event EventHandler<HttpProgressEventArgs>? HttpReceiveProgress;

    public SubscribableProgressMessageHandler(HttpMessageHandler innerHandler) : base(innerHandler)
    {
        // 转发基类事件到自定义事件
        base.HttpReceiveProgress += (sender, args) => HttpReceiveProgress?.Invoke(sender, args);
    }
}
  1. 修改DI配置,替换原Handler为自定义类:
services.AddTransient<SubscribableProgressMessageHandler>();
services.AddHttpClient("api", opt =>
{
    opt.BaseAddress = new Uri(configuration.GetValue<string>("HttpClientConfig:BaseUrl"));
})
.AddHttpMessageHandler<SubscribableProgressMessageHandler>()
.ConfigurePrimaryHttpMessageHandler(() =>
{
    return new HttpClientHandler()
    {
        AllowAutoRedirect = false,
        UseDefaultCredentials = true
    };
});
  1. 使用时获取Handler并订阅事件:
var client = _httpClientFactory.CreateClient("api");
// 从HttpClient的Handler链中获取自定义进度Handler
if (client.GetTypedClientHandler<SubscribableProgressMessageHandler>() is { } progressHandler)
{
    progressHandler.HttpReceiveProgress += (_, args) =>
    {
        // 处理下载进度逻辑,例如计算百分比
        var progressPercent = args.BytesTransferred * 100 / args.TotalBytes;
        Console.WriteLine($"下载进度:{progressPercent}%");
    };
}

// 发起下载请求
var requestMessage = new HttpRequestMessage(HttpMethod.Get, "DownloadFile");
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _tokenConfig.AccessToken);
var response = await client.SendAsync(requestMessage);

方法二:直接从Handler链中获取原生ProgressMessageHandler

如果不想自定义Handler,可以通过遍历Handler链找到目标实例,但该方案依赖HttpClient内部实现,版本更新可能失效:

var client = _httpClientFactory.CreateClient("api");
var currentHandler = client.DefaultRequestHeaders.GetType().Assembly
    .GetType("System.Net.Http.HttpClient+HttpHandlerFilter")?
    .GetField("_innerHandler", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?
    .GetValue(client.DefaultRequestHeaders) as HttpMessageHandler;

// 遍历Handler链查找ProgressMessageHandler
while (currentHandler != null)
{
    if (currentHandler is ProgressMessageHandler progressHandler)
    {
        progressHandler.HttpReceiveProgress += (_, args) =>
        {
            // 处理下载进度逻辑
            Console.WriteLine($"已下载:{args.BytesTransferred} / {args.TotalBytes}");
        };
        break;
    }
    currentHandler = (currentHandler as DelegatingHandler)?.InnerHandler;
}

// 发起下载请求
var requestMessage = new HttpRequestMessage(HttpMethod.Get, "DownloadFile");
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _tokenConfig.AccessToken);
var response = await client.SendAsync(requestMessage);

注意:方法二使用反射,存在版本兼容性风险,优先推荐方法一。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 13:05:56