.Net HttpClient 使用代理功能

HttpClient 使用代理功能

实际开发中,HttpClient 通过代理访问目标服务器是常见的需求。

本文将全面介绍如何在 .NET 中配置 HttpClient 使用代理(Proxy)功能,包括基础使用方式、代码示例、以及与依赖注入结合的最佳实践。

注意:运行代码之前,先开启Fiddler Classic及其代理功能,充当代理服务器。

初始化

开启Fiddler Classic及其代理功能,充当代理服务器

在这里插入图片描述

导入初始终化笔记文件,并且执行一次

#!import "./Ini.ipynb"

//共享变量
var fiddlerProxyAddress = "127.0.0.1:8888";

🧩 什么是代理?

代理(Proxy)是一种中间服务器,用于转发客户端请求到目标服务器。它常用于以下目的:

  • 访问受限资源:企业内网中,通过代理服务器访问外部资源;
  • 提高安全性和隐私保护:代理可隐藏真实 IP 地址,保护目标服务器的隐私和数据安全;
  • 提高性能:代理可使用请求缓存和负载均衡等,减少目标服务器的压力,提高性能;
  • 方便调试、测试
    • 代理服务器可记录请求和响应信息,方便调试和测试;
    • Fiddler Classic等软件,默认是抓不到 HttpClient 的请求的,需要将其设置为代理服务器,才能抓取到 HttpClient 的请求;

在 .NET HttpClient 中,可以通过多种方式来设置代理服务器。

🛠️ 设置 HttpClient 代理

✅ 基本方式使用(无用户名密码)

{   
    // 设置 SocketsHttpHandler 使用代理
    var handler = new SocketsHttpHandler()
    {
        UseProxy = true,
        Proxy = new WebProxy(fiddlerProxyAddress),
    };

    // 创建 HttpClient,并且请求
    using (var client = new HttpClient(handler))
    {
        var response = await client.GetAsync("https://www.baidu.com");
        
        Console.WriteLine($"响应状态:{response.StatusCode}");
    }
}

执行上面的单元格,应该在fiddler classic 中,抓到请求包:可以查看和管理详细信息.

在这里插入图片描述

✅ 带用户名和密码的代理

{   
    // 设置 SocketsHttpHandler 使用代理
    var handler = new SocketsHttpHandler()
    {
        UseProxy = true,
        Proxy = new WebProxy(fiddlerProxyAddress)
        {
            //正式项目:机密数据一定要脱敏处理或者使用环境变量、机密管理器等手段
            //因为Fiddler代理服务器,没有用户凭据要求,所以此处随意填写的。需要的话,真实填写正确的用户凭据。
            Credentials = new NetworkCredential("username", "password"),
        },
    };

    // 创建 HttpClient,并且请求
    using (var client = new HttpClient(handler))
    {
        var response = await client.GetAsync("https://www.baidu.com");
        Console.WriteLine($"响应状态:{response.StatusCode}");
    }
}

📦 在IoC和工厂中使用 Proxy [推荐方式]

在 ASP.NET Core 或基于 IServiceCollection 的项目中,可以通过 UseSocketsHttpHandler 扩展方法,统一管理代理服务器配置。

还可以根据客户端的命名不同,进行不同的代理服务器配置!

//IoC或工厂中设置代理
{
   //IoC
   var services = new ServiceCollection();

   //默认命名客户端
   services
      .AddHttpClient<HttpClient>(string.Empty)
      .ConfigureHttpClient(client => 
      {
         client.BaseAddress = new Uri(webApiBaseUrl);
         client.Timeout = TimeSpan.FromSeconds(10);
      })

      //配置代理服务器
      .UseSocketsHttpHandler(handlerBuilder =>
      {
         handlerBuilder.Configure((handler,s) => 
         {
            handler.Proxy = new WebProxy(fiddlerProxyAddress);
         }); 
      });

   //发送请求
   var factory = services.BuildServiceProvider().GetRequiredService<IHttpClientFactory>();

   //正常请求
   var defaultClient = factory.CreateClient();
   var defaultContent = await defaultClient.GetStringAsync("api/hello/ping");
   Console.WriteLine($"正常请求,响应内容为: {defaultContent}");
}

🔄 动态切换代理服务器

要根据请求的不同(请求地址、请求方法、请求头、请求参数等),动态选择使用一同的代理服务器,可以使用Pipeline中间件,来管理。

///<summary>
/// 代理服务选择器中间件
/// 注意:此中间件会短路,必须设置为最后一个中间件
///</summary>
public class ProxySelectorLastHandler : DelegatingHandler
{
    /// <summary>
    /// 拦截请求,并动态设置代理
    /// 注意:会短路其它中间件,要放最后
    /// </summary>
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken ct)
    {
        Console.WriteLine("ProxySelectorLastHandler -> SendAsync -> Before");
        //动态选择示例
        var proxy = request.RequestUri.Host switch
        {
            string url when url.Contains("baidu") => new WebProxy("127.0.0.1:8888"),
            string url when url.Contains("qq") => new WebProxy("127.0.0.1:8888"),
            _ => null
        };

        InnerHandler = new SocketsHttpHandler
        {
            Proxy = proxy,
            UseProxy = proxy != null
        };


        //请求
        HttpResponseMessage response = await base.SendAsync(request, ct);

        Console.WriteLine("ProxySelectorLastHandler -> SendAsync -> After");

        return response;
    }
}

//日志中间件(管道类)
public class LoggerDelegatingHandler : DelegatingHandler
{
    protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Console.WriteLine("LoggerDelegatingHandler -> Send -> Before");

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

        Console.WriteLine("LoggerDelegatingHandler -> Send -> After");

        return response;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Console.WriteLine("LoggerDelegatingHandler -> SendAsync -> Before");

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

        Console.WriteLine("LoggerDelegatingHandler -> SendAsync -> After");

        return response;
    }
}

//使用:ProxySelectorLastHandler必须设置为最后一个中间件
{
    var handlerLink = new LoggerDelegatingHandler()
    {
        InnerHandler = new ProxySelectorLastHandler(),
    };

    var myClient = new HttpClient(handlerLink);
    var response = await myClient.GetAsync("https://www.qq.com");

    Console.WriteLine(response.StatusCode);
    Console.WriteLine("---------------------------------------------------");
}


// ProxySelectorLastHandler 不是最后一个的话,其它中间件无效(被短路)
{
    var handlerLink = new ProxySelectorLastHandler()
    {
        InnerHandler = new LoggerDelegatingHandler (),
    };

    var myClient = new HttpClient(handlerLink);
    var response = await myClient.GetAsync("https://www.qq.com");

    Console.WriteLine(response.StatusCode);
}

//注意看输出:后面的没有日志中间件的任何输出

🔐 HTTPS 代理信任问题

当使用 HTTPS 代理时,可能会遇到 SSL/TLS 证书不被信任的问题,尤其是在测试环境中使用自签名证书。

✅解决方法一:忽略证书验证(⚠️ 注意:仅用于开发环境)

var handler = new HttpClientHandler
{
    Proxy = new WebProxy(fiddlerProxyAddress),
    UseProxy = true,
    ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
};

using (var client = new HttpClient(handler))
{ 
    var response = await client.GetAsync("https://www.baidu.com");
    Console.WriteLine(response.StatusCode);
};

✅ 解决方法二:手动添加根证书信任

using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

var file = Environment.CurrentDirectory + "\\Assets\\FiddlerRoot.cer";
//Console.WriteLine(file);
var rootCert = System.Security.Cryptography.X509Certificates.X509CertificateLoader.LoadCertificateFromFile(file);

var handler = new HttpClientHandler
{
    Proxy = new WebProxy(fiddlerProxyAddress),
    UseProxy = true,
    ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
    {
        //return true;
        return chain.ChainElements.Any(x => x.Certificate.Thumbprint == cert.Thumbprint); // 验证证书链包含指定根证书
    }
};

using (var client = new HttpClient(handler))
{ 
    var response = await client.GetAsync("https://www.baidu.com");
    Console.WriteLine(response.StatusCode);
};

📌 总结

通过本文,你应该掌握了以下内容:

  • 如何在 HttpClient 中直接设置代理
  • 如何在依赖注入系统中配置全局代理
  • 如何通过环境变量设置代理
  • 如何验证代理是否生效
  • 实际使用中的注意事项
  • 动态选择及证书信任
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bicijinlian

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

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

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

打赏作者

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

抵扣说明:

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

余额充值