除了可以使用HttpWebRequest类来实现HTTP网络请求之外,还可以使用HttpClient类来实现。对于基本的请求操作,HttpClient类提供了一个简单的接口来处理最常见的任务,并为身份验证提供了适用于大多数方案的合理的默认设置。对于较为复杂的 HTTP 操作,更多的功能包括:执行常见操作(DELETE、GET、PUT 和 POST)的方法;获取、设置和删除 Cookie 的功能;支持常见的身份验证设置和模式;异步方法上提供的 HTTP 请求进度信息;访问有关传输的安全套接字层 (SSL) 详细信息;在高级应用中包含自定义筛选器的功能等。
(1 )获取字符串数据
HttpClient类使用基于任务的异步模式提供了非常简化的请求操作,你可以直接调用HttpClient类GetStringAsync方法便可以获取到网络返回的字符串数据。下面来看一下使用Get请求来获取网络返回的字符串,实现的代码很简洁和简单,示例代码如下所示:
Uri uri = new Uri("http://yourwebsite.com");
HttpClient httpClient = new HttpClient();
// 获取网络的返回的字符串数据
string result = await httpClient.GetStringAsync (uri);
使用GetStringAsync方法是一种简化的HTTP请求,如果要获取到HTTP请求所返回的整个对象HttpResponseMessage类对象可以使用GetAsync方法。HttpResponseMessage对象是HTTP的相应消息对象,它包含了网络请求相应的HTTP头、数据体等信息。下面使用GetAsync方法来获取网络返回的字符串信息,示例代码如下所示:
HttpResponseMessage response = await httpClient.GetAsync(uri);
string responseBody = await response.Content.ReadAsStringAsync();
(2 )获取数据流数据
HttpResponseMessage对象的Content属性表示是返回的数据对象,是一个IHttpContent类型的对象。如果要获取的是数据流数据,可以通过它的ReadAsBufferAsync方法获取到返回的IBuffer对象,或者通过ReadAsInputStreamAsync地方获取IInputStream对象,然后再转化为Stream对象,示例代码如下所示:
using (Stream responseStream = (await response.Content.ReadAsInputStreamAsync()).AsStreamForRead())
{
int read = 0;
byte[] responseBytes = new byte[1000];
do
{
// 如果read等于0表示Stream的数据以及读取完毕
read = await responseStream.ReadAsync(responseBytes, 0, responseBytes.Length);
} while (read != 0);
}
(3 )取消网络请求
HttpClient类发起的网络请求都是基于任务的异步方法,所以要取消其异步的操作可以通过异步任务的取消对象CancellationTokenSource对象来取消,这点和HttpWebRequest类是不同。如果使用CancellationTokenSource对象来取消异步的请求会触发TaskCanceledException异常,这个异常需要用try catch语句来捕获,便可以识别到请求是被取消的。
private CancellationTokenSource cts = new CancellationTokenSource();
try
{
// 使用CancellationTokenSource对象来控制异步任务的取消操作
HttpResponseMessage response = await httpClient.GetAsync(new Uri(resourceAddress)).AsTask(cts.Token);
responseBody = await response.Content.ReadAsStringAsync().AsTask(cts.Token);
cts.Token.ThrowIfCancellationRequested();
}
catch (TaskCanceledException)
{
responseBody = "请求被取消";
}
// 调用Cancel方法取消网络请求
if (cts.Token.CanBeCanceled)
{
cts.Cancel();
}
使用HttpClient类发起Post请求的编程方式也很简洁,可以调用方法PostAsync(Uri uri, IHttpContent content)来直接向目标的地址Post数据,在该方法里面有两个参数其中uri就是网络的目标地址,两外一个content是指你要向目标地址Post的数据对象。在Post数据之前首先把数据初始化成为一个IHttpContent对象,实现了IHttpContent接口的类有HttpStringContent类、HttpStreamContent类和HttpBufferContent类,这三个类分表代表了字符串类型、数据流类型和二进制类型,数据流类型和二进制类型是可以互相转换的区别不大。调用PostAsync方法之后会返回一个HttpResponseMessage对象,通过这个HTTP的相应消息对象我们就可以获取Post请求之后的返回的结果信息。Post请求发送字符串和数据流数据的代码示例如下所示:
(1)Post请求发送字符串数据
HttpStringContent httpStringContent = new HttpStringContent("hello Windows 10");
HttpResponseMessage response = await httpClient.PostAsync(uri,
httpStringContent).AsTask(cts.Token);
string responseBody = await response.Content.ReadAsStringAsync().AsTask(cts.Token);
(2)Post请求发送数据流数据
HttpStreamContent streamContent = new HttpStreamContent(stream.AsInputStream());
HttpResponseMessage response = await httpClient.PostAsync(uri,
streamContent).AsTask(cts.Token);
string responseBody = await response.Content.ReadAsStringAsync().AsTask(cts.Token);
除了使用PostAsync方法之外,还可以使用SendRequestAsync方法来发送网络请求,SendRequestAsync方法既可以使用Get方式也可以使用Post方式。SendRequestAsync方法发送的消息类型是HttpRequestMessage类对象,HttpRequestMessage类表示HTTP的请求消息类,你可以通过HttpRequestMessage对象设置请求的类型(Get/Post)和传输的数据对象。使用SendRequestAsync方法的代码示例如下所示:
// 创建HttpRequestMessage对象
HttpStreamContent streamContent = new HttpStreamContent(stream.AsInputStream());
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, new Uri(resourceAddress));
request.Content = streamContent;
// 发送数据
HttpResponseMessage response = await httpClient.SendRequestAsync(request).AsTask(cts.Token);
string responseBody = await response.Content.ReadAsStringAsync().AsTask(cts.Token);
Cookie是指某些网站为了辨别用户身份、进行回话跟踪而储存在用户本地终端上的数据(通常经过加密)。当在使用HTTP请求的时候,如果服务器返回的数据待用Cookie数据,也是可以获取出来,存储在本地,下次发起HTTP请求的时候就会带上这些Cookie的数据。
在HttpClient类的网络请求中可以通过HttpBaseProtocolFilter类来获取网站的Cookie信息,HttpBaseProtocolFilter类表示是HttpClient的HTTP请求的基础协议的过滤器。获取Cookie的代码示例如下所示:
// 创建一个HttpBaseProtocolFilter对象
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
// 通过HttpBaseProtocolFilter对象获取使用HttpClient进行过网络请求的地址的Cookie信息
HttpCookieCollection cookieCollection = filter.CookieManager.GetCookies(new Uri(resourceAddress));
// 遍历整个Cookie集合的Cookie信息
foreach (HttpCookie cookie in cookieCollection)
{
}
当然在发送HTTP请求的时候也一样可以带上Cookie信息,如果服务器可以识别到Cookie信息就会通过Cookie信息来进行一些操作,比如Cookie信息信息带有用户名和密码的加密信息,那么就可以免去登录的步骤。在HttpClient的网路请求里面HttpCookie类表示是一个Cookie对象,创建好Cookie对象之后通过HttpBaseProtocolFilter对象的CookieManager属性来设置Cookie,然后发送网络请求,这时候的网络请求就会把Cookie信息给带上。设置Cookie的代码示例如下所示:
// 创建一个HttpCookie对象,"id"表示是Cookie的名称,"localhost"是主机名,"/"是表示服务器的虚拟路径
HttpCookie cookie = new HttpCookie("id", "yourwebsite.com", "/");
// 设置Cookie的值
cookie.Value = "123456";
// 设置Cookie存活的时间,如果设置为null表示只是在一个会话里面生效
cookie.Expires = new DateTimeOffset(DateTime.Now, new TimeSpan(0, 1, 8));
// 在过滤器里面设置Cookie
HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();
bool replaced = filter.CookieManager.SetCookie(cookie, false);
……接下来可以向"yourwebsite.com"远程主机发起请求
HttpClient的网络请求是支持进度监控,通过异步任务的IProgress<T>对象可以直接监控到HttpClient的网络请求返回的进度信息,返回的进度对象是HttpProgress类对象。在进度对象HttpProgress里面包含了下面的一些信息:Stage(当前的状态)、BytesSent(已发送的数据大小)、BytesReceived(已接收的数据大小)、Retries(重试的次数)、TotalBytesToSend(总共需要发送的数据大小)和TotalBytesToReceive(总共需要接收的数据大小)。网络请求进度监控的代码示例如下所示:
// 创建IProgress<HttpProgress>对象
IProgress<HttpProgress> progress = new Progress<HttpProgress>(ProgressHandler);
// 在异步任务中加入进度监控
HttpResponseMessage response = await httpClient.PostAsync(new Uri(resourceAddress), streamContent).AsTask(cts.Token, progress);
// 进度监控的回调方法
private void ProgressHandler(HttpProgress progress)
{
// 在这里可以通过progress参数获取到进度的相关信息
}
HTTP请求筛选器是HttpClient网络请求的一个很强大的功能,它可以把你每次网络请求需要规则封装起来作为一个公共的筛选器来使用,使得特定连接和安全方案的 Web 请求变得更加简单。我们可以把身份验证、数据加密、连接失败后使用自动重试等逻辑封装在筛选器里面,然后再使用筛选器来初始化一个HttpClient对象进行网络请求。
通常情况下,处理请求期间预期可能会出现的一个网络或安全状况很容易,但要处理多个网络或安全状况可能就比较困难。 你可以创建一些简单的筛选器,然后再根据需要将它们链接起来。这样你就能够针对预期可能会出现的复杂情况开发出一些 Web 请求功能,而无需开发非常复杂的程序。
HttpClient是用于通过HTTP发送和接收请求的主类,它使用HttpBaseProtocolFilter类来确定如何发送和接收数据,所以HttpBaseProtocolFilter在逻辑上是所有自定义筛选器链的结尾。每个HttpClient实例都可以有一个不同的筛选器链或管道,如图14.3所示。
图14.3 HttpClient请求的筛选器链模型
若要编写一个自定义筛选器,你需要创建一个自定义的筛选器类实现IHttpFilter接口,通过IHttpFilter.SendRequestAsync方法来指定筛选器的工作方式,也就是把你对于网络请求封装的信息放在该方法里面,在发起网络请求的时候筛选器内部会调用该方法。你可以使用 C#、Visual Basic .NET 或 C++ 来编写筛选器,并且这些筛选器可以在 Windows 运行时支持的所有语言中调用和使用。下面来看一个向 HTTP 请求和响应添加自定义标头的筛选器的示例代码。
// 创建一个自定义筛选器使用该筛选器会在HTTP请求和相应中都添加一个自定义的HTTP头信息
public class PlugInFilter : IHttpFilter
{
private IHttpFilter innerFilter;
public PlugInFilter(IHttpFilter innerFilter)
{
if (innerFilter == null)
{
throw new ArgumentException("innerFilter cannot be null.");
}
this.innerFilter = innerFilter;
}
// 在SendRequestAsync方法里面添加自定义的HTTP头
public IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> SendRequestAsync(HttpRequestMessage request)
{
return AsyncInfo.Run<HttpResponseMessage, HttpProgress>(async (cancellationToken, progress) =>
{
// 添加请求头
request.Headers.Add("Custom-Header", "CustomRequestValue");
HttpResponseMessage response = await innerFilter.SendRequestAsync(request).AsTask(cancellationToken, progress);
cancellationToken.ThrowIfCancellationRequested();
// 添加相应头
response.Headers.Add("Custom-Header", "CustomResponseValue");
return response;
});
}
public void Dispose()
{
innerFilter.Dispose();
GC.SuppressFinalize(this);
}
}
若要使用此筛选器,在创建HttpClient对象时将其接口传递到HttpClient(IHttpFilter)构造方法里面。若要设置筛选器链,请将新筛选器链接到之前的筛选器以及位于该链结尾处的 HttpBaseProtocolFilter对象。下面使用PlugInFilter筛选器来创建HttpClient对象,代码示例如下所示:
// 先创建一个HttpBaseProtocolFilter对象,因为这个是HttpClient默认的最底下的筛选器
var basefilter = new HttpBaseProtocolFilter();
// 创建PlugInFilter筛选器对象,链接到HttpBaseProtocolFilter对象上
var myfilter = new PlugInFilter(basefilter);
// 使用自定义的筛选器创建HttpClient对象
HttpClient httpClient = new HttpClient(myfilter);
……下面使用httpClient对象来发起网络请求都会自动带上自定义筛选器所添加的HTTP头