Files
29WxOrder/Source/Trade.Client/Helpers/HttpHelper.cs
T
2026-08-31 16:28:23 +08:00

1395 lines
39 KiB
C#
Raw 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;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Trade.Client.Helpers
{
/// <summary>
/// Http连接操作帮助类
/// </summary>
public class HttpHelper
{
public static HttpResult Get(string Url)
{
return new HttpHelper().GetHtml(new HttpItem
{
URL = Url,
Method = MethodType.Get,
});
}
public static HttpResult Post(string Url, string PostData)
{
return new HttpHelper().GetHtml(new HttpItem
{
URL = Url,
Method = MethodType.Get,
Postdata = PostData,
PostDataType = PostDataType.String
});
}
public static HttpResult Request(HttpItem item)
{
return new HttpHelper().GetHtml(item);
}
#region 异步方法
/// <summary>
/// 异步Get请求
/// </summary>
public static Task<HttpResult> GetAsync(string Url)
{
return new HttpHelper().GetHtmlAsync(new HttpItem
{
URL = Url,
Method = MethodType.Get,
});
}
/// <summary>
/// 异步Post请求
/// </summary>
public static Task<HttpResult> PostAsync(string Url, string PostData)
{
return new HttpHelper().GetHtmlAsync(new HttpItem
{
URL = Url,
Method = MethodType.Post,
Postdata = PostData,
PostDataType = PostDataType.String
});
}
/// <summary>
/// 异步执行请求
/// </summary>
public static Task<HttpResult> RequestAsync(HttpItem item)
{
return new HttpHelper().GetHtmlAsync(item);
}
/// <summary>
/// 异步下载文件
/// </summary>
/// <param name="address"></param>
public static async Task<byte[]> DownloadFileAsync(string address)
{
HttpItem item = new HttpItem();
item.URL = address;
item.Timeout = 3000;
item.ResultType = ResultType.Byte;
HttpHelper http = new HttpHelper();
HttpResult result = await http.GetHtmlAsync(item);
if (result.StatusCode == HttpStatusCode.OK)
{
return result.ResultByte;
}
else
{
throw new HttpListenerException((int)result.StatusCode, result.StatusDescription);
}
}
/// <summary>
/// 异步下载文件并保存到本地
/// </summary>
/// <param name="address"></param>
/// <param name="fileName"></param>
public static async Task<bool> DownloadFileAsync(string address, string fileName)
{
HttpItem item = new HttpItem();
item.URL = address;
item.Timeout = 3000;
item.ResultType = ResultType.Byte;
HttpHelper http = new HttpHelper();
HttpResult result = await http.GetHtmlAsync(item);
if (result.StatusCode == HttpStatusCode.OK)
{
using (FileStream fs = new FileStream(fileName, FileMode.CreateNew))
{
await fs.WriteAsync(result.ResultByte, 0, result.ResultByte.Length);
}
}
else
{
throw new HttpListenerException((int)result.StatusCode, result.StatusDescription);
}
return true;
}
#endregion
/// <summary>
/// 下载文件
/// </summary>
/// <param name="address"></param>
/// <param name="fileName"></param>
public static byte[] DownloadFile(string address)
{
HttpItem item = new HttpItem();
item.URL = address;
item.Timeout = 3000;
item.ResultType = ResultType.Byte;
HttpHelper http = new HttpHelper();
HttpResult result = http.GetHtml(item);
if (result.StatusCode == HttpStatusCode.OK)
{
return result.ResultByte;
}
else
{
throw new HttpListenerException((int)result.StatusCode, result.StatusDescription);
}
}
/// <summary>
/// 下载文件
/// </summary>
/// <param name="address"></param>
/// <param name="fileName"></param>
public static bool DownloadFile(string address, string fileName)
{
HttpItem item = new HttpItem();
item.URL = address;
item.Timeout = 3000;
item.ResultType = ResultType.Byte;
HttpHelper http = new HttpHelper();
HttpResult result = http.GetHtml(item);
if (result.StatusCode == HttpStatusCode.OK)
{
FileStream fs = new FileStream(fileName, FileMode.CreateNew);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(result.ResultByte);
fs.Close();
bw.Close();
}
else
{
throw new HttpListenerException((int)result.StatusCode, result.StatusDescription);
}
return true;
}
#region 预定义方法或者变更
//默认的编码
private Encoding encoding = Encoding.Default;
//Post数据编码
private Encoding postencoding = Encoding.Default;
//HttpWebRequest对象用来发起请求
private HttpWebRequest request = null;
//获取影响流的数据对象
private HttpWebResponse response = null;
/// <summary>
/// 根据相传入的数据,得到相应页面数据
/// </summary>
/// <param name="item">参数类对象</param>
/// <returns>返回HttpResult类型</returns>
public HttpResult GetHtml(HttpItem item)
{
//返回参数
HttpResult result = new HttpResult();
result.item = item;
try
{
//准备参数
SetRequest(item);
}
catch (Exception ex)
{
result = new HttpResult();
result.Cookie = string.Empty;
result.Header = null;
result.Html = ex.Message;
result.StatusDescription = "配置参数时出错:" + ex.Message;
return result;
}
try
{
#region 得到请求的response
using (response = (HttpWebResponse)request.GetResponse())
{
result.StatusCode = response.StatusCode;
result.StatusDescription = response.StatusDescription;
result.Header = response.Headers;
if (response.Cookies != null) result.CookieCollection = response.Cookies;
if (response.Headers["set-cookie"] != null) result.Cookie = response.Headers["set-cookie"];
MemoryStream _stream = new MemoryStream();
//GZIIP处理
if (response.ContentEncoding != null && response.ContentEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase))
{
//开始读取流并设置编码方式
//new GZipStream(response.GetResponseStream(), CompressionMode.Decompress).CopyTo(_stream, 10240);
//.net4.0以下写法
_stream = GetMemoryStream(new GZipStream(response.GetResponseStream(), CompressionMode.Decompress));
}
else
{
//开始读取流并设置编码方式
//response.GetResponseStream().CopyTo(_stream, 10240);
//.net4.0以下写法
_stream = GetMemoryStream(response.GetResponseStream());
}
//获取Byte
byte[] ResponseByte = _stream.ToArray();
_stream.Close();
if (ResponseByte != null & ResponseByte.Length > 0)
{
//是否返回Byte类型数据
if (item.ResultType == ResultType.Byte) result.ResultByte = ResponseByte;
//从这里开始我们要无视编码了
if (encoding == null)
{
Match meta = Regex.Match(Encoding.Default.GetString(ResponseByte), "<meta([^<]*)charset=([^<]*)[\"']", RegexOptions.IgnoreCase);
string c = (meta.Groups.Count > 1) ? meta.Groups[2].Value.ToLower().Trim() : string.Empty;
if (c.Length > 2)
{
try
{
if (c.IndexOf(" ") > 0) c = c.Substring(0, c.IndexOf(" "));
encoding = Encoding.GetEncoding(c.Replace("\"", "").Replace("'", "").Replace(";", "").Replace("iso-8859-1", "gbk").Trim());
}
catch
{
if (string.IsNullOrEmpty(response.CharacterSet)) encoding = Encoding.UTF8;
else encoding = Encoding.GetEncoding(response.CharacterSet);
}
}
else
{
if (string.IsNullOrEmpty(response.CharacterSet)) encoding = Encoding.UTF8;
else encoding = Encoding.GetEncoding(response.CharacterSet);
}
}
//得到返回的HTML
result.Html = encoding.GetString(ResponseByte);
}
else
{
//得到返回的HTML
result.Html = "本次请求并未返回任何数据";
}
}
#endregion
}
catch (WebException ex)
{
//这里是在发生异常时返回的错误信息
response = (HttpWebResponse)ex.Response;
result.Html = ex.Message;
if (response != null)
{
result.StatusCode = response.StatusCode;
result.StatusDescription = response.StatusDescription;
}
}
catch (Exception ex)
{
result.Html = ex.Message;
}
if (item.IsToLower) result.Html = result.Html.ToLower();
return result;
}
/// <summary>
/// 根据相传入的数据,异步得到相应页面数据
/// </summary>
/// <param name="item">参数类对象</param>
/// <returns>返回HttpResult类型</returns>
public async Task<HttpResult> GetHtmlAsync(HttpItem item)
{
//返回参数
HttpResult result = new HttpResult();
result.item = item;
try
{
//准备参数(不在此处同步写入Post数据,改为异步写入)
SetRequest(item, false);
await SetPostDataAsync(item);
}
catch (Exception ex)
{
result = new HttpResult();
result.Cookie = string.Empty;
result.Header = null;
result.Html = ex.Message;
result.StatusDescription = "配置参数时出错:" + ex.Message;
return result;
}
try
{
#region 得到请求的response
using (response = (HttpWebResponse)await request.GetResponseAsync())
{
result.StatusCode = response.StatusCode;
result.StatusDescription = response.StatusDescription;
result.Header = response.Headers;
if (response.Cookies != null) result.CookieCollection = response.Cookies;
if (response.Headers["set-cookie"] != null) result.Cookie = response.Headers["set-cookie"];
MemoryStream _stream;
//GZIIP处理
if (response.ContentEncoding != null && response.ContentEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase))
{
_stream = await GetMemoryStreamAsync(new GZipStream(response.GetResponseStream(), CompressionMode.Decompress));
}
else
{
_stream = await GetMemoryStreamAsync(response.GetResponseStream());
}
//获取Byte
byte[] ResponseByte = _stream.ToArray();
_stream.Close();
if (ResponseByte != null & ResponseByte.Length > 0)
{
//是否返回Byte类型数据
if (item.ResultType == ResultType.Byte) result.ResultByte = ResponseByte;
//从这里开始我们要无视编码了
if (encoding == null)
{
Match meta = Regex.Match(Encoding.Default.GetString(ResponseByte), "<meta([^<]*)charset=([^<]*)[\"']", RegexOptions.IgnoreCase);
string c = (meta.Groups.Count > 1) ? meta.Groups[2].Value.ToLower().Trim() : string.Empty;
if (c.Length > 2)
{
try
{
if (c.IndexOf(" ") > 0) c = c.Substring(0, c.IndexOf(" "));
encoding = Encoding.GetEncoding(c.Replace("\"", "").Replace("'", "").Replace(";", "").Replace("iso-8859-1", "gbk").Trim());
}
catch
{
if (string.IsNullOrEmpty(response.CharacterSet)) encoding = Encoding.UTF8;
else encoding = Encoding.GetEncoding(response.CharacterSet);
}
}
else
{
if (string.IsNullOrEmpty(response.CharacterSet)) encoding = Encoding.UTF8;
else encoding = Encoding.GetEncoding(response.CharacterSet);
}
}
//得到返回的HTML
result.Html = encoding.GetString(ResponseByte);
}
else
{
//得到返回的HTML
result.Html = "本次请求并未返回任何数据";
}
}
#endregion
}
catch (WebException ex)
{
//这里是在发生异常时返回的错误信息
response = (HttpWebResponse)ex.Response;
result.Html = ex.Message;
if (response != null)
{
result.StatusCode = response.StatusCode;
result.StatusDescription = response.StatusDescription;
}
}
catch (Exception ex)
{
result.Html = ex.Message;
}
if (item.IsToLower) result.Html = result.Html.ToLower();
return result;
}
/// <summary>
/// 4.0以下.net版本取数据使用
/// </summary>
/// <param name="streamResponse">流</param>
private MemoryStream GetMemoryStream(Stream streamResponse)
{
MemoryStream _stream = new MemoryStream();
int Length = 256;
Byte[] buffer = new Byte[Length];
int bytesRead = streamResponse.Read(buffer, 0, Length);
while (bytesRead > 0)
{
_stream.Write(buffer, 0, bytesRead);
bytesRead = streamResponse.Read(buffer, 0, Length);
}
return _stream;
}
/// <summary>
/// 异步读取流数据
/// </summary>
/// <param name="streamResponse">流</param>
private async Task<MemoryStream> GetMemoryStreamAsync(Stream streamResponse)
{
MemoryStream _stream = new MemoryStream();
await streamResponse.CopyToAsync(_stream);
return _stream;
}
/// <summary>
/// 为请求准备参数
/// </summary>
///<param name="item">参数列表</param>
private void SetRequest(HttpItem item)
{
SetRequest(item, true);
}
/// <summary>
/// 为请求准备参数
/// </summary>
/// <param name="item">参数列表</param>
/// <param name="setPostDataSync">是否在此处同步写入Post数据。异步调用时传入false,改由调用方await SetPostDataAsync写入</param>
private void SetRequest(HttpItem item, bool setPostDataSync)
{
// 验证证书
SetCer(item);
//设置Header参数
if (item.Header != null && item.Header.Count > 0) foreach (string key in item.Header.AllKeys)
{
request.Headers.Add(key, item.Header[key]);
}
// 设置代理
SetProxy(item);
if (item.ProtocolVersion != null) request.ProtocolVersion = item.ProtocolVersion;
request.ServicePoint.Expect100Continue = item.Expect100Continue;
//请求方式Get或者Post
request.Method = item.Method.ToString();
request.Timeout = item.Timeout;
request.KeepAlive = item.KeepAlive;
request.ReadWriteTimeout = item.ReadWriteTimeout;
//Accept
request.Accept = item.Accept;
//ContentType返回类型
request.ContentType = item.ContentType;
//UserAgent客户端的访问类型,包括浏览器版本和操作系统信息
request.UserAgent = item.UserAgent;
// 编码
encoding = item.Encoding;
//设置安全凭证
request.Credentials = item.ICredentials;
//设置Cookie
SetCookie(item);
//来源地址
request.Referer = item.Referer;
//是否执行跳转功能
request.AllowAutoRedirect = item.Allowautoredirect;
//设置JWT Bearer Token(Authorization: Bearer xxx)
SetJwt(item);
//设置Post数据
if (setPostDataSync) SetPostData(item);
//设置最大连接
if (item.Connectionlimit > 0) request.ServicePoint.ConnectionLimit = item.Connectionlimit;
}
/// <summary>
/// 设置JWT令牌到请求头(Authorization: Bearer {JwtToken})
/// 如果同时设置了AuthorizationHeaderScheme,则使用自定义的方案名代替Bearer
/// </summary>
/// <param name="item">Http参数</param>
private void SetJwt(HttpItem item)
{
if (!string.IsNullOrEmpty(item.JwtToken))
{
string scheme = string.IsNullOrEmpty(item.AuthorizationScheme) ? "Bearer" : item.AuthorizationScheme;
request.Headers[HttpRequestHeader.Authorization] = $"{scheme} {item.JwtToken}";
}
}
/// <summary>
/// 设置证书
/// </summary>
/// <param name="item"></param>
private void SetCer(HttpItem item)
{
if (!string.IsNullOrEmpty(item.CerPath))
{
//这一句一定要写在创建连接的前面。使用回调的方法进行证书验证。
ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);
//初始化对像,并设置请求的URL地址
request = (HttpWebRequest)WebRequest.Create(item.URL);
SetCerList(item);
//将证书添加到请求里
request.ClientCertificates.Add(new X509Certificate(item.CerPath));
}
else
{
//初始化对像,并设置请求的URL地址
request = (HttpWebRequest)WebRequest.Create(item.URL);
SetCerList(item);
}
}
/// <summary>
/// 设置多个证书
/// </summary>
/// <param name="item"></param>
private void SetCerList(HttpItem item)
{
if (item.ClentCertificates != null && item.ClentCertificates.Count > 0)
{
foreach (X509Certificate c in item.ClentCertificates)
{
request.ClientCertificates.Add(c);
}
}
}
/// <summary>
/// 设置Cookie
/// </summary>
/// <param name="item">Http参数</param>
private void SetCookie(HttpItem item)
{
if (!string.IsNullOrEmpty(item.Cookie)) request.Headers[HttpRequestHeader.Cookie] = item.Cookie;
//设置CookieCollection
if (item.ResultCookieType == ResultCookieType.CookieCollection)
{
request.CookieContainer = new CookieContainer();
if (item.CookieCollection != null && item.CookieCollection.Count > 0)
request.CookieContainer.Add(item.CookieCollection);
}
}
/// <summary>
/// 设置Post数据
/// </summary>
/// <param name="item">Http参数</param>
private void SetPostData(HttpItem item)
{
//验证在得到结果时是否有传入数据
if (request.Method.Trim().ToLower().Contains("post"))
{
byte[] buffer = GetPostBuffer(item);
if (buffer != null)
{
request.ContentLength = buffer.Length;
request.GetRequestStream().Write(buffer, 0, buffer.Length);
}
}
}
/// <summary>
/// 异步设置Post数据
/// </summary>
/// <param name="item">Http参数</param>
private async Task SetPostDataAsync(HttpItem item)
{
//验证在得到结果时是否有传入数据
if (request.Method.Trim().ToLower().Contains("post"))
{
byte[] buffer = GetPostBuffer(item);
if (buffer != null)
{
request.ContentLength = buffer.Length;
using (Stream stream = await request.GetRequestStreamAsync())
{
await stream.WriteAsync(buffer, 0, buffer.Length);
}
}
}
}
/// <summary>
/// 根据HttpItem的PostDataType构建要发送的Post数据字节数组
/// </summary>
/// <param name="item">Http参数</param>
private byte[] GetPostBuffer(HttpItem item)
{
if (item.PostEncoding != null)
{
postencoding = item.PostEncoding;
}
byte[] buffer = null;
//写入Byte类型
if (item.PostDataType == PostDataType.Byte && item.PostdataByte != null && item.PostdataByte.Length > 0)
{
//验证在得到结果时是否有传入数据
buffer = item.PostdataByte;
}//写入文件
else if (item.PostDataType == PostDataType.FilePath && !string.IsNullOrEmpty(item.Postdata))
{
using (StreamReader r = new StreamReader(item.Postdata, postencoding))
{
buffer = postencoding.GetBytes(r.ReadToEnd());
}
} //写入字符串
else if (!string.IsNullOrEmpty(item.Postdata))
{
buffer = postencoding.GetBytes(item.Postdata);
}
return buffer;
}
/// <summary>
/// 设置代理
/// </summary>
/// <param name="item">参数对象</param>
private void SetProxy(HttpItem item)
{
bool isIeProxy = item.ProxyIp.ToLower().Contains("ieproxy");
if (!string.IsNullOrEmpty(item.ProxyIp) && !isIeProxy)
{
//设置代理服务器
if (item.ProxyIp.Contains(":"))
{
string[] plist = item.ProxyIp.Split(':');
WebProxy myProxy = new WebProxy(plist[0].Trim(), Convert.ToInt32(plist[1].Trim()));
//建议连接
myProxy.Credentials = new NetworkCredential(item.ProxyUserName, item.ProxyPwd);
//给当前请求对象
request.Proxy = myProxy;
}
else
{
WebProxy myProxy = new WebProxy(item.ProxyIp, false);
//建议连接
myProxy.Credentials = new NetworkCredential(item.ProxyUserName, item.ProxyPwd);
//给当前请求对象
request.Proxy = myProxy;
}
}
else if (isIeProxy)
{
//设置为IE代理
}
else
{
request.Proxy = item.WebProxy;
}
}
/// <summary>
/// 回调验证证书问题
/// </summary>
/// <param name="sender">流对象</param>
/// <param name="certificate">证书</param>
/// <param name="chain">X509Chain</param>
/// <param name="errors">SslPolicyErrors</param>
/// <returns>bool</returns>
public bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { return true; }
#endregion
}
/// <summary>
/// Http请求参考类
/// </summary>
public class HttpItem
{
string _URL = string.Empty;
/// <summary>
/// 请求URL必须填写
/// </summary>
public string URL
{
get { return _URL; }
set { _URL = value; }
}
MethodType _Method = MethodType.Get;
/// <summary>
/// 请求方式默认为GET方式,当为POST方式时必须设置Postdata的值
public MethodType Method
/// </summary>
{
get { return _Method; }
set
{
_Method = value;
if (_Method == MethodType.Post)
{
_ContentType = "application/x-www-form-urlencoded";
}
}
}
int _Timeout = 100000;
/// <summary>
/// 默认请求超时时间
/// </summary>
public int Timeout
{
get { return _Timeout; }
set { _Timeout = value; }
}
int _ReadWriteTimeout = 30000;
/// <summary>
/// 默认写入Post数据超时间
/// </summary>
public int ReadWriteTimeout
{
get { return _ReadWriteTimeout; }
set { _ReadWriteTimeout = value; }
}
Boolean _KeepAlive = true;
/// <summary>
/// 获取或设置一个值,该值指示是否与 Internet 资源建立持久性连接默认为true。
/// </summary>
public Boolean KeepAlive
{
get { return _KeepAlive; }
set { _KeepAlive = value; }
}
string _Accept = "text/html, application/xhtml+xml, */*";
/// <summary>
/// 请求标头值 默认为text/html, application/xhtml+xml, */*
/// </summary>
public string Accept
{
get { return _Accept; }
set { _Accept = value; }
}
string _ContentType = "text/html";
/// <summary>
/// 请求返回类型默认 text/html
/// </summary>
public string ContentType
{
get { return _ContentType; }
set { _ContentType = value; }
}
string _UserAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)";
/// <summary>
/// 客户端访问信息默认Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
/// </summary>
public string UserAgent
{
get { return _UserAgent; }
set { _UserAgent = value; }
}
Encoding _Encoding = null;
/// <summary>
/// 返回数据编码默认为NUll,可以自动识别,一般为utf-8,gbk,gb2312
/// </summary>
public Encoding Encoding
{
get { return _Encoding; }
set { _Encoding = value; }
}
private PostDataType _PostDataType = PostDataType.String;
/// <summary>
/// Post的数据类型
/// </summary>
public PostDataType PostDataType
{
get { return _PostDataType; }
set { _PostDataType = value; }
}
string _Postdata = string.Empty;
/// <summary>
/// Post请求时要发送的字符串Post数据
/// </summary>
public string Postdata
{
get { return _Postdata; }
set { _Postdata = value; }
}
private byte[] _PostdataByte = null;
/// <summary>
/// Post请求时要发送的Byte类型的Post数据
/// </summary>
public byte[] PostdataByte
{
get { return _PostdataByte; }
set { _PostdataByte = value; }
}
private WebProxy _WebProxy;
/// <summary>
/// 设置代理对象,不想使用IE默认配置就设置为Null,而且不要设置ProxyIp
/// </summary>
public WebProxy WebProxy
{
get { return _WebProxy; }
set { _WebProxy = value; }
}
CookieCollection cookiecollection = null;
/// <summary>
/// Cookie对象集合
/// </summary>
public CookieCollection CookieCollection
{
get { return cookiecollection; }
set { cookiecollection = value; }
}
string _Cookie = string.Empty;
/// <summary>
/// 请求时的Cookie
/// </summary>
public string Cookie
{
get { return _Cookie; }
set { _Cookie = value; }
}
string _Referer = string.Empty;
/// <summary>
/// 来源地址,上次访问地址
/// </summary>
public string Referer
{
get { return _Referer; }
set { _Referer = value; }
}
string _CerPath = string.Empty;
/// <summary>
/// 证书绝对路径
/// </summary>
public string CerPath
{
get { return _CerPath; }
set { _CerPath = value; }
}
private Boolean isToLower = false;
/// <summary>
/// 是否设置为全文小写,默认为不转化
/// </summary>
public Boolean IsToLower
{
get { return isToLower; }
set { isToLower = value; }
}
private string _JwtToken = string.Empty;
/// <summary>
/// JWT令牌(不含Bearer前缀),设置后会自动添加到请求头 Authorization: Bearer {JwtToken}
/// </summary>
public string JwtToken
{
get { return _JwtToken; }
set { _JwtToken = value; }
}
private string _AuthorizationScheme = "Bearer";
/// <summary>
/// Authorization请求头的方案名,默认为Bearer,一般无需修改
/// </summary>
public string AuthorizationScheme
{
get { return _AuthorizationScheme; }
set { _AuthorizationScheme = value; }
}
private Boolean allowautoredirect = true;
/// <summary>
/// 支持跳转页面,查询结果将是跳转后的页面
/// </summary>
public Boolean Allowautoredirect
{
get { return allowautoredirect; }
set { allowautoredirect = value; }
}
private int connectionlimit = 1024;
/// <summary>
/// 最大连接数
/// </summary>
public int Connectionlimit
{
get { return connectionlimit; }
set { connectionlimit = value; }
}
private string proxyusername = string.Empty;
/// <summary>
/// 代理Proxy 服务器用户名
/// </summary>
public string ProxyUserName
{
get { return proxyusername; }
set { proxyusername = value; }
}
private string proxypwd = string.Empty;
/// <summary>
/// 代理 服务器密码
/// </summary>
public string ProxyPwd
{
get { return proxypwd; }
set { proxypwd = value; }
}
private string proxyip = string.Empty;
/// <summary>
/// 代理 服务IP ,如果要使用IE代理就设置为ieproxy
/// </summary>
public string ProxyIp
{
get { return proxyip; }
set { proxyip = value; }
}
private ResultType resulttype = ResultType.String;
/// <summary>
/// 设置返回类型String和Byte
/// </summary>
public ResultType ResultType
{
get { return resulttype; }
set { resulttype = value; }
}
private WebHeaderCollection header = new WebHeaderCollection();
/// <summary>
/// header对象
/// </summary>
public WebHeaderCollection Header
{
get { return header; }
set { header = value; }
}
private Version _ProtocolVersion;
/// <summary>
// 获取或设置用于请求的 HTTP 版本。返回结果:用于请求的 HTTP 版本。默认为 System.Net.HttpVersion.Version11。
/// </summary>
public Version ProtocolVersion
{
get { return _ProtocolVersion; }
set { _ProtocolVersion = value; }
}
private Boolean _expect100continue = true;
/// <summary>
/// 获取或设置一个 System.Boolean 值,该值确定是否使用 100-Continue 行为。如果 POST 请求需要 100-Continue 响应,则为 true;否则为 false。默认值为 true。
/// </summary>
public Boolean Expect100Continue
{
get { return _expect100continue; }
set { _expect100continue = value; }
}
private X509CertificateCollection _ClentCertificates;
/// <summary>
/// 设置509证书集合
/// </summary>
public X509CertificateCollection ClentCertificates
{
get { return _ClentCertificates; }
set { _ClentCertificates = value; }
}
private Encoding _PostEncoding;
/// <summary>
/// 设置或获取Post参数编码,默认的为Default编码
/// </summary>
public Encoding PostEncoding
{
get { return _PostEncoding; }
set { _PostEncoding = value; }
}
private ResultCookieType _ResultCookieType = ResultCookieType.String;
/// <summary>
/// Cookie返回类型,默认的是只返回字符串类型
/// </summary>
public ResultCookieType ResultCookieType
{
get { return _ResultCookieType; }
set { _ResultCookieType = value; }
}
private ICredentials _ICredentials = CredentialCache.DefaultCredentials;
/// <summary>
/// 获取或设置请求的身份验证信息。
/// </summary>
public ICredentials ICredentials
{
get { return _ICredentials; }
set { _ICredentials = value; }
}
}
/// <summary>
/// Http返回参数类
/// </summary>
public class HttpResult
{
private string _Cookie;
/// <summary>
/// Http请求返回的Cookie
/// </summary>
public string Cookie
{
get { return _Cookie; }
set { _Cookie = value; }
}
private CookieCollection _CookieCollection;
/// <summary>
/// Cookie对象集合
/// </summary>
public CookieCollection CookieCollection
{
get { return _CookieCollection; }
set { _CookieCollection = value; }
}
private string _Html;
/// <summary>
/// 返回的String类型数据 只有ResultType.String时才返回数据,其它情况为空
/// </summary>
public string Html
{
get { return _Html; }
set { _Html = value; }
}
private byte[] _ResultByte;
/// <summary>
/// 返回的Byte数组 只有ResultType.Byte时才返回数据,其它情况为空
/// </summary>
public byte[] ResultByte
{
get { return _ResultByte; }
set { _ResultByte = value; }
}
private WebHeaderCollection _Header;
/// <summary>
/// header对象
/// </summary>
public WebHeaderCollection Header
{
get { return _Header; }
set { _Header = value; }
}
private string _StatusDescription;
/// <summary>
/// 返回状态说明
/// </summary>
public string StatusDescription
{
get { return _StatusDescription; }
set { _StatusDescription = value; }
}
private HttpStatusCode _StatusCode;
/// <summary>
/// 返回状态码,默认为OK
/// </summary>
public HttpStatusCode StatusCode
{
get { return _StatusCode; }
set { _StatusCode = value; }
}
/// <summary>
/// HttpItem类,用于存储实际执行所用到的HttpItem对象
/// </summary>
public HttpItem item { get; set; }
}
/// <summary>
/// 返回类型
/// </summary>
public enum ResultType
{
/// <summary>
/// 表示只返回字符串 只有Html有数据
/// </summary>
String,
/// <summary>
/// 表示返回字符串和字节流 ResultByte和Html都有数据返回
/// </summary>
Byte
}
/// <summary>
/// Post的数据格式默认为string
/// </summary>
public enum PostDataType
{
/// <summary>
/// 字符串类型,这时编码Encoding可不设置
/// </summary>
String,
/// <summary>
/// Byte类型,需要设置PostdataByte参数的值编码Encoding可设置为空
/// </summary>
Byte,
/// <summary>
/// 传文件,Postdata必须设置为文件的绝对路径,必须设置Encoding的值
/// </summary>
FilePath
}
/// <summary>
/// Cookie返回类型
/// </summary>
public enum ResultCookieType
{
/// <summary>
/// 只返回字符串类型的Cookie
/// </summary>
String,
/// <summary>
/// CookieCollection格式的Cookie集合同时也返回String类型的cookie
/// </summary>
CookieCollection
}
public enum MethodType
{
Get,
Post
}
/// <summary>
/// 轻量级JWT帮助类:不依赖第三方NuGet包,支持HS256签名的生成与验证。
/// 生成的Token可直接赋值给 HttpItem.JwtToken,HttpHelper会自动添加到Authorization请求头。
/// </summary>
public static class JwtHelper
{
/// <summary>
/// 生成JWT(HS256签名)
/// </summary>
/// <param name="claims">自定义payload声明,例如 { "sub", "1001" }, { "name", "cow" }</param>
/// <param name="secret">签名密钥</param>
/// <param name="expireMinutes">过期时间(分钟),默认60分钟</param>
/// <returns>完整的JWT字符串:header.payload.signature</returns>
public static string GenerateToken(Dictionary<string, object> claims, string secret, int expireMinutes = 60)
{
if (claims == null) claims = new Dictionary<string, object>();
if (string.IsNullOrEmpty(secret)) throw new ArgumentException("secret不能为空", nameof(secret));
var header = new Dictionary<string, object>
{
{ "alg", "HS256" },
{ "typ", "JWT" }
};
var payload = new Dictionary<string, object>(claims);
long now = ToUnixTimeSeconds(DateTime.UtcNow);
if (!payload.ContainsKey("iat")) payload["iat"] = now;
if (!payload.ContainsKey("exp")) payload["exp"] = now + (expireMinutes * 60L);
string headerEncoded = Base64UrlEncode(Encoding.UTF8.GetBytes(ToJson(header)));
string payloadEncoded = Base64UrlEncode(Encoding.UTF8.GetBytes(ToJson(payload)));
string signingInput = headerEncoded + "." + payloadEncoded;
string signature = Sign(signingInput, secret);
return signingInput + "." + signature;
}
/// <summary>
/// 校验JWT签名和有效期,成功时返回payload中的声明
/// </summary>
/// <param name="token">JWT字符串</param>
/// <param name="secret">签名密钥</param>
/// <param name="claims">解析出的payload声明</param>
/// <param name="error">失败时的错误信息</param>
/// <returns>是否验证通过</returns>
public static bool ValidateToken(string token, string secret, out Dictionary<string, object> claims, out string error)
{
claims = null;
error = null;
if (string.IsNullOrEmpty(token))
{
error = "token不能为空";
return false;
}
string[] parts = token.Split('.');
if (parts.Length != 3)
{
error = "token格式不正确";
return false;
}
string signingInput = parts[0] + "." + parts[1];
string expectedSignature = Sign(signingInput, secret);
if (!SlowEquals(expectedSignature, parts[2]))
{
error = "签名校验失败";
return false;
}
string payloadJson;
try
{
payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(parts[1]));
claims = FromJson(payloadJson);
}
catch (Exception ex)
{
error = "payload解析失败:" + ex.Message;
return false;
}
if (claims.TryGetValue("exp", out object expObj))
{
if (long.TryParse(expObj.ToString(), out long exp))
{
if (ToUnixTimeSeconds(DateTime.UtcNow) > exp)
{
error = "token已过期";
return false;
}
}
}
return true;
}
/// <summary>
/// 不校验签名,仅解析出JWT的payload内容(用于调试查看Token内容)
/// </summary>
public static Dictionary<string, object> DecodeToken(string token)
{
if (string.IsNullOrEmpty(token)) throw new ArgumentException("token不能为空", nameof(token));
string[] parts = token.Split('.');
if (parts.Length != 3) throw new FormatException("token格式不正确");
string payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(parts[1]));
return FromJson(payloadJson);
}
private static string Sign(string input, string secret)
{
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret ?? string.Empty)))
{
byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(input));
return Base64UrlEncode(hash);
}
}
private static bool SlowEquals(string a, string b)
{
if (a == null || b == null) return false;
if (a.Length != b.Length) return false;
int diff = 0;
for (int i = 0; i < a.Length; i++)
{
diff |= a[i] ^ b[i];
}
return diff == 0;
}
private static long ToUnixTimeSeconds(DateTime utcDateTime)
{
return (long)(utcDateTime - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
}
private static string Base64UrlEncode(byte[] input)
{
return Convert.ToBase64String(input).TrimEnd('=').Replace('+', '-').Replace('/', '_');
}
private static byte[] Base64UrlDecode(string input)
{
string s = input.Replace('-', '+').Replace('_', '/');
switch (s.Length % 4)
{
case 2: s += "=="; break;
case 3: s += "="; break;
}
return Convert.FromBase64String(s);
}
/// <summary>
/// 极简JSON序列化,支持扁平的 Dictionary&lt;string, object&gt;,值类型支持 string/数值/bool/null
/// </summary>
private static string ToJson(Dictionary<string, object> dict)
{
var sb = new StringBuilder();
sb.Append('{');
bool first = true;
foreach (var kv in dict)
{
if (!first) sb.Append(',');
first = false;
sb.Append('"').Append(EscapeJson(kv.Key)).Append("\":");
AppendJsonValue(sb, kv.Value);
}
sb.Append('}');
return sb.ToString();
}
private static void AppendJsonValue(StringBuilder sb, object value)
{
if (value == null)
{
sb.Append("null");
}
else if (value is bool b)
{
sb.Append(b ? "true" : "false");
}
else if (value is sbyte || value is byte || value is short || value is ushort ||
value is int || value is uint || value is long || value is ulong ||
value is float || value is double || value is decimal)
{
sb.Append(Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture));
}
else
{
sb.Append('"').Append(EscapeJson(value.ToString())).Append('"');
}
}
private static string EscapeJson(string s)
{
return s.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n").Replace("\r", "\\r").Replace("\t", "\\t");
}
/// <summary>
/// 极简JSON反序列化,仅支持扁平对象(JWT payload通常满足此结构)
/// </summary>
private static Dictionary<string, object> FromJson(string json)
{
var result = new Dictionary<string, object>();
int i = 0;
SkipWhitespace(json, ref i);
if (i >= json.Length || json[i] != '{') throw new FormatException("不是合法的JSON对象");
i++;
SkipWhitespace(json, ref i);
if (i < json.Length && json[i] == '}') return result;
while (i < json.Length)
{
SkipWhitespace(json, ref i);
string key = ParseJsonString(json, ref i);
SkipWhitespace(json, ref i);
if (json[i] != ':') throw new FormatException("JSON格式错误,缺少':'");
i++;
SkipWhitespace(json, ref i);
object value = ParseJsonValue(json, ref i);
result[key] = value;
SkipWhitespace(json, ref i);
if (i < json.Length && json[i] == ',') { i++; continue; }
if (i < json.Length && json[i] == '}') { i++; break; }
break;
}
return result;
}
private static void SkipWhitespace(string s, ref int i)
{
while (i < s.Length && char.IsWhiteSpace(s[i])) i++;
}
private static string ParseJsonString(string s, ref int i)
{
if (s[i] != '"') throw new FormatException("JSON格式错误,期望字符串");
i++;
var sb = new StringBuilder();
while (s[i] != '"')
{
if (s[i] == '\\')
{
i++;
switch (s[i])
{
case 'n': sb.Append('\n'); break;
case 'r': sb.Append('\r'); break;
case 't': sb.Append('\t'); break;
case '"': sb.Append('"'); break;
case '\\': sb.Append('\\'); break;
default: sb.Append(s[i]); break;
}
}
else
{
sb.Append(s[i]);
}
i++;
}
i++; // 跳过结尾的引号
return sb.ToString();
}
private static object ParseJsonValue(string s, ref int i)
{
if (s[i] == '"') return ParseJsonString(s, ref i);
if (s[i] == 't' && s.Substring(i, 4) == "true") { i += 4; return true; }
if (s[i] == 'f' && s.Substring(i, 5) == "false") { i += 5; return false; }
if (s[i] == 'n' && s.Substring(i, 4) == "null") { i += 4; return null; }
int start = i;
while (i < s.Length && (char.IsDigit(s[i]) || s[i] == '-' || s[i] == '+' || s[i] == '.' || s[i] == 'e' || s[i] == 'E')) i++;
string numStr = s.Substring(start, i - start);
if (numStr.Contains(".") || numStr.Contains("e") || numStr.Contains("E"))
{
return double.Parse(numStr, System.Globalization.CultureInfo.InvariantCulture);
}
return long.Parse(numStr, System.Globalization.CultureInfo.InvariantCulture);
}
}
}