不“简单”的HttpClient
Web能够打下天下,最重要的功臣就是HTTP;HTTP能够建功立业,最重要的原因就是它的简单。
微软在.NET Framework 4.5中为大家带来了System.Net.Http.HttpClient,既然叫HttpClient,我想应该迎合了HTTP简单的特性,应该会比HttpWebRequest更简单。
在之前的博文“jQuery能做到,PHP能做到,C#也能做到”中也的确发现用HttpClient发起HTTP POST请求并传递url query string参数,比用HttpWebRequest更简单。于是打算把基于HttpWebRequest的实现改为基于HttpClient的实现。
基于HttpWebRequest的实现中有设置UserAgent的代码:
var webRequest = WebRequest.Create(url) as HttpWebRequest; webRequest.UserAgent = "CNBlogs";
本来以为HttpClient也有同样的UserAgent属性,于是想这样写:
var httpClient = new HttpClient(); httpClient.UserAgent = "CNBlogs";//错误的代码
结果发现HttpClient根本没有UserAgent这个属性。
于是,找啊找,终于找到了一个UserAgent:
var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.UserAgent
以为就是它了,却发现这里的UserAgent是只读属性。
再一看它的类型是HttpHeaderValueCollection<ProductInfoHeaderValue>,可以Add,Add的参数类型是ProductInfoHeaderValue,于是new ProductInfoHeaderValue,构造函数的参数类型是ProductHeaderValue,于是new ProductHeaderValue。代码如下:
var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.UserAgent.Add( new ProductInfoHeaderValue( new ProductHeaderValue("CNBlogs")));
运行代码一看,的确得到了想要的UserAgent。顺藤摸瓜终于摸到了,但有些复杂。
换了根藤摸,摸到了一个稍微简单些的瓜,这样也可以:
var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.UserAgent.Add( new ProductInfoHeaderValue("CNBlogs", null));
根据实际需求,需要在UserAgent中设置邮件地址,于是代码改为:
var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.UserAgent.Add( new ProductInfoHeaderValue( new ProductHeaderValue("contact@cnblogs.com")));
运行结果竟然报错:
failed: System.FormatException : The format of value 'contact@cnblogs.com' is invalid.
at System.Net.Http.Headers.HeaderUtilities.CheckValidToken(String value, String parameterName)
at System.Net.Http.Headers.ProductHeaderValue..ctor(String name)
而在HttpWebRequest中是可以的:
var webRequest = WebRequest.Create(url) as HttpWebRequest; webRequest.UserAgent = "contact@cnblogs.com";
摸了半天,原来摸到的是半生不熟的瓜。。。
算了,还是求助摸瓜高手Google吧。。。找到了How to use HttpClient handlers中的一行代码:
httpClient.DefaultRequestHeaders.Add("user-agent", "...");
高手一出手,就知有没有,改为这个代码就能搞定:
var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("UserAgent", "contact@cnblogs.com");
这个应该是HtppClient中设置UserAgent最简单的方法,HttpWebRequest的对应实现是:
var webRequest = WebRequest.Create(url) as HttpWebRequest; webRequest.Headers.Add("UserAgent", "contact@cnblogs.com");
搞定是搞定了,原以为HttpClient会比HttpWebRequest更简单。可是现在设置UserAgent这么简单的操作,使用HttpWebRequest信手拈来,只要输入“.u”,智能感知就能找到;使用HttpClient反而有些麻烦,不仅没有智能感知,而且要手动输入字符串"UserAgent"。
我想绝大多数人使用HttpClient设置UserAgent时,首先想到的是HttpClient.UserAgent。优秀的设计应该是用户想到哪里,它就出现在哪里。而不“简单”的HttpClient却要和用户捉迷藏。
HttpClient与HttpWebRequest究竟有什么区别,为什么要故意显得与HttpWebRequest不一样?
MSDN上是这么说:
By default, HttpWebRequest will be used to send requests to the server.
If an app using HttpClient and related classes in the System.Net.Http namespace intends to download large amounts of data (50 megabytes or more).
仅仅因为响应请求的数据量大,HttpClient就与HttpWebRequest差别如此大,这是站不住脚的理由。
后来在Henrik's Blog中找到这样一句话:
The default HttpClient is the simplest way in which you can start sending requests. A single HttpClient can be used to send as many HTTP requests as you want concurrently so in many scenarios you can just create one HttpClient and then use that for all your requests.
原来答案在这里!HttpClient最与众不同的地方是同一个HttpClient实例可以发出多次请求,每次请求是可以是完全不同的URL。而一个HttpWebRequest实例对应于一个Url的一次请求。这才是HttpClient与HttpWebRequest的最大区别所在。
本来写这篇博客是想批评HttpClient,写的过程中才发现原来是自己对HttpClient不够了解。但是依然以批评收尾,HttpClient设计得不够简单。