天天看点

详解 UWP (通用 Windows 平台) 中的两种 HttpClient API

很多开发者对于 UWP 中的上述两个 API 有着功能异同、如何选择等疑问。本文旨在解答这些疑问并阐明两个 API 各自的用途。

<code>System.Net.Http.HttpClient</code> 这一 API 在 .NET 4.5 中被首次引入,同时也有一个变体以 NuGet 包的形式为 .NET 4.0 以及 Windows Phone 8 Silverlight 应用提供支持。该 API 的目的在于提供一种比老旧的 <code>HttpWebRequest</code> API 更为简单明了的抽象层,以及更为弹性灵活的 HTTP 客户端角色的实现方法。例如,开发者可以通过其提供的链式自定义 handler,拦截每个请求或响应并实现自定义逻辑。直到 Windows 8.1 为止,该 API 的底层都是由纯 .NET 实现的。在 Windows 10 中,该 API 的 UWP 实现已经改为基于 <code>Windows.Web.Http</code> 和 WinINet HTTP 协议栈实现了。

另一方面,<code>Windows.Web.Http.HttpClient</code> API 则在 Windows 8.1 被引入,并同时可用于 Windows Phone 8.1。创建这一 API 的最大动因在于整合各种 Windows 应用开发语言可用的 HTTP API,使一种 API 能够提供这些语言各自 API 的全部特性。其中大部分基础 API 的设计均来源于 <code>System.Net.Http</code>,而其实现则是基于WinINet HTTP 协议栈。

在 Windows 商店应用中使用上述两种 API时,操作系统版本以及编程语言的支持情况如下:

API

操作系统版本

支持语言

System.Net.Http.HttpClient

Windows, Windows Phone 8 以上

仅限 .NET 语言

Windows.Web.Http.HttpClient

Windows, Windows Phone 8.1 以上

所有 Windows 商店应用语言

两种 API 在 UWP 中均可用,因而 HTTP 开发者面临的最大问题就是该在应用中选择二者中的哪一种。选择结果要依一些具体因素而定。

你是否需要整合原生 UI 以收集用户凭据、控制 HTTP 患侧读写行为或传递指定 SSL 客户端证书用于验证? 

如果是,则使用 <code>Windows.Web.Http.HttpClient</code>。截至撰写本文时,相对于 <code>System.Net.Http</code> API,<code>Windows.Web.Http.HttpClient</code> API 提供了更多对 HTTP 设置的掌控能力。未来 <code>System.Net.Http</code> API 可能也会得到加强以提供这些特性。

你是否要编写跨平台 .NET 代码(通用于 UWP/ASP.NET 5/iOS 以及 Android 平台)? 

现在我们已经了解了创建这两个相似 API 的原因以及如何选择的基本原则,接下来深入了解一下它们各自的对象模型。

每个 <code>HttpClient</code> 对象底层均有一个 handler 对象表示所有客户端 HTTP 相关设置。你可以从概念上把 handler 理解为客户端底层的 HTTP 栈。它负责把客户端的 HTTP 请求发送至服务器并传回相应的响应。

<code>System.Net.Http.HttpClient</code> API 设计中的一个关键优势就是可以在一个 <code>HttpClient</code> 对象底层插入自定义 handler,并创建一条 handler 对象链。假设你要构建一个需要从 Web 服务查询数据的应用。你编写了自定义逻辑来处理从服务器返回的 HTTP 4xx(客户端错误) 和 5xx(服务端错误) 错误,并发起更换端点或添加用户凭证等重试动作。而你也想要将这部分与 HTTP 相关的工作与其它处理 Web 服务返回数据的业务逻辑分开。

详解 UWP (通用 Windows 平台) 中的两种 HttpClient API

完成上述需求的示例代码:

注意:

如果你打算向一个远程服务器端点发送请求,通常链中的最后一个 handler 都是 <code>HttpClientHandler</code>,该 handler 负责实际通过系统的 HTTP 栈发送请求并接收响应。 若非如此,你可以使用一个自定义 handler 模拟发送请求以及接收模拟响应的过程。

在将请求传递到下一个 handler 之前,或在前一个返回响应之前添加处理逻辑可能会带来性能损失。在这类场景中最好避免开销昂贵的同步操作。

<code>Windows.Web.Http</code> API 的对象模式与上文中描述的 <code>System.Net.Http</code> 非常类似,它也有客户端实体、handler(该命名空间内叫做 "filter",即过滤器)以及在客户端与系统默认 filter 之间插入自定义逻辑等概念。

本 API 中大部分类型与 <code>System.Net.Http</code> 的对象模型类似:

HTTP 客户端角色表示

System.Net.Http 类型

对应 Windows.Web.Http 类型

客户端实体

HttpClient

HTTP 请求

HttpRequestMessage

HTTP 响应

HttpResponseMessage

HTTP 或响应的 entity body

HttpContent

IHttpContent

HTTP 内容的字符串、流等表示

StringContent, StreamContent and ByteArrayContent

HttpStringContent, HttpStreamContent and HttpBufferContent respectively

HTTP 栈/设置

HttpClientHandler

HttpBaseProtocolFilter

用于创建自定义 handlers/filters 的基类/接口

DelegatingHandler

IHttpFilter

上文中关于 <code>System.Net.Http</code> API 链式 handler 的讨论亦可用于 <code>Windows.Web.Http</code> API。你可以创建一组链式自定义 filter,传递给 HttpClient 对象的构造函数。

System.Net.Http:

修改 HttpClient 实例发出的所有请求的头:

只修改特定请求的头:

Windows.Web.Http:

上述代码同样适用于 <code>Windows.Web.Http</code> API。

部分头项目是集合,修改需要通过 Add 和 Remove 方法实现。

HttpClient.DefaultRequestHeaders 属性表示在应用层次上,默认头集合是否会添加到请求中。由于请求是由系统的 HTTP 栈处理的,所以请求实际发送出去前,一些附加头可能会添加到请求中。

在 <code>System.Net.Http</code> API 中,有两种方式设置超时。要为客户端发出的所有请求设置超时,使用:

要为单个请求设置超时,则使用 CancellationToken:

<code>Windows.Web.Http.HttpClient</code> 类型中没有超时属性可用,因此你必须向上文一样使用 CancellationToken 实现超时处理。

为了保护用户的凭证信息,HTTP 栈默认不会向发出的请求添加任何验证凭据。要使用指定用户的凭据,可以使用如下方法:

对于 <code>Windows.Web.Http</code> API,默认情况下如果发出的请求访问了要求用户验证的资源,系统会弹出一个 UI 对话框。要关闭 UI 对话框,可以把 <code>HttpBaseProtocolFilter</code> 的 <code>AllowUI</code> 属性设置为 false。要使用指定用户的凭据,可以使用如下方法:

在上述示例中,<code>myUsername</code> 和 <code>myPassword</code> 两个字符串变量可以来自用户通过 UI 输入或者应用自身的配置。

为保护用户的凭据信息,该 API 默认不会向服务器发送任何客户端证书。要使用客户端证书用于验证,使用如下方法:

使用该 API 进行客户端证书验证有两种选择——默认方式是弹出一个 UI 让用户选择证书;另一种选择是在代码中自行指定一个客户端证书:

默认情况下,两种 API 的代理设置都会自动根据 Internet Explorer/ Microsoft Edge 的设置自动为所有 HTTP 调用进行配置。这使得应用在用户通过代理连接网络时也能正常工作。两种 API 都没有提供任何方法为应用指定一个自定义代理。然而你可以通过在 System.Net.Http 中设置 <code>HttpClientHandler.UseProxy</code> 为 false 或在 Windows.Web.Http 中设置 <code>HttpBaseProtocolFilter.UseProxy</code> 为 false 来禁止使用默认代理配置。

为客户端生成的所有请求添加一个 cookie:

为单个请求添加 cookie:

查看给定 URI 的所有 cookie:

上文中为单个请求添加 cookie 的示例同样适用于 <code>Windows.Web.Http</code> API。

管理 cookie:

在 <code>Windows.Web.Http</code> API 中,对于应用容器内的 <code>Windows.Web.Syndication</code>、<code>Windows.Web.AtomPub</code> 以及 <code>XHR</code> 等几个使用 WinINet 栈实现的网络 API之间,cookie 管理器内的 cookie 数据是共享的。因此,之前一个 Syndication API 调用通过服务器响应获得 cookie 可能会被添加到相同应用容器内之后发送给同一服务器的 HttpClient 请求里。

默认情况下,操作系统底层的 HTTP 栈对每个服务器最多使用六个连接。<code>System.Net.Http</code> 的 HttpClient API没有提供任何方法控制最大连接数。对于 <code>Windows.Web.Http</code> API,可以使用如下方法设置:

Windows 10 中的 UWP 在两种 API 中都添加了对 HTTP/2 的支持。该支持默认开启,因此开发者无需做任何代码修改即可享受更低延迟的提升。两种 API (<code>System.Net.Http</code> 和 <code>Windows.Web.Http</code>)也都允许手动关闭这一特性并强制 HTTP 版本为 1.1 或 1.0。

继续阅读