`

HttpClient .NET4.5 使用方法

 
阅读更多

简介

本文的主要参考资料:C# 5.0 in  a nutshell   ,由于我的英文能力有限,算是读书笔记。需要此书的可以留邮箱地址。以下是正文:

HttpClient是.NET4.5提供的一个实现了http传输协议的类,该类可以说分装了HttpWebRequest和HttpWebResponse,它可以说是WebClient的精简升级版,适用于新的Metro-Style  App以及原生的异步模式,在Metro-Style App中已不能使用原来的WebClient了,所以你可以把它看成是一个替代的类。它与WebClient相比,有几个特点:

  1. 一个单一的HttpClient实例,便可以被并发地使用,而不需重新生成实例,换句话说,它是线程安全,而且一次生成,N次使用,中间就少了很多重复的设置过程。
  2. HttpClient可以让你实现并插入自己的消息处理,这对于记录以及单元测试非常有好处。
  3. HttpClient拥有丰富和扩展性强的Headers和Content类型系统。
  4. 当然,HttpClient并非可以完全替代WebClient,因为后者还包括了处理FTP协议的能力,应该说HttpClient主要替代的是在Metro-Style App中WebClient实现Http协议的能力。

使用快速入门
使用HttpClient最简单的方法,便是,先实例化一个HttpClient,然后在”Get* “系列方法中传入一个URI地址,如:

 

string html = await new HttpClient().GetStringAsync ("http://linqpad.net");
 

 当然,还有GetByteArrayAsync 和GetStreamAsync方法,这些方法很简单。而特点1的代码体现如下:

 

var client = new HttpClient();
var task1 = client.GetStringAsync ("http://www.linqpad.net");
var task2 = client.GetStringAsync ("http://www.albahari.com");
Console.WriteLine (await task1);
Console.WriteLine (await task2);
 

 HttpClient有一个Timeout属性(作用如其名字)和一个BaseAddress属性(可以作为URI的头部分)。

但是HttpClient只是简单的一个外壳而已,具体的配置类被抽象出成了一个接口,叫做HttpMessageHandler,其中,.NET为我们实现了一个具体的类,叫做HttpClientHandler(实现了HttpMessageHandler接口),使用代码如下:

 

var handler = new HttpClientHandler { UseProxy = false };
var client = new HttpClient (handler);
...
 

 也就是HttpClient有个接收该接口的重载版本,这个例子中,我们使得此次的Http操作不能用代理来进行。该抽象接口在后续介绍。

GetAsync和response messages(即代表http返回报文的一个类)

以上的GetStringAsync, GetByteArrayAsync, 和 GetStreamAsync都是HttpClient的GetAsync方法的简化版本(也就是说调用者更明确地想将返回的结果具体化,而非只是一个response message),若使用底层的GetAsync,那么代码参考如下:

 

var client = new HttpClient();
// The GetAsync method also accepts a CancellationToken.
HttpResponseMessage response = await client.GetAsync ("http://...");
response.EnsureSuccessStatusCode();
string html = await response.Content.ReadAsStringAsync();
 

HttpResponseMessage对象就是代表了一个Http请求从服务器返回的“报文”,HttpResponseMessage暴露了属性用于访问“报文”中的Headers信息,它是一个HttpResponseHeaders类型的属性(继承自发送和返回Headers共享的HttpHeaders),具体有哪些可以参考API文档。

。HttpResponseMessage当然还有典型的“返回状态码”属性,即StatusCode,这概念了解一下Http协议便可以知道,当然一般它的值是200的时候,说明是返回正常,但你可以用属性IsSuccessStatusCode来指示Http响应是否成功。

不像WebClient,在使用HttpClient时,若StatusCode返回的是非成功的,比如是404(not found),它是不会抛出异常,若你想让它抛出,则需要调用HttpResponseMessage对象的 EnsureSuccessStatusCode方法,然后再去对Content进行处理。Content是一个HttpContent 类型,代表的是Http协议返回报文中的Body部分,它是承载从服务器返回的具体的内容的,算是最重要的payload,很多时候,我们只要把这部分的东东解析成一个XML文档即可。      HttpResponseMessage有个CopyToAsync的方法将承载的内容写到另外一个Stream中,比如将它输出到一个文件流中,代码如下:

 

using (var fileStream = File.Create ("linqpad.html"))
await response.Content.CopyToAsync (fileStream);
 

 最后,GetAsync是Http协议的四个方法之一(分别是GET,POST,PUT和DELETE,在HttpClient都已经有对应的方法了,分别是GetAsync,PostAsync,PutAsync和DeleteAsync)。其实占用流量最多是GET,其次是POST。

SendAsync 和request messages(Http请求报文)

上一节中描述的HttpClient的四大方法,其实是对应到的是一个SendAsync方法,因为对于底层来说,你把对应的协议请求报文准备好,然后统一执行一个发送动作将报文发送出去。而SendAsync方法接收的参数就是一个代表“请求报文”的Request,参考代码如下:

 

var client = new HttpClient();
var request = new HttpRequestMessage (HttpMethod.Get, "http://...");
HttpResponseMessage response = await client.SendAsync (request);
response.EnsureSuccessStatusCode();
...
 

 而以上这段稍微复杂的代码等效于快速入门那一节的那两句代码。自定义一个HttpRequestMessage对象,意味着,你要自己构建一些Header,以及在Content中传入Post需要的负载信息。

Uploading data and HttpContent

在使用Post方法时,往往是为了给服务器端发送一段数据(比如一个XML,一张图片),而这段数据便可以通过留的形式放在HttpRequestMessage的Content中,它和HttpResponseMessage中的Content一样,都是HttpContent类型。.NET给我们提供了4个继承自HttpContet的类,用以将传输数据,如下:


  • ByteArrayContent
  • StringContent
  • FormUrlEncodedContent
  • StreamContent


参考代码如下:

 

var client = new HttpClient (new HttpClientHandler { UseProxy = false });
var request = new HttpRequestMessage (
HttpMethod.Post, "http://www.albahari.com/EchoPost.aspx");
request.Content = new StringContent ("This is a test");
HttpResponseMessage response = await client.SendAsync (request);
response.EnsureSuccessStatusCode();
Console.WriteLine (await response.Content.ReadAsStringAsync());
 


 其中“This is a test”就是你要传给服务器的数据,当然,这没啥意义。

HttpMessageHandler

之前提到过的,大多数自定义的请求属性是定义在HttpClientHandler(.net提供的,实现了HttpMessageHandler)上,而不是在HttpClient上。以下是HttpMessageHandler的部分:

 

public abstract class HttpMessageHandler : IDisposable
{
protected internal abstract Task<HttpResponseMessage> SendAsync
(HttpRequestMessage request, CancellationToken cancellationToken);
public void Dispose();
protected virtual void Dispose (bool disposing);
}
 


 你会突然发现,这里面也有一个SendAsync方法,那么其实,HttpClient的SendAsync方法就是调用HttpMessageHandler的这个方法的。

   Unit testing and mocking(使用自定义HttpMessageHandler来单元测试和模拟

代码1,自定义的HttpMessageHandler,即MockHandler:

 

class MockHandler : HttpMessageHandler
{
Func <HttpRequestMessage, HttpResponseMessage> _responseGenerator;
public MockHandler
(Func <HttpRequestMessage, HttpResponseMessage> responseGenerator)
{
_responseGenerator = responseGenerator;
}
protected override Task <HttpResponseMessage> SendAsync
(HttpRequestMessage request, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var response = _responseGenerator (request);
response.RequestMessage = request;
return Task.FromResult (response);
}
}
 


 代码2,在单元测试中使用MockHandler

 

var mocker = new MockHandler (request =>
new HttpResponseMessage (HttpStatusCode.OK)
{
Content = new StringContent ("You asked for " + request.RequestUri)
});
var client = new HttpClient (mocker);
var response = await client.GetAsync ("http://www.linqpad.net");
string result = await response.Content.ReadAsStringAsync();
Assert.AreEqual ("You asked for http://www.linqpad.net/", result);
 


使用 DelegatingHandler实现“职责链模式”

DelegatingHandler是.NET提供的,继承自HttpResponseMessage,用来实现职责链模式的类,使用方法如下:

 

class LoggingHandler : DelegatingHandler
{
public LoggingHandler (HttpMessageHandler nextHandler)
{
InnerHandler = nextHandler;
}
protected async override Task <HttpResponseMessage> SendAsync
(HttpRequestMessage request, CancellationToken cancellationToken)
{
Console.WriteLine ("Requesting: " + request.RequestUri);
var response = await base.SendAsync (request, cancellationToken);
Console.WriteLine ("Got response: " + response.StatusCode);
return response;
}
}
 

 其中,DelegatingHandler定义了一个InnerHandler属性(根据设计模式也能知道,它也是HttpMessageHandler类型),用于内部使用,也就是base.SendAsync调用时,会调用InnerHandler的SendAsync。那么我们只需要进行一些自己需要的操作即可,这是关于设计模式的,不比多说。

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics