.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或工厂中设置代理
{//IoCvar 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 中直接设置代理
- 如何在依赖注入系统中配置全局代理
- 如何通过环境变量设置代理
- 如何验证代理是否生效
- 实际使用中的注意事项
- 动态选择及证书信任