当前位置: 首页 > java >正文

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

相关文章:

  • Leetcode (力扣)做题记录 hot100(62,64,287,108)
  • C#调用C++dll 过程记录
  • 技术债务积累,如何进行有效管理
  • C++ 日志输出(宏定义)
  • 无人机数据处理与特征提取技术分析!
  • 劫持__security_check_cookie
  • 入门OpenTelemetry——部署OpenTelemetry
  • 分布式1(cap base理论 锁 事务 幂等性 rpc)
  • 微信小程序之将轮播图设计为组件
  • “强强联手,智启未来”凯创未来与绿算技术共筑高端智能家居及智能照明领域新生态
  • 【Alist+RaiDrive挂载网盘到本地磁盘】
  • 面向对象设计模式之代理模式详解
  • 如何查看SD卡存储扇区分配表?有什么不同之处
  • 远程连接电脑的方法?异地远程桌面连接和三方软件实现
  • Java 重试机制详解
  • QT之QComboBox组件
  • 软考 系统架构设计师系列知识点之杂项集萃(59)
  • 【springcloud学习(dalston.sr1)】Eureka单个服务端的搭建(含源代码)(三)
  • Python 常用模块(八):logging模块
  • 基于GpuGeek平台的深度学习项目
  • Keil5 MDK 安装教程
  • LeetCode 热题 100 35.搜索插入位置
  • python打包exe报错:处理文件时错误:Excel xlsx file; not supported
  • iOS Safari调试教程
  • vue使用路由技术实现登录成功后跳转到首页
  • 【Vue 3 + Vue Router 4】如何正确重置路由实例(resetRouter)——避免“VueRouter is not defined”错误
  • 数据结构与算法:状压dp
  • 反向传播算法——矩阵形式递推公式——ReLU传递函数
  • 如何保证RabbitMQ消息的顺序性?
  • 简单易懂的JavaScript中的this指针