WebUtils.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.IO.Compression;
  5. using System.Net;
  6. using System.Net.Security;
  7. using System.Security.Cryptography.X509Certificates;
  8. using System.Text;
  9. using System.Web;
  10. namespace Top.Api.Util
  11. {
  12. /// <summary>
  13. /// 网络工具类。
  14. /// </summary>
  15. public sealed class WebUtils
  16. {
  17. private int _timeout = 20000;
  18. private int _readWriteTimeout = 60000;
  19. private bool _ignoreSSLCheck = true;
  20. private bool _disableWebProxy = false;
  21. public WebProxy Proxy { get; set; } = null;
  22. /// <summary>
  23. /// 等待请求开始返回的超时时间
  24. /// </summary>
  25. public int Timeout
  26. {
  27. get { return this._timeout; }
  28. set { this._timeout = value; }
  29. }
  30. /// <summary>
  31. /// 等待读取数据完成的超时时间
  32. /// </summary>
  33. public int ReadWriteTimeout
  34. {
  35. get { return this._readWriteTimeout; }
  36. set { this._readWriteTimeout = value; }
  37. }
  38. /// <summary>
  39. /// 是否忽略SSL检查
  40. /// </summary>
  41. public bool IgnoreSSLCheck
  42. {
  43. get { return this._ignoreSSLCheck; }
  44. set { this._ignoreSSLCheck = value; }
  45. }
  46. /// <summary>
  47. /// 是否禁用本地代理
  48. /// </summary>
  49. public bool DisableWebProxy
  50. {
  51. get { return this._disableWebProxy; }
  52. set { this._disableWebProxy = value; }
  53. }
  54. /// <summary>
  55. /// 执行HTTP POST请求。
  56. /// </summary>
  57. /// <param name="url">请求地址</param>
  58. /// <param name="textParams">请求文本参数</param>
  59. /// <returns>HTTP响应</returns>
  60. public string DoPost(string url, IDictionary<string, string> textParams)
  61. {
  62. return DoPost(url, textParams, null);
  63. }
  64. /// <summary>
  65. /// 执行HTTP POST请求。
  66. /// </summary>
  67. /// <param name="url">请求地址</param>
  68. /// <param name="textParams">请求文本参数</param>
  69. /// <param name="headerParams">请求头部参数</param>
  70. /// <returns>HTTP响应</returns>
  71. public string DoPost(string url, IDictionary<string, string> textParams, IDictionary<string, string> headerParams)
  72. {
  73. HttpWebRequest req = GetWebRequest(url, "POST", headerParams);
  74. req.ContentType = "application/x-www-form-urlencoded;charset=utf-8";
  75. byte[] postData = Encoding.UTF8.GetBytes(BuildQuery(textParams));
  76. System.IO.Stream reqStream = req.GetRequestStream();
  77. reqStream.Write(postData, 0, postData.Length);
  78. reqStream.Close();
  79. HttpWebResponse rsp = (HttpWebResponse)req.GetResponse();
  80. Encoding encoding = GetResponseEncoding(rsp);
  81. return GetResponseAsString(rsp, encoding);
  82. }
  83. /// <summary>
  84. /// 执行HTTP GET请求。
  85. /// </summary>
  86. /// <param name="url">请求地址</param>
  87. /// <param name="textParams">请求文本参数</param>
  88. /// <returns>HTTP响应</returns>
  89. public string DoGet(string url, IDictionary<string, string> textParams)
  90. {
  91. return DoGet(url, textParams, null);
  92. }
  93. /// <summary>
  94. /// 执行HTTP GET请求。
  95. /// </summary>
  96. /// <param name="url">请求地址</param>
  97. /// <param name="textParams">请求文本参数</param>
  98. /// <param name="headerParams">请求头部参数</param>
  99. /// <returns>HTTP响应</returns>
  100. public string DoGet(string url, IDictionary<string, string> textParams, IDictionary<string, string> headerParams)
  101. {
  102. if (textParams != null && textParams.Count > 0)
  103. {
  104. url = BuildRequestUrl(url, textParams);
  105. }
  106. HttpWebRequest req = GetWebRequest(url, "GET", headerParams);
  107. req.ContentType = "application/x-www-form-urlencoded;charset=gbk";
  108. HttpWebResponse rsp = (HttpWebResponse)req.GetResponse();
  109. Encoding encoding = GetResponseEncoding(rsp);
  110. return GetResponseAsString(rsp, encoding);
  111. }
  112. /// <summary>
  113. /// 执行带文件上传的HTTP POST请求。
  114. /// </summary>
  115. /// <param name="url">请求地址</param>
  116. /// <param name="textParams">请求文本参数</param>
  117. /// <param name="fileParams">请求文件参数</param>
  118. /// <param name="headerParams">请求头部参数</param>
  119. /// <returns>HTTP响应</returns>
  120. public string DoPost(string url, IDictionary<string, string> textParams, IDictionary<string, FileItem> fileParams, IDictionary<string, string> headerParams)
  121. {
  122. // 如果没有文件参数,则走普通POST请求
  123. if (fileParams == null || fileParams.Count == 0)
  124. {
  125. return DoPost(url, textParams, headerParams);
  126. }
  127. string boundary = DateTime.Now.Ticks.ToString("X"); // 随机分隔线
  128. HttpWebRequest req = GetWebRequest(url, "POST", headerParams);
  129. req.ContentType = "multipart/form-data;charset=utf-8;boundary=" + boundary;
  130. System.IO.Stream reqStream = req.GetRequestStream();
  131. byte[] itemBoundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");
  132. byte[] endBoundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
  133. if (textParams != null)
  134. {
  135. // 组装文本请求参数
  136. string textTemplate = "Content-Disposition:form-data;name=\"{0}\"\r\nContent-Type:text/plain\r\n\r\n{1}";
  137. foreach (KeyValuePair<string, string> kv in textParams)
  138. {
  139. string textEntry = string.Format(textTemplate, kv.Key, kv.Value);
  140. byte[] itemBytes = Encoding.UTF8.GetBytes(textEntry);
  141. reqStream.Write(itemBoundaryBytes, 0, itemBoundaryBytes.Length);
  142. reqStream.Write(itemBytes, 0, itemBytes.Length);
  143. }
  144. }
  145. // 组装文件请求参数
  146. string fileTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type:{2}\r\n\r\n";
  147. foreach (KeyValuePair<string, FileItem> kv in fileParams)
  148. {
  149. string key = kv.Key;
  150. FileItem fileItem = kv.Value;
  151. if (!fileItem.IsValid())
  152. {
  153. throw new ArgumentException("FileItem is invalid");
  154. }
  155. string fileEntry = string.Format(fileTemplate, key, fileItem.GetFileName(), fileItem.GetMimeType());
  156. byte[] itemBytes = Encoding.UTF8.GetBytes(fileEntry);
  157. reqStream.Write(itemBoundaryBytes, 0, itemBoundaryBytes.Length);
  158. reqStream.Write(itemBytes, 0, itemBytes.Length);
  159. fileItem.Write(reqStream);
  160. }
  161. reqStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length);
  162. reqStream.Close();
  163. HttpWebResponse rsp = (HttpWebResponse)req.GetResponse();
  164. Encoding encoding = GetResponseEncoding(rsp);
  165. return GetResponseAsString(rsp, encoding);
  166. }
  167. /// <summary>
  168. /// 执行带body体的POST请求。
  169. /// </summary>
  170. /// <param name="url">请求地址,含URL参数</param>
  171. /// <param name="body">请求body体字节流</param>
  172. /// <param name="contentType">body内容类型</param>
  173. /// <param name="headerParams">请求头部参数</param>
  174. /// <returns>HTTP响应</returns>
  175. public string DoPost(string url, byte[] body, string contentType, IDictionary<string, string> headerParams)
  176. {
  177. HttpWebRequest req = GetWebRequest(url, "POST", headerParams);
  178. req.ContentType = contentType;
  179. if (body != null)
  180. {
  181. System.IO.Stream reqStream = req.GetRequestStream();
  182. reqStream.Write(body, 0, body.Length);
  183. reqStream.Close();
  184. }
  185. HttpWebResponse rsp = (HttpWebResponse)req.GetResponse();
  186. Encoding encoding = GetResponseEncoding(rsp);
  187. return GetResponseAsString(rsp, encoding);
  188. }
  189. /// <summary>
  190. /// 调用请求
  191. /// content type: application/json
  192. /// </summary>
  193. /// <returns>The post with json.</returns>
  194. /// <param name="url">URL.</param>
  195. /// <param name="textParams">Text parameters.</param>
  196. /// <param name="headerParams">Header parameters.</param>
  197. public string DoPostWithJson(string url, IDictionary<string, Object> textParams, IDictionary<string, string> headerParams)
  198. {
  199. HttpWebRequest req = GetWebRequest(url, "POST", headerParams);
  200. req.ContentType = "application/json;charset=utf-8";
  201. String body = TopUtils.ObjectToJson(textParams, new FastJSON.JSONParameters() { UseApiNamingStyle = false, UseExtensions = false, SerializeNullValues = false });
  202. byte[] postData = Encoding.UTF8.GetBytes(body);
  203. System.IO.Stream reqStream = req.GetRequestStream();
  204. reqStream.Write(postData, 0, postData.Length);
  205. reqStream.Close();
  206. HttpWebResponse rsp = (HttpWebResponse)req.GetResponse();
  207. Encoding encoding = GetResponseEncoding(rsp);
  208. return GetResponseAsString(rsp, encoding);
  209. }
  210. public HttpWebRequest GetWebRequest(string url, string method, IDictionary<string, string> headerParams)
  211. {
  212. HttpWebRequest req = null;
  213. if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
  214. {
  215. if (this._ignoreSSLCheck)
  216. {
  217. ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(TrustAllValidationCallback);
  218. }
  219. req = (HttpWebRequest)WebRequest.CreateDefault(new Uri(url));
  220. }
  221. else
  222. {
  223. req = (HttpWebRequest)WebRequest.Create(url);
  224. }
  225. //if (this._disableWebProxy)
  226. //{
  227. // req.Proxy = null;
  228. //}
  229. if (Proxy != null)
  230. {
  231. req.Proxy = Proxy;
  232. req.UseDefaultCredentials = false;
  233. }
  234. if (headerParams != null && headerParams.Count > 0)
  235. {
  236. foreach (string key in headerParams.Keys)
  237. {
  238. req.Headers.Add(key, headerParams[key]);
  239. }
  240. }
  241. req.ServicePoint.Expect100Continue = false;
  242. req.Method = method;
  243. req.KeepAlive = true;
  244. req.UserAgent = "top-sdk-net";
  245. req.Accept = "text/xml,text/javascript";
  246. req.Timeout = this._timeout;
  247. req.ReadWriteTimeout = this._readWriteTimeout;
  248. return req;
  249. }
  250. /// <summary>
  251. /// 把响应流转换为文本。
  252. /// </summary>
  253. /// <param name="rsp">响应流对象</param>
  254. /// <param name="encoding">编码方式</param>
  255. /// <returns>响应文本</returns>
  256. public string GetResponseAsString(HttpWebResponse rsp, Encoding encoding)
  257. {
  258. Stream stream = null;
  259. StreamReader reader = null;
  260. try
  261. {
  262. // 以字符流的方式读取HTTP响应
  263. stream = rsp.GetResponseStream();
  264. if (Constants.CONTENT_ENCODING_GZIP.Equals(rsp.ContentEncoding, StringComparison.OrdinalIgnoreCase))
  265. {
  266. stream = new GZipStream(stream, CompressionMode.Decompress);
  267. }
  268. reader = new StreamReader(stream, encoding);
  269. return reader.ReadToEnd();
  270. }
  271. finally
  272. {
  273. // 释放资源
  274. if (reader != null) reader.Close();
  275. if (stream != null) stream.Close();
  276. if (rsp != null) rsp.Close();
  277. }
  278. }
  279. /// <summary>
  280. /// 组装含参数的请求URL。
  281. /// </summary>
  282. /// <param name="url">请求地址</param>
  283. /// <param name="parameters">请求参数映射</param>
  284. /// <returns>带参数的请求URL</returns>
  285. public static string BuildRequestUrl(string url, IDictionary<string, string> parameters)
  286. {
  287. if (parameters != null && parameters.Count > 0)
  288. {
  289. return BuildRequestUrl(url, BuildQuery(parameters));
  290. }
  291. return url;
  292. }
  293. /// <summary>
  294. /// 组装含参数的请求URL。
  295. /// </summary>
  296. /// <param name="url">请求地址</param>
  297. /// <param name="queries">一个或多个经过URL编码后的请求参数串</param>
  298. /// <returns>带参数的请求URL</returns>
  299. public static string BuildRequestUrl(string url, params string[] queries)
  300. {
  301. if (queries == null || queries.Length == 0)
  302. {
  303. return url;
  304. }
  305. StringBuilder newUrl = new StringBuilder(url);
  306. bool hasQuery = url.Contains("?");
  307. bool hasPrepend = url.EndsWith("?") || url.EndsWith("&");
  308. foreach (string query in queries)
  309. {
  310. if (!string.IsNullOrEmpty(query))
  311. {
  312. if (!hasPrepend)
  313. {
  314. if (hasQuery)
  315. {
  316. newUrl.Append("&");
  317. }
  318. else
  319. {
  320. newUrl.Append("?");
  321. hasQuery = true;
  322. }
  323. }
  324. newUrl.Append(query);
  325. hasPrepend = false;
  326. }
  327. }
  328. return newUrl.ToString();
  329. }
  330. /// <summary>
  331. /// 组装普通文本请求参数。
  332. /// </summary>
  333. /// <param name="parameters">Key-Value形式请求参数字典</param>
  334. /// <returns>URL编码后的请求数据</returns>
  335. public static string BuildQuery(IDictionary<string, string> parameters)
  336. {
  337. if (parameters == null || parameters.Count == 0)
  338. {
  339. return null;
  340. }
  341. StringBuilder query = new StringBuilder();
  342. bool hasParam = false;
  343. foreach (KeyValuePair<string, string> kv in parameters)
  344. {
  345. string name = kv.Key;
  346. string value = kv.Value;
  347. // 忽略参数名或参数值为空的参数
  348. if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))
  349. {
  350. if (hasParam)
  351. {
  352. query.Append("&");
  353. }
  354. query.Append(name);
  355. query.Append("=");
  356. query.Append(HttpUtility.UrlEncode(value, Encoding.UTF8));
  357. hasParam = true;
  358. }
  359. }
  360. return query.ToString();
  361. }
  362. private Encoding GetResponseEncoding(HttpWebResponse rsp)
  363. {
  364. string charset = rsp.CharacterSet;
  365. if (string.IsNullOrEmpty(charset))
  366. {
  367. charset = Constants.CHARSET_UTF8;
  368. }
  369. return Encoding.GetEncoding(charset);
  370. }
  371. private static bool TrustAllValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
  372. {
  373. return true; // 忽略SSL证书检查
  374. }
  375. }
  376. }