Files
MegghysAPI/Modules/ProxyManager.cs

178 lines
6.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Net;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Caching.Memory;
namespace MegghysAPI.Modules
{
/// <summary>
/// 代理IP管理器
/// </summary>
public static class ProxyManager
{
// 用于获取代理IP的客户端不使用代理
private static readonly HttpClient ProxyApiHttpClient = new();
// 缓存代理IP的响应
private static readonly MemoryCache ProxyResponseCache = new(new MemoryCacheOptions());
// 缓存配置了代理的HttpClient
private static readonly MemoryCache HttpClientCache = new(new MemoryCacheOptions());
private const string BaseUrl = "https://share.proxy.qg.net/get";
private const string DefaultKey = "9IK2T3XL";
private const string DefaultPassword = "04BC86C88CDB";
/// <summary>
/// 获取配置了代理的 HttpClient带缓存和自动释放功能
/// </summary>
/// <param name="proxyAddress">可选的代理地址。如果为空,则自动获取。</param>
/// <param name="key">代理用户名</param>
/// <param name="password">代理密码</param>
/// <returns>配置好代理的 HttpClient</returns>
public static async Task<HttpClient> GetHttpClientAsync(string? proxyAddress = null, string key = DefaultKey, string password = DefaultPassword)
{
// 如果未提供代理地址则从API获取
if (string.IsNullOrWhiteSpace(proxyAddress))
{
var proxyResponse = await GetProxyAsync(key: key);
var proxyIp = proxyResponse?.Data?.FirstOrDefault();
if (proxyIp?.Server == null || proxyIp.Deadline == null)
{
// 如果无法获取有效代理返回一个不带代理的普通HttpClient
return new HttpClient();
}
// 检查缓存中是否已有此代理的HttpClient
if (HttpClientCache.TryGetValue(proxyIp.Server, out HttpClient? cachedClient))
{
return cachedClient!;
}
// 创建新的HttpClient并配置缓存
var client = CreateConfiguredHttpClient(proxyIp.Server, key, password);
var deadline = DateTime.Parse(proxyIp.Deadline);
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(deadline)
.RegisterPostEvictionCallback((cacheKey, value, reason, state) =>
{
(value as HttpClient)?.Dispose();
});
HttpClientCache.Set(proxyIp.Server, client, cacheEntryOptions);
return client;
}
else
{
// 如果用户手动提供了代理地址,我们无法知道其过期时间,因此不进行缓存
return CreateConfiguredHttpClient(proxyAddress, key, password);
}
}
private static HttpClient CreateConfiguredHttpClient(string proxyAddress, string key, string password)
{
var handler = new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.All,
Proxy = new WebProxy(proxyAddress, false)
{
Credentials = new NetworkCredential(key, password)
},
UseProxy = true
};
var client = new HttpClient(handler, disposeHandler: true);
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36");
return client;
}
/// <summary>
/// 异步获取代理IP, 带缓存功能
/// </summary>
public static async Task<ProxyResponse?> GetProxyAsync(string key = DefaultKey, string? area = null, string? areaEx = null, int? isp = null, int? num = null, bool? distinct = null)
{
var queryParams = new Dictionary<string, string?>
{
{ "key", key },
{ "area", area },
{ "area_ex", areaEx },
{ "isp", isp?.ToString() },
{ "num", num?.ToString() },
{ "distinct", distinct?.ToString().ToLower() }
};
var queryString = string.Join("&", queryParams
.Where(kvp => !string.IsNullOrEmpty(kvp.Value))
.Select(kvp => $"{kvp.Key}={Uri.EscapeDataString(kvp.Value!)}"));
var cacheKey = $"ProxyResponse_{queryString}";
if (ProxyResponseCache.TryGetValue(cacheKey, out ProxyResponse? cachedResponse))
{
return cachedResponse;
}
var requestUrl = $"{BaseUrl}?{queryString}";
try
{
var response = await ProxyApiHttpClient.GetAsync(requestUrl);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var proxyResponse = JsonSerializer.Deserialize<ProxyResponse>(json);
if (proxyResponse?.Code == "SUCCESS" && proxyResponse.Data?.Any() == true)
{
// 使用代理的过期时间作为缓存的过期时间
var deadline = DateTime.Parse(proxyResponse.Data.First().Deadline!);
ProxyResponseCache.Set(cacheKey, proxyResponse, deadline);
}
return proxyResponse;
}
catch (HttpRequestException e)
{
Console.WriteLine($"请求代理IP时发生错误: {e.Message}");
return null;
}
}
}
/// <summary>
/// 代理IP接口响应
/// </summary>
public class ProxyResponse
{
[JsonPropertyName("code")]
public string? Code { get; set; }
[JsonPropertyName("data")]
public List<ProxyIp>? Data { get; set; }
[JsonPropertyName("request_id")]
public string? RequestId { get; set; }
}
/// <summary>
/// 代理IP信息
/// </summary>
public class ProxyIp
{
[JsonPropertyName("proxy_ip")]
public string? ProxyIpAddress { get; set; }
[JsonPropertyName("server")]
public string? Server { get; set; }
[JsonPropertyName("area")]
public string? Area { get; set; }
[JsonPropertyName("isp")]
public string? Isp { get; set; }
[JsonPropertyName("deadline")]
public string? Deadline { get; set; }
}
}