理解并使用.NET 4.5中的HttpClient
背景
在平时工作中我偶尔会写一些脚本监控HTTP接口的健康状况,基本上都是发送HTTP GET或HTTP POST请求,然后检测响应内容。但一直用的是WebClient和HttpWebRequest,虽然用它们也能进行异步请求(可参考我分享的代码:C#异步GET的3个例子),但总感觉那样用起来不太自然。(关于测试大量页面链接,Stackoverflow的开发人员有一篇相关文章:Testing 3 Million Hyperlinks, Lessons Learned)
网上搜索后发现.NET 4.5引入的HttpClient库能够完成异步HTTP请求。于是结合多方资料总结下HttpClient的特性和使用。本文属于阅读笔记性质博客!
准备工作
在VS2012(VS2010应该也可以)中新建一个控制台应用,然后添加对System.Net和System.Net.Http程序集的引用。
HttpClient介绍
HttpClient是.NET4.5引入的一个HTTP客户端库,其命名空间为System.Net.Http。.NET 4.5之前我们可能使用WebClient和HttpWebRequest来达到相同目的。但是有几点值得关注:
- 可以使用单个HttpClient实例发任意数目的请求
- 一个HttpClient实例不会跟某个HTTP服务器或主机绑定,也就是说我们可以用一个实例同时给www.a.com和www.b.com发请求
- 可以继承HttpClient达到定制目的
- HttpClient利用了最新的面向任务模式,使得处理异步请求非常容易
异步HTTP GET
下面是一个使用HttpClient进行HTTP GET请求数据的例子:
查看文本打印
- using System;
- using System.Net.Http;
- namespace HttpClientProject
- {
- class HttpClientDemo
- {
- private const string Uri = "http://api.worldbank.org/countries?format=json";
- static void Main(string[] args)
- {
- HttpClient httpClient = new HttpClient();
- // 创建一个异步GET请求,当请求返回时继续处理
- httpClient.GetAsync(Uri).ContinueWith(
- (requestTask) =>
- {
- HttpResponseMessage response = requestTask.Result;
- // 确认响应成功,否则抛出异常
- response.EnsureSuccessStatusCode();
- // 异步读取响应为字符串
- response.Content.ReadAsStringAsync().ContinueWith(
- (readTask) => Console.WriteLine(readTask.Result));
- });
- Console.WriteLine("Hit enter to exit...");
- Console.ReadLine();
- }
- }
- }
代码运行后将先输出“Hit enter to exit...“,然后才输出请求响应内容,因为采用的是GetAsync(string requestUri)异步方法,它返回的是Task<HttpResponseMessage>对象( 这里的httpClient.GetAsync(Uri).ContinueWith(...)有点类似JavaScript中使用Promise对象进行异步编程的写法,具体可以参考 JavaScript异步编程的四种方法 的第四节和 jQuery的deferred对象详解)。于是我们可以用.NET 4.5之后支持的async、await关键字来重写上面的代码,仍保持了异步性:
查看文本打印
- using System;
- using System.Net.Http;
- namespace HttpClientProject
- {
- class HttpClientDemo
- {
- private const string Uri = "http://api.worldbank.org/countries?format=json";
- static async void Run()
- {
- HttpClient httpClient = new HttpClient();
- // 创建一个异步GET请求,当请求返回时继续处理(Continue-With模式)
- HttpResponseMessage response = await httpClient.GetAsync(Uri);
- response.EnsureSuccessStatusCode();
- string resultStr = await response.Content.ReadAsStringAsync();
- Console.WriteLine(resultStr);
- }
- static void Main(string[] args)
- {
- Run();
- Console.WriteLine("Hit enter to exit...");
- Console.ReadLine();
- }
- }
- }
注意,如果以下面的方式获取HttpResponseMessage会有什么后果呢?
查看文本打印
- HttpResponseMessage response = httpClient.GetAsync(Url).Result;
查看文本打印
- string resultStr = response.Content.ReadAsStringAsync().Result;
异步HTTP POST
我以前写过一个用HttpWebRequest自动登录开源中国的代码(见 C#自动登录开源中国 ),在此基础上我用HttpClient的PostAsync方法改写如下:
查看文本打印
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Net.Http;
- using System.Web.Security;
- namespace AsycLoginOsChina
- {
- public class OschinaLogin
- {
- // MD5或SHA1加密
- public static string EncryptPassword(string pwdStr, string pwdFormat)
- {
- string EncryptPassword = null;
- if ("SHA1".Equals(pwdFormat.ToUpper()))
- {
- EncryptPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(pwdStr, "SHA1");
- }
- else if ("MD5".Equals(pwdFormat.ToUpper()))
- {
- EncryptPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(pwdStr, "MD5");
- }
- else
- {
- EncryptPassword = pwdStr;
- }
- return EncryptPassword;
- }
- /// <summary>
- /// OsChina登陆函数
- /// </summary>
- /// <param name="email"></param>
- /// <param name="pwd"></param>
- public static void LoginOsChina(string email, string pwd)
- {
- HttpClient httpClient = new HttpClient();
- // 设置请求头信息
- httpClient.DefaultRequestHeaders.Add("Host", "www.oschina.net");
- httpClient.DefaultRequestHeaders.Add("Method", "Post");
- httpClient.DefaultRequestHeaders.Add("KeepAlive", "false"); // HTTP KeepAlive设为false,防止HTTP连接保持
- httpClient.DefaultRequestHeaders.Add("UserAgent",
- "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11");
- // 构造POST参数
- HttpContent postContent = new FormUrlEncodedContent(new Dictionary<string, string>()
- {
- {"email", email},
- {"pwd", EncryptPassword(pwd, "SHA1")}
- });
- httpClient
- .PostAsync("http://www.oschina.net/action/user/hash_login", postContent)
- .ContinueWith(
- (postTask) =>
- {
- HttpResponseMessage response = postTask.Result;
- // 确认响应成功,否则抛出异常
- response.EnsureSuccessStatusCode();
- // 异步读取响应为字符串
- response.Content.ReadAsStringAsync().ContinueWith(
- (readTask) => Console.WriteLine("响应网页内容:" + readTask.Result));
- Console.WriteLine("响应是否成功:" + response.IsSuccessStatusCode);
- Console.WriteLine("响应头信息如下: ");
- var headers = response.Headers;
- foreach (var header in headers)
- {
- Console.WriteLine("{0}: {1}", header.Key, string.Join("", header.Value.ToList()));
- }
- }
- );
- }
- public static void Main(string[] args)
- {
- LoginOsChina("你的邮箱", "你的密码");
- Console.ReadLine();
- }
- }
- }
代码很简单,就不多说了,只要将上面的Main函数的邮箱、密码信息替换成你自己的OsChina登录信息即可。上面通过httpClient.DefaultRequestHeaders属性来设置请求头信息,也可以通过postContent.Header属性来设置。上面并没有演示如何设置Cookie,而有的POST请求可能需要携带Cookie,那么该怎么做呢?这时候就需要利用HttpClientHandler(关于它的详细信息见下一节)了,如下:
查看文本打印
- CookieContainer cookieContainer = new CookieContainer();
- cookieContainer.Add(new Cookie("test", "0")); // 加入Cookie
- HttpClientHandler httpClientHandler = new HttpClientHandler()
- {
- CookieContainer = cookieContainer,
- AllowAutoRedirect = true,
- UseCookies = true
- 上一篇: Knockout获取数组元素索引的2种方法
- 下一篇: .NET平台常用的框架整理