C# HttpWebRequest GET HTTP HTTPS 請求
作者:周公(zhoufoxcn)
原文:http://blog.csdn.net/zhoufoxcn
這個需求來自於我最近練手的一個項目,在項目中我需要將一些自己發表的和收藏整理的網文集中到一個地方存放,如果全部采用手工操作工作量大而且繁瑣,因此周公決定利用C#來實現。在很多地方都需要驗證用戶身份才可以進行下一步操作,這就免不了POST請求來登錄,在實際過程中發現有些網站登錄是HTTPS形式的,在解決過程中遇到了一些小問題,現在跟大家分享。
通用輔助類
下面是我編寫的一個輔助類,在這個類中采用了HttpWebRequest中發送GET/HTTP/HTTPS請求,因為有的時候需要獲取認證信息(如Cookie),所以返回的是HttpWebResponse對象,有了返回的HttpWebResponse實例,可以獲取登錄過程中返回的會話信息,也可以獲取響應流。
代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using System.DirectoryServices.Protocols; using System.ServiceModel.Security; using System.Net; using System.IO; using System.IO.Compression; using System.Text.RegularExpressions; namespace BaiduCang { /// <summary> /// 有關HTTP請求的輔助類 /// </summary> public class HttpWebResponseUtility { private static readonly string DefaultUserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"; /// <summary> /// 創建GET方式的HTTP請求 /// </summary> /// <param name="url">請求的URL</param> /// <param name="timeout">請求的超時時間</param> /// <param name="userAgent">請求的客戶端瀏覽器信息,可以為空</param> /// <param name="cookies">隨同HTTP請求發送的Cookie信息,如果不需要身份驗證可以為空</param> /// <returns></returns> public static HttpWebResponse CreateGetHttpResponse(string url,int? timeout, string userAgent,CookieCollection cookies) { if (string.IsNullOrEmpty(url)) { throw new ArgumentNullException("url"); } HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; request.Method = "GET"; request.UserAgent = DefaultUserAgent; if (!string.IsNullOrEmpty(userAgent)) { request.UserAgent = userAgent; } if (timeout.HasValue) { request.Timeout = timeout.Value; } if (cookies != null) { request.CookieContainer = new CookieContainer(); request.CookieContainer.Add(cookies); } return request.GetResponse() as HttpWebResponse; } /// <summary> /// 創建POST方式的HTTP請求 /// </summary> /// <param name="url">請求的URL</param> /// <param name="parameters">隨同請求POST的參數名稱及參數值字典</param> /// <param name="timeout">請求的超時時間</param> /// <param name="userAgent">請求的客戶端瀏覽器信息,可以為空</param> /// <param name="requestEncoding">發送HTTP請求時所用的編碼</param> /// <param name="cookies">隨同HTTP請求發送的Cookie信息,如果不需要身份驗證可以為空</param> /// <returns></returns> public static HttpWebResponse CreatePostHttpResponse(string url,IDictionary<string,string> parameters,int? timeout, string userAgent,Encoding requestEncoding,CookieCollection cookies) { if (string.IsNullOrEmpty(url)) { throw new ArgumentNullException("url"); } if(requestEncoding==null) { throw new ArgumentNullException("requestEncoding"); } HttpWebRequest request=null; //如果是發送HTTPS請求 if(url.StartsWith("https",StringComparison.OrdinalIgnoreCase)) { ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult); request = WebRequest.Create(url) as HttpWebRequest; request.ProtocolVersion=HttpVersion.Version10; } else { request = WebRequest.Create(url) as HttpWebRequest; } request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; if (!string.IsNullOrEmpty(userAgent)) { request.UserAgent = userAgent; } else { request.UserAgent = DefaultUserAgent; } if (timeout.HasValue) { request.Timeout = timeout.Value; } if (cookies != null) { request.CookieContainer = new CookieContainer(); request.CookieContainer.Add(cookies); } //如果需要POST數據 if(!(parameters==null||parameters.Count==0)) { StringBuilder buffer = new StringBuilder(); int i = 0; foreach (string key in parameters.Keys) { if (i > 0) { buffer.AppendFormat("&{0}={1}", key, parameters[key]); } else { buffer.AppendFormat("{0}={1}", key, parameters[key]); } i++; } byte[] data = requestEncoding.GetBytes(buffer.ToString()); using (Stream stream = request.GetRequestStream()) { stream.Write(data, 0, data.Length); } } return request.GetResponse() as HttpWebResponse; } private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { return true; //總是接受 } } }
從上面的代碼中可以看出POST數據到HTTP和HTTPS站點不同,POST數據到HTTPS站點的時候需要設置ServicePointManager類的ServerCertificateValidationCallback屬性,並且在POST到https://passport.baidu.com/?login時還需要將HttpWebResquest實例的ProtocolVersion屬性設置為HttpVersion.Version10(這個未驗證是否所有的HTTPS站點都需要設置),否則在調用GetResponse()方法時會拋出“基礎連接已經關閉: 連接被意外關閉。”的異常。
用法舉例
這個類用起來也很簡單:
(1)POST數據到HTTPS站點,用它來登錄百度:
string loginUrl = "https://passport.baidu.com/?login"; string userName = "userName"; string password = "password"; string tagUrl = "http://cang.baidu.com/"+userName+"/tags"; Encoding encoding = Encoding.GetEncoding("gb2312"); IDictionary<string, string> parameters = new Dictionary<string, string>(); parameters.Add("tpl", "fa"); parameters.Add("tpl_reg", "fa"); parameters.Add("u", tagUrl); parameters.Add("psp_tt", "0"); parameters.Add("username", userName); parameters.Add("password", password); parameters.Add("mem_pass", "1"); HttpWebResponse response = HttpWebResponseUtility.CreatePostHttpResponse(loginUrl, parameters, null, null, encoding, null); string cookieString = response.Headers["Set-Cookie"];
(2)發送GET請求到HTTP站點
在cookieString中包含了服務器端返回的會話信息數據,從中提取了之后可以設置Cookie下次登錄時帶上這個Cookie就可以以認證用戶的信息,假設我們已經登錄成功並且獲取了Cookie,那么發送GET請求的代碼如下:
string userName = "userName"; string tagUrl = "http://cang.baidu.com/"+userName+"/tags"; CookieCollection cookies = new CookieCollection();//如何從response.Headers["Set-Cookie"];中獲取並設置CookieCollection的代碼略 response = HttpWebResponseUtility.CreateGetHttpResponse(tagUrl, null, null, cookies);
(3)發送POST請求到HTTP站點
以登錄51CTO為例:
string loginUrl = "http://home.51cto.com/index.php?s=/Index/doLogin"; string userName = "userName"; string password = "password"; IDictionary<string, string> parameters = new Dictionary<string, string>(); parameters.Add("email", userName); parameters.Add("passwd", password); HttpWebResponse response = HttpWebResponseUtility.CreatePostHttpResponse(loginUrl, parameters, null, null, Encoding.UTF8, null);
在這里說句題外話,CSDN的登錄處理是由http://passport.csdn.net/ajax/accounthandler.ashx這個Handler來處理的。
總結
在本文只是講解了在C#中發送請求到HTTP和HTTPS的用法,分GET/POST兩種方式,為減少一些繁瑣和機械的編碼,周公將其封裝為一個類,發送數據之后返回HttpWebResponse對象實例,利用這個實例我們可以獲取服務器端返回的Cookie以便用認證用戶的身份繼續發送請求,或者讀取服務器端響應的內容,不過在讀取響應內容時要注意響應格式和編碼,本來在這個類中還有讀取HTML和WML內容的方法(包括服務器使用壓縮方式傳輸的數據),但限於篇幅和其它方面的原因,此處省略掉了。如有機會,在以后的文章中會繼續講述這方面的內容。