学习本篇之前,对 HttpWebRequest 与 HttpWebResponse 不太熟悉的同学,请先学习《C# HTTP系列》。
应用程序中使用HTTP协议和服务器交互主要是进行数据的上传与下载,最常见的方式是通过 GET 和 POST 两种方式来完成。本篇介绍 C# HttpWebRequest 如何使用这两种方式来实现。
示例场景:
1 <form id="form1" runat="server" action="UserManageHandler.ashx" method="post" enctype="application/x-www-form-urlencoded"> 2 <div> 3 名称: <input type="text" name="uname" class="uname" /><br /> 4 邮件: <input type="text" name="email" class="email" /><br /> 5 <input type="submit" name="submit" value="提交" /> 6 </div> 7 </form>
1 using System; 2 using System.Web; 3 4 namespace SparkSoft.Platform.UI.WebForm.Test 5 { 6 public class UserManageHandler : IHttpHandler 7 { 8 9 public void ProcessRequest(HttpContext context) 10 { 11 context.Response.ContentType = "text/plain"; 12 13 string uname = context.Request["uname"]; 14 string email = context.Request["email"]; 15 16 context.Response.Write("提交结果如下:" + Environment.NewLine + 17 "名称:" + uname + Environment.NewLine + 18 "邮箱:" + email); 19 } 20 21 public bool IsReusable 22 { 23 get { return false; } 24 } 25 } 26 }
01 HttpWebRequest 提交数据方式1 GET方式
GET 方式通过在网络地址附加参数来完成数据的提交,
比如在地址 http://localhost:5000/Test/UserManageHandler.ashx?uname=zhangsan 中,前面部分 http://localhost:5000/Test/UserManageHandler.ashx 表示数据提交的网址,后面部分 uname=zhangsan 表示附加的参数,其中uname 表示一个键(key), zhangsan 表示这个键对应的值(value)。程序代码如下:
1 /// <summary> 2 /// 普通 GET 方式请求 3 /// </summary> 4 public void Request01_ByGet() 5 { 6 HttpWebRequest httpWebRequest = WebRequest.Create("http://localhost:5000/Test/UserManageHandler.ashx?uname=zhangsan") as HttpWebRequest; 7 httpWebRequest.Method = "GET"; 8 9 HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse; // 获取响应 10 if (httpWebResponse != null) 11 { 12 using (StreamReader sr = new StreamReader(httpWebResponse.GetResponseStream())) 13 { 14 string content = sr.ReadToEnd(); 15 } 16 17 httpWebResponse.Close(); 18 } 19 }
02 HttpWebRequest 提交数据方式2 POST方式
POST 方式通过在页面内容中填写参数的方法来完成数据的提交,
参数的格式和 GET 方式一样,是类似于 uname=zhangsan&email=123456@qq.com 这样的结构。程序代码如下:
1 /// <summary> 2 /// 普通 POST 方式请求 3 /// </summary> 4 public void Request02_ByPost() 5 { 6 string param = "uname=zhangsan&email=123456@qq.com"; //参数 7 byte[] paramBytes = Encoding.ASCII.GetBytes(param); //参数转化为 ASCII 码 8 9 HttpWebRequest httpWebRequest = WebRequest.Create("http://localhost:5000/Test/UserManageHandler.ashx") as HttpWebRequest; 10 httpWebRequest.Method = "POST"; 11 httpWebRequest.ContentType = "application/x-www-form-urlencoded"; 12 httpWebRequest.ContentLength = paramBytes.Length; 13 14 using (Stream reqStream = httpWebRequest.GetRequestStream()) 15 { 16 reqStream.Write(paramBytes, 0, paramBytes.Length); 17 } 18 19 HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse; // 获取响应 20 if (httpWebResponse != null) 21 { 22 using (StreamReader sr = new StreamReader(httpWebResponse.GetResponseStream())) 23 { 24 string content = sr.ReadToEnd(); 25 } 26 27 httpWebResponse.Close(); 28 } 29 }
如果提交的请求参数中包含中文,那么需要对其进行编码,让目标网站能够识别。
03 HttpWebRequest 提交数据方式3 GET方式提交中文数据
GET 方式通过在网络地址中附加参数来完成数据提交,对于中文的编码,常用的有 gb2312 和 utf-8 两种。下面以 gb2312 方式编码来说明,程序代码如下:
1 /// <summary> 2 /// 使用 GET 方式提交中文数据 3 /// </summary> 4 public void Request03_ByGet() 5 { 6 /*GET 方式通过在网络地址中附加参数来完成数据提交,对于中文的编码,常用的有 gb2312 和 utf8 两种。 7 * 由于无法告知对方提交数据的编码类型,所以编码方式要以对方的网站为标准。 8 * 常见的网站中, www.baidu.com (百度)的编码方式是 gb2312, www.google.com (谷歌)的编码方式是 utf8。 9 */ 10 Encoding myEncoding = Encoding.GetEncoding("gb2312"); //确定用哪种中文编码方式 11 string address = "http://www.baidu.com/s?" + HttpUtility.UrlEncode("参数一", myEncoding) + "=" + HttpUtility.UrlEncode("值一", myEncoding); //拼接数据提交的网址和经过中文编码后的中文参数 12 13 HttpWebRequest httpWebRequest = WebRequest.Create(address) as HttpWebRequest; 14 httpWebRequest.Method = "GET"; 15 16 HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse; // 获取响应 17 if (httpWebResponse != null) 18 { 19 using (StreamReader sr = new StreamReader(httpWebResponse.GetResponseStream())) 20 { 21 string content = sr.ReadToEnd(); 22 } 23 24 httpWebResponse.Close(); 25 } 26 }
在上面的程序代码中,我们以 GET 方式访问了网址 http://www.baidu.com/s ,传递了参数“参数一=值一”,由于无法告知对方提交数据的编码类型,所以编码方式要以对方的网站为标准。常见的网站中, www.baidu.com (百度)的编码方式是 gb2312, www.google.com (谷歌)的编码方式是 utf-8。
04 HttpWebRequest 提交数据方式4 POST方式提交中文数据
POST 方式通过在页面内容中填写参数的方法来完成数据的提交,由于提交的参数中可以说明使用的编码方式,所以理论上能获得更大的兼容性。下面以 gb2312 方式编码来说明,程序代码如下:
/// <summary> /// 使用 POST 方式提交中文数据 /// </summary> public void Request04_ByPost() { /* POST 方式通过在页面内容中填写参数的方法来完成数据的提交,由于提交的参数中可以说明使用的编码方式,所以理论上能获得更大的兼容性。*/ Encoding myEncoding = Encoding.GetEncoding("gb2312"); //确定用哪种中文编码方式 string param = HttpUtility.UrlEncode("参数一", myEncoding) + "=" + HttpUtility.UrlEncode("值一", myEncoding) + "&" + HttpUtility.UrlEncode("参数二", myEncoding) + "=" + HttpUtility.UrlEncode("值二", myEncoding); byte[] paramBytes = Encoding.ASCII.GetBytes(param); //参数转化为 ASCII 码 HttpWebRequest httpWebRequest = WebRequest.Create("https://www.baidu.com/") as HttpWebRequest; httpWebRequest.Method = "POST"; httpWebRequest.ContentType = "application/x-www-form-urlencoded;charset=gb2312"; httpWebRequest.ContentLength = paramBytes.Length; using (Stream reqStream = httpWebRequest.GetRequestStream()) { reqStream.Write(paramBytes, 0, paramBytes.Length); } HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse; // 获取响应 if (httpWebResponse != null) { using (StreamReader sr = new StreamReader(httpWebResponse.GetResponseStream())) { string content = sr.ReadToEnd(); } httpWebResponse.Close(); } }
以上列出了客户端程序使用HTTP协议与服务器交互的情况,常用的是 GET 和 POST 方式。现在流行的 WebService 也是通过 HTTP 协议来交互的,使用的是 POST 方法。与以上稍有所不同的是, WebService 提交的数据内容和接收到的数据内容都是使用了 XML 方式编码。所以, HttpWebRequest 也可以使用在调用 WebService 的场景下。
HttpWebRequest 请求通用方法
(不包含body数据)POST、GET请求
1 /// <summary> 2 /// HTTP-GET方法,(不包含body数据)。 3 /// 发送 HTTP 请求并返回来自 Internet 资源的响应(HTML代码) 4 /// </summary> 5 /// <param name="url">请求目标URL</param> 6 /// <returns>HTTP-GET的响应结果</returns> 7 public HttpResult Get(string url) 8 { 9 return Request(url, WebRequestMethods.Http.Get); 10 }
1 /// <summary> 2 /// HTTP-POST方法,(不包含body数据)。 3 /// 发送 HTTP 请求并返回来自 Internet 资源的响应(HTML代码) 4 /// </summary> 5 /// <param name="url">请求目标URL</param> 6 /// <returns>HTTP-POST的响应结果</returns> 7 public HttpResult Post(string url) 8 { 9 return Request(url, WebRequestMethods.Http.Post); 10 }
(包含文本的body数据)请求
1 /// <summary> 2 /// HTTP请求(包含JSON文本的body数据) 3 /// </summary> 4 /// <param name="url">请求目标URL</param> 5 /// <param name="data">主体数据(JSON文本)。如果参数中有中文,请使用合适的编码方式进行编码,例如:gb2312或者utf-8</param> 6 /// <param name="method">请求的方法。请使用 WebRequestMethods.Http 的枚举值</param> 7 /// <returns></returns> 8 public HttpResult UploadJson(string url, string data, string method = WebRequestMethods.Http.Post) 9 { 10 return Request(url, data, method, HttpContentType.APPLICATION_JSON); 11 }
1 /// <summary> 2 /// HTTP请求(包含普通文本的body数据) 3 /// </summary> 4 /// <param name="url">请求目标URL</param> 5 /// <param name="data">主体数据(普通文本)。如果参数中有中文,请使用合适的编码方式进行编码,例如:gb2312或者utf-8</param> 6 /// <param name="method">请求的方法。请使用 WebRequestMethods.Http 的枚举值</param> 7 /// <returns></returns> 8 public HttpResult UploadText(string url, string data, string method = WebRequestMethods.Http.Post) 9 { 10 return Request(url, data, method, HttpContentType.TEXT_PLAIN); 11 }
上面的4个方法调用了公用的业务方法,分别如下:
1 /// <summary> 2 /// HTTP请求,(不包含body数据)。 3 /// 发送 HTTP 请求并返回来自 Internet 资源的响应(HTML代码) 4 /// </summary> 5 /// <param name="url">请求目标URL</param> 6 /// <param name="method">请求的方法。请使用 WebRequestMethods.Http 的枚举值</param> 7 /// <returns>HTTP的响应结果</returns> 8 private HttpResult Request(string url, string method) 9 { 10 HttpResult httpResult = new HttpResult(); 11 HttpWebRequest httpWebRequest = null; 12 try 13 { 14 httpWebRequest = WebRequest.Create(url) as HttpWebRequest; 15 httpWebRequest.Method = method; 16 httpWebRequest.UserAgent = _userAgent; 17 httpWebRequest.AllowAutoRedirect = _allowAutoRedirect; 18 httpWebRequest.ServicePoint.Expect100Continue = false; 19 20 HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse; 21 if (httpWebResponse != null) 22 { 23 GetResponse(ref httpResult, httpWebResponse); 24 httpWebResponse.Close(); 25 } 26 } 27 catch (WebException webException) 28 { 29 GetWebExceptionResponse(ref httpResult, webException); 30 } 31 catch (Exception ex) 32 { 33 GetExceptionResponse(ref httpResult, ex, method, string.Empty); 34 } 35 finally 36 { 37 if (httpWebRequest != null) 38 { 39 httpWebRequest.Abort(); 40 } 41 } 42 43 return httpResult; 44 }
1 /// <summary> 2 /// HTTP请求(包含文本的body数据) 3 /// </summary> 4 /// <param name="url">请求目标URL</param> 5 /// <param name="data">主体数据(普通文本或者JSON文本)。如果参数中有中文,请使用合适的编码方式进行编码,例如:gb2312或者utf-8</param> 6 /// <param name="method">请求的方法。请使用 WebRequestMethods.Http 的枚举值</param> 7 /// <param name="contentType"><see langword="Content-type" /> HTTP 标头的值。请使用 ContentType 类的常量来获取</param> 8 /// <returns></returns> 9 private HttpResult Request(string url, string data, string method, string contentType) 10 { 11 HttpResult httpResult = new HttpResult(); 12 HttpWebRequest httpWebRequest = null; 13 14 try 15 { 16 httpWebRequest = WebRequest.Create(url) as HttpWebRequest; 17 httpWebRequest.Method = method; 18 httpWebRequest.Headers = HeaderCollection; 19 httpWebRequest.CookieContainer = CookieContainer; 20 httpWebRequest.ContentType = contentType;// 此属性的值存储在WebHeaderCollection中。如果设置了WebHeaderCollection,则属性值将丢失。所以放置在Headers 属性之后设置 21 httpWebRequest.UserAgent = _userAgent; 22 httpWebRequest.AllowAutoRedirect = _allowAutoRedirect; 23 httpWebRequest.ServicePoint.Expect100Continue = false; 24 25 if (data != null) 26 { 27 httpWebRequest.AllowWriteStreamBuffering = true; 28 using (Stream requestStream = httpWebRequest.GetRequestStream()) 29 { 30 requestStream.Write(EncodingType.GetBytes(data), 0, data.Length);//将请求参数写入请求流中 31 requestStream.Flush(); 32 } 33 } 34 35 HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse; 36 if (httpWebResponse != null) 37 { 38 GetResponse(ref httpResult, httpWebResponse); 39 httpWebResponse.Close(); 40 } 41 } 42 catch (WebException webException) 43 { 44 GetWebExceptionResponse(ref httpResult, webException); 45 } 46 catch (Exception ex) 47 { 48 GetExceptionResponse(ref httpResult, ex, method, contentType); 49 } 50 finally 51 { 52 if (httpWebRequest != null) 53 { 54 httpWebRequest.Abort(); 55 } 56 } 57 58 return httpResult; 59 }
其中2个Request方法中用到了其他的封装类,比如:HttpResult
1 /// <summary> 2 /// HTTP请求(GET,POST等)的响应返回消息 3 /// </summary> 4 public sealed class HttpResult 5 { 6 #region 字段 7 8 /// <summary> 9 /// HTTP 响应成功,即状态码为200 10 /// </summary> 11 public const string STATUS_SUCCESS = "success"; 12 13 /// <summary> 14 /// HTTP 响应失败 15 /// </summary> 16 public const string STATUS_FAIL = "fail"; 17 18 #endregion 19 20 #region 属性 21 22 /// <summary> 23 /// 获取或设置请求的响应状态,success 或者 fail。建议使用常量:HttpResult.STATUS_SUCCESS 与 HttpResult.STATUS_FAIL 24 /// </summary> 25 public string Status { get; set; } 26 27 /// <summary> 28 /// 获取或设置请求的响应状态描述 29 /// </summary> 30 public string StatusDescription { get; set; } 31 32 /// <summary> 33 /// 状态码。与 HttpWebResponse.StatusCode 完全相同 34 /// </summary> 35 public int? StatusCode { get; set; } 36 37 /// <summary> 38 /// 响应消息或错误文本 39 /// </summary> 40 public string Text { get; set; } 41 42 /// <summary> 43 /// 响应消息或错误(二进制格式) 44 /// </summary> 45 public byte[] Data { get; set; } 46 47 /// <summary> 48 /// 参考代码(用户自定义)。 49 /// 当 Status 等于 success 时,该值为 null; 50 /// 当 Status 等于 fail 时,该值为程序给出的用户自定义编码。 51 /// </summary> 52 public int? RefCode { get; set; } 53 54 /// <summary> 55 /// 附加信息(用户自定义内容,如Exception内容或者自定义提示信息)。 56 /// 当 Status 等于 success 时,该值为为空 57 /// 当 Status 等于 fail 时,该值为程序给出的用户自定义内容,如Exception内容或者自定义提示信息。 58 /// </summary> 59 public string RefText { get; set; } 60 61 /// <summary> 62 /// 获取或设置Http的请求响应。 63 /// </summary> 64 public HttpWebResponse HttpWebResponse { get; set; } 65 66 / <summary> 67 / 参考信息(从返回消息 WebResponse 的头部获取) 68 / </summary> 69 //public Dictionary<string, string> RefInfo { get; set; } 70 71 #endregion 72 73 #region 构造函数 74 /// <summary> 75 /// 初始化(所有成员默认值,需要后续赋值) 76 /// </summary> 77 public HttpResult() 78 { 79 Status = string.Empty; 80 StatusDescription = string.Empty; 81 StatusCode = null; 82 Text = string.Empty; 83 Data = null; 84 85 RefCode = null; 86 RefText = string.Empty; 87 //RefInfo = null; 88 89 HttpWebResponse = null; 90 } 91 92 #endregion 93 94 #region 方法 95 96 /// <summary> 97 /// 对象复制 98 /// </summary> 99 /// <param name="httpResultSource">要复制其内容的来源</param> 100 public void Shadow(HttpResult httpResultSource) 101 { 102 this.Status = httpResultSource.Status; 103 this.StatusDescription = string.Empty; 104 this.StatusCode = httpResultSource.StatusCode; 105 this.Text = httpResultSource.Text; 106 this.Data = httpResultSource.Data; 107 108 this.RefCode = httpResultSource.RefCode; 109 this.RefText += httpResultSource.RefText; 110 //this.RefInfo = httpResultSource.RefInfo; 111 112 this.HttpWebResponse = httpResultSource.HttpWebResponse; 113 } 114 115 /// <summary> 116 /// 转换为易读或便于打印的字符串格式 117 /// </summary> 118 /// <returns>便于打印和阅读的字符串</returns> 119 public override string ToString() 120 { 121 StringBuilder sb = new StringBuilder(); 122 123 sb.AppendFormat("Status:{0}", Status); 124 sb.AppendFormat("StatusCode:{0}", StatusCode); 125 sb.AppendFormat("StatusDescription:{0}", StatusDescription); 126 sb.AppendLine(); 127 128 if (!string.IsNullOrEmpty(Text)) 129 { 130 sb.AppendLine("text:"); 131 sb.AppendLine(Text); 132 } 133 134 if (Data != null) 135 { 136 sb.AppendLine("data:"); 137 int n = 1024; 138 if (Data.Length <= n) 139 { 140 sb.AppendLine(Encoding.UTF8.GetString(Data)); 141 } 142 else 143 { 144 sb.AppendLine(Encoding.UTF8.GetString(Data, 0, n)); 145 sb.AppendFormat("<--- TOO-LARGE-TO-DISPLAY --- TOTAL {0} BYTES --->", Data.Length); 146 sb.AppendLine(); 147 } 148 } 149 150 sb.AppendLine(); 151 152 sb.AppendFormat("ref-code:{0}", RefCode); 153 sb.AppendLine(); 154 155 if (!string.IsNullOrEmpty(RefText)) 156 { 157 sb.AppendLine("ref-text:"); 158 sb.AppendLine(RefText); 159 } 160 161 sb.AppendLine(); 162 163 return sb.ToString(); 164 } 165 166 #endregion 167 }