.Net HttpClient 管道黑科技,让你的请求效率飞起来!

HttpClient 管道黑科技,让你的请求效率飞起来!🚀

Pipeline(管道)是HttpClient网络请求的隐形加速器

HttpClient 采用了与 ASP.NET Core 管道机制相同的设计,通过组合 HttpMessageHandler中间件模式 形成请求/响应链来实现。允许你在请求和响应之间插入多个处理步骤,这些步骤可以按顺序执行,类似于管道。

  • 提高性能

    • 异步流水线式处理:利用async/await串联管道,实现非阻塞的请求-响应流水线,提升吞吐量;
    • 资源高效使用:结合IHttpClientFactory自动管理连接池,高效使用HttpClient实例;
  • 优化程序设计

    • 职责分离:将请求处理拆分为多个独立管道(如认证、日志、重试),每个管道专注单一功能;
    • 可插拔性‌:通过增减管道,动态调整处理流程(如临时添加请求加密步骤),无需修改核心逻辑;
    • 统一扩展点:为整个请求流程,形成统一的扩展点;结合AOP模式,更容易实现统一日志、权限、拦截、自定义流程等扩展功能。
  • 增加可维护

    • 基于管道,形成模块化、可扩展化程序设计,提高可维护性;
    • 调试友好:每个管道可单独测试、跟踪;

初始化

#!import "./Ini.ipynb"

1、创建自定义 HttpMessageHandler

/// <summary>
/// 日志管道(中间件)
/// </summary>
public class LoggingHandler : DelegatingHandler
{
    public LoggingHandler()
    {
        //防止成为最后一个管道时异常
        this.InnerHandler = new SocketsHttpHandler();
    }
    
    protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Console.WriteLine("日志管道: LoggingHandler -> Send -> Before");

        HttpResponseMessage response = base.Send(request, cancellationToken);
        response.EnsureSuccessStatusCode();

        Console.WriteLine("日志管道: LoggingHandler -> Send -> After");

        return response;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // 记录请求信息
        Console.WriteLine($"日志管道: LoggingHandler -> Send -> Before Request: {request.Method} {request.RequestUri}");
        // 调用管道中的下一个处理器,并获取响应
        var response = await base.SendAsync(request, cancellationToken);
        // 记录响应信息
        Console.WriteLine($"日志管道: LoggingHandler -> Send -> After Response: {response.StatusCode}");
        return response;
    }
}

/// <summary>
/// 令牌验证管道(中间件)
/// </summary>
public class TokenDelegatingHandler : DelegatingHandler 
{
    public TokenDelegatingHandler()
    {
        //防止成为最后一个管道时异常
        this.InnerHandler = new SocketsHttpHandler();
    }

    protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Console.WriteLine("令牌验证管道: TokenDelegatingHandler -> Send -> Added Token");

        if (!request.Headers.Contains(Microsoft.Net.Http.Headers.HeaderNames.Authorization)) 
        {
            Console.WriteLine("没有 Token, TokenDelegatingHandler 添加之");
            request.Headers.Add(Microsoft.Net.Http.Headers.HeaderNames.Authorization, "Bearer " + "a.b.c");
        }
        else
        {
            Console.WriteLine($"已有Token, {request.Headers.Authorization}");
        }

        HttpResponseMessage response = base.Send(request, cancellationToken);

        Console.WriteLine("令牌验证管道: TokenDelegatingHandler -> Send -> After");

        return response;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Console.WriteLine("令牌验证管道: TokenDelegatingHandler -> SendAsync -> Before");

        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

        Console.WriteLine("令牌验证管道: TokenDelegatingHandler -> SendAsync -> After");

        return response;
    }
}

///<summary>
/// 异常处理管道(中间件)
/// </summary>
public class ExceptionDelegatingHandler : DelegatingHandler
{
    public ExceptionDelegatingHandler()
    {
        //防止成为最后一个管道时异常
        this.InnerHandler = new SocketsHttpHandler();
    }

    protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Console.WriteLine("异常处理管道: ExceptionDelegatingHandler -> Send -> Before");

        HttpResponseMessage response;
        try 
        {
            response = base.Send(request, cancellationToken);
            response.EnsureSuccessStatusCode();
        }
        catch(Exception ex)
        {
            Console.WriteLine($"异常管道中间件,监控到异常:{ex.Message}");

            response = new HttpResponseMessage(HttpStatusCode.ExpectationFailed);
        }
        finally
        {
            //清理业务
        }
        
        Console.WriteLine("异常处理管道: ExceptionDelegatingHandler -> Send -> After");

        return response;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Console.WriteLine("异常处理管道: ExceptionDelegatingHandler -> SendAsync -> Before");

        HttpResponseMessage response;
        try 
        {
            response = response = await base.SendAsync(request, cancellationToken);
            response.EnsureSuccessStatusCode();
        }
        catch(Exception ex)
        {
            Console.WriteLine($"异常管道中间件,监控到异常:{ex.Message}");

            response = new HttpResponseMessage(HttpStatusCode.ExpectationFailed);
        }
        finally
        {
            //清理业务
        }

        Console.WriteLine("异常处理管道: ExceptionDelegatingHandler -> SendAsync -> After");

        return response;
    }
}

2、组合多个 HttpMessageHandler 形成请求/响应管道(处理链)

组合管道,就是利用 DelegatingHandler 类的 InnerHandler 属性,指定本管道的下一个管道(中间件),将多个管道链接起来,形成一个请求/响应处理链。

  • 默认管道
{   //默认配置
    var httpClient = new HttpClient()
    {
        BaseAddress = webApiBaseUri
    };

    //发送请求,从日志看管道执行顺序
    var response = await httpClient.GetAsync("/api/Hello/Ping");

    var content = await response.Content.ReadAsStringAsync();

    Console.WriteLine($"响应内容:{content}");
}
  • 手动组合
{   //组装管道

    /*
        1. 最后一个管道必须是SocketsHttpHandler 或者 保证最后的管道的InnerHandler有默认值
        2. 倒序组装,正序执行:HttpClient构造函数,传入的HttpMessageHandler是第一个执行的,其InnerHandler是下一个执行的,依次类推
    */

    var lastHandler = new SocketsHttpHandler()
    {
        UseCookies =  false,
        UseProxy = false,
    };

    var logPipeline = new LoggingHandler()
    {
        InnerHandler  = lastHandler
    };
    
    var exceptionHandler = new ExceptionDelegatingHandler()
    {
        InnerHandler = logPipeline
    };

    var tokenHandler = new TokenDelegatingHandler()
    {
        InnerHandler = exceptionHandler
    };

    var httpClient = new HttpClient(tokenHandler)
    {
        BaseAddress = webApiBaseUri
    };

    //发送请求,从日志看管道执行顺序
    var response = await httpClient.GetAsync("/api/Config/GetApiConfig");

    var content = await response.Content.ReadAsStringAsync();

    Console.WriteLine($"响应内容:{content}");
}
  • 动态组合
    整个管道链的组装,是非常灵活的,动态组装也不是难事!
/// <summary>
/// 管道构建器
/// </summary>
public class HttpMessageHandlerBuilder
{
    public List<DelegatingHandler> Handlers { get; set; }

    public HttpMessageHandlerBuilder()
    {
        this.Handlers = new List<DelegatingHandler>();
    }

    public HttpMessageHandlerBuilder(List<DelegatingHandler> handlers):base()
    {
        if(handlers != null)
        {
            this.Handlers = handlers;
        }
    }

    public void AddHttpMessageHandler(DelegatingHandler handler)
    {
        Handlers.Add(handler);
    }

    public void RemoveHttpMessageHandler(DelegatingHandler handler)
    {
        Handlers.Remove(handler);
    }

    public List<DelegatingHandler> FindHttpMessageHandler(DelegatingHandler handler)
    {
        return Handlers.Where(h => h.GetType() == handler.GetType()).ToList();
    }

    ///  <summary>
    ///  根据管道配置:创建一个新HttpClient实例
    ///  </summary>
    public HttpClient Build()
    {
        if(this.Handlers ==null ||  this.Handlers.Count == 0)
        {
            return new HttpClient();
        }

        //默认最终处理器
        HttpMessageHandler defaultLastHandler = new SocketsHttpHandler()
        {
            //自定义配置
            PooledConnectionLifetime = TimeSpan.FromSeconds(120),
        };

        //倒序组装:完成后执行顺序与注册顺序一致
        //拷贝份,省得多次调用有问题

        HttpMessageHandler currentHandler = new SocketsHttpHandler()
        {
            //自定义配置
            PooledConnectionLifetime = TimeSpan.FromSeconds(120),
        };

        var builderHandlers = this.Handlers.ToList<DelegatingHandler>();
        builderHandlers.Reverse();

        if(builderHandlers[0].InnerHandler != null)
        {
            currentHandler = builderHandlers[0].InnerHandler;
        }

        for (int i = 0; i < builderHandlers.Count; i++)
        {
            builderHandlers[i].InnerHandler = currentHandler;
            currentHandler = builderHandlers[i];
        }

        // 利用管道创建HttpClient
        var httpClient = new HttpClient(currentHandler);

        return httpClient;
    }
}

//构建管道
List<DelegatingHandler> handlers = new List<DelegatingHandler>()
{
    new TokenDelegatingHandler(),
};

//添加
handlers.Add(new LoggingHandler());
handlers.Add(new ExceptionDelegatingHandler());

var handlerBuilder = new HttpMessageHandlerBuilder(handlers);

//第一次:发送请求,从日志看管道执行顺序
var httpClient = handlerBuilder.Build();
httpClient.BaseAddress = webApiBaseUri;
webApiBaseUrl.Display();
var response = await httpClient.GetAsync("/api/Config/GetApiConfig");
var content = await response.Content.ReadAsStringAsync();

Console.WriteLine($"响应内容:{content}");

3、发送请求

和一般的请求,没什么区别。

4、管道执行顺序

  • 请求从最外层的 DelegatingHandler 开始,逐层进入内部处理器。
  • 响应则从最内层返回,经过每一层的 SendAsync 方法后返回给调用者。

执行下面示例,查看日志输出:

{   
    //组装管道
    //最后一个管道,必须是SocketsHttpHandler,或者默认的是SocketsHttpHandler
    var lastHandler = new SocketsHttpHandler()
    {
        UseCookies =  false,
        UseProxy = false,
    };

    var logPipeline = new LoggingHandler()
    {
        InnerHandler  = lastHandler
    };
    
    var exceptionHandler = new ExceptionDelegatingHandler()
    {
        InnerHandler = logPipeline
    };

    var tokenHandler = new TokenDelegatingHandler()
    {
        InnerHandler = exceptionHandler
    };

    var httpClient = new HttpClient(tokenHandler)
    {
        BaseAddress = webApiBaseUri
    };

    //发送请求,从日志看管道执行顺序
    var response = await httpClient.GetAsync("/api/Hello/Ping");

    var content = await response.Content.ReadAsStringAsync();

    Console.WriteLine($"响应内容:{content}");
}

5、使用 IHttpClientFactory 构建更复杂的管道(推荐)

小结

特性描述
HttpMessageHandler是 HttpClient 的核心处理单元:ml-citation{ref=“1” data=“citationList”}
DelegatingHandler可用于构建自定义中间件逻辑:ml-citation{ref=“1,5” data=“citationList”}
管道顺序请求按顺序进入,响应逆序返回:ml-citation{ref=“5,8” data=“citationList”}
IHttpClientFactory推荐用于生产环境,支持灵活配置:ml-citation{ref=“1,7” data=“citationList”}

通过添加更多 DelegatingHandler 实现缓存、重试、认证等!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bicijinlian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值