1、揭秘通用平台的 HttpClient (译)

时间:2021-11-28 00:32:55

  原文链接:Demystifying HttpClient APIs in the Universal Windows Platform

  正打算翻译这篇文章时,发现园子里已经有朋友翻译过了,既然已经开始了,就再概要的翻译一遍吧,就不逐字逐句了 :)  。这段时间没有春节前那么忙了,正好整理一下技术文档。

译文:

  作为一个 Universal Windows Platform (UWP) app 开发者,如果想通过 HTTP 协议与 web服务器端进行交互,你有很多的 API 可以选择。两个最常用、较为推荐的是  System.Net.Http.HttpClientWindows.Web.Http.HttpClient。相对于较老的 WebClient 和 HttpWebRequest(虽然为了向后兼容, HttpWebRequest 仍然在 UWP中可以使用),推荐使用 HttpClient。

  

  我们已经收到一些关于这两个 API 的问题,它们之间功能的异同,在什么时候使用哪一个等等。在这个文章里面我们会说明这两个 API 的目的。

概述  

  在 .NET 4.5 中 System.Net.Http.HttpClient API 首次被引入,并且通过 NuGet 包,向下为 .NET 4.0 和 Windows Phone8 Sliverlight app 提供支持。相对于老的 HttpWebRequest,这个 API 的目的只为了提供一个 HTTP 客户端一个轻量级、易扩展的抽象层。直到 Windows8.1,这个 API 在底层通过托管的 .NET完全实现了。在 Windows10 上,这个 API 在 UWP中改为基于 Window.Web.Http 和 Windows 的 WinlNet HTTP 协议栈实现了。

  另一方面, Windows.Web.Http.HttpClient API 第一次在 Windows 8.1 和 Windows Phone8.1 上引入。目的是在不同的 Windows app 语言(C#、VB、C++、JavaScript)提供一个统一的 HTTP API 支持。大多数的 Syste.Net.Http 命名空间下的 API 是基于Windows D的 WinlNet HTTP协议栈设计实现的。

  在 Windows Store app 中使用这些 API,支持的系统版本和编程语言:

  

API   系统版本 支持的语言
System.Net.Http.HttpClient Windows,Windows Phone8 以上   仅 .NET 语言
Windows.Web.Http.HttpClient Windows,Windows Phone8.1 以上 所有 Windows Store app 语言

应该使用哪个?

  这个两个 API 在 UWP 中都可以使用,最大的问题就是在 app 中使用哪一个。这取决于两个因素:

  1、你是否需要继承原 UI来收集用户凭据;控制 HTTP 缓存读、写;传递客户端指定的 SSL 验证凭据?

  如果是,那么就使用 Windows.Web.Http.HttpClient。到目前为止,这个 Windows.Web.Http API 提供了比 System.Net.Http API 对 Http 设置更强的控制。在未来 UWP 的版本中,这个 System.Net.Http API 可能会获得相同的功能支持。

  2、你是否想写跨 .NET 平台的代码(UWP/ASP.NET 5/ iOS 和 Android)?

  如果是,那么就使用 System.Net.Http API。这可以让你写其它 .NET 平台的代码,比如 ASP.NET 和 .NET Framework 桌面应用。基于 Xamarin, 这 API 同样支持 iOS 和 Android。

对象模型

  现在我们理解了这两个相似 API 的目的和原理,我们分别详细看一下它们的对象模型。

  System.Net.Http

  最上层的抽象是 HttpClient 对象,它作为 HTTP 协议中 “客户端-服务器” 模型中的客户端实体。这个 Client 可以向服务器端发出多个请求(由 HttpRequestMessage 表示),然后收到相应的响应(由 HttpResponseMessage表示)。每个 HTTP 请求和响应的实体 body 和 headers 由基类 HttpContent 、派生类如 StreamContent、MultipartContent 和 StringContent 表示。它们表示不同的 HTTP 实体 body。他们分别提供一些 ReadAs* 方法来读取请求的 body 或者作为 string 、byte 数组或者 stream 进行响应。

  每一个 HttpClient 对象在底层有一个代表所有 HTTP协议设置相关的 handler 对象。理论上说,你可以认为这个 handler 代表客户端的 HTTP协议栈。它负责发送客户端的 HTTP请求到服务器端,并且把响应传输回客户端。

  在 System.Net.Http API 中使用的默认 handler 类是 HttpClientHandler 。当你创建一个 HttpClient 实体对象时 — 例如,调用 new HttpClient() —  会自动为你的默认 HTTP 协议栈设置创建一个 HttpClientHandler 对象。如果你想更改默认的设置,比如缓存行为、自动压缩、认证、代理,你可以创建你自己的 HttpClientHandler 实体,更改它的属性,然后把它传递给 HttpClient 的构造函数中,比如:

HttpClientHandler myHandler = new HttpClientHandler();
myHandler.AllowAutoRedirect = false;
HttpClient myClient = new HttpClient(myHandler);

链式处理(Chaining of Handlers)

  System.Net.Http.HttpClient API 设计的一个核心的优点就是能够插入自定义的 handlers 并且在一个 HttpClient对象的底层创建链式处理对象。比如,你的 app向一个 Web service 查询一些数据。你自定义客户端的逻辑来处理  HTTP 4xx(客户端错误) 和 5xx(服务器端错误),并且采取特定的重试步骤,比如尝试一个不同的地址或者添加用户的凭据。你希望把 HTTP协议相关的设置工作,跟你关心的 web service 返回数据的处理逻辑区分开。

  你可以通过创建一个继承自 DelegatingHandler 的新 handler 类(比如 CustomHandler1),然后创建一个它的实体 传递给 HttpClient 的构造函数中。DelegatingHandler 类的 InnerHandler 属性用来指定在这个响应链中另一个 handler — 比如,你可以添加另一个自定义的 handler(如 CustomHandler2)。对于最后的 handler,你可以赋值 inner handeler 为一个 HttpClientHandler 实体 — 这会传递这个请求到系统的 HTTP协议栈。看起来类似于:

1、揭秘通用平台的 HttpClient (译)

  实现逻辑的示例:

public class CustomHandler1 : DelegatingHandler
{
// Constructors and other code here.
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// Process the HttpRequestMessage object here.
Debug.WriteLine("Processing request in Custom Handler 1"); // Once processing is done, call DelegatingHandler.SendAsync to pass it on the
// inner handler.
HttpResponseMessage response = await base.SendAsync(request, cancellationToken); // Process the incoming HttpResponseMessage object here.
Debug.WriteLine("Processing response in Custom Handler 1"); return response;
}
} public class CustomHandler2 : DelegatingHandler
{
// Similar code as CustomHandler1.
}
public class Foo
{
public void CreateHttpClientWithChain()
{
HttpClientHandler systemHandler = new HttpClientHandler();
CustomHandler1 myHandler1 = new CustomHandler1();
CustomHandler2 myHandler2 = new CustomHandler2(); // Chain the handlers together.
myHandler1.InnerHandler = myHandler2;
myHandler2.InnerHandler = systemHandler; // Create the client object with the topmost handler in the chain.
HttpClient myClient = new HttpClient(myHandler1);
}
}

 注意:

  1、如果你打算发送请求到远程服务器,这个 handler 链的最后经常是一个 HttpClientHandler,它主要用作发送请求,然后接收 OS的 HTTP 协议栈响应。或者,你可以使用一个假冒的 handler 来模拟 server 端,然后返回伪造的响应。

  2、在发送和接收请求时,为 inner handler 添加额外的处理逻辑,可能导致性能上的损失。所以避免昂贵的异步操作。

  有关 chaining handler 更详细的理论,可以参考 Henrik Nielsen 的这篇文章。(注意这个是关于 ASP.NET Web API 版本的,可能和这里 .NET framework 的版本稍微有区别,不过 chaining handler 理论是一样的)

Windows.Web.Http

  Windows.Web.Http API 对象模型和上面描述的 System.Net.Http 版本类似 — 它也有一个客户端实体的概念,一个 handler(在这个命名空间叫做 “filter”),并且可以选择在 Client 对象和系统默认过滤器之间插入自定义的逻辑。

  大多数的类型定义直接模拟 System.Net.Http 的对象模型,如下:

HTTP client role aspect System.Net.Http type Corresponding Windows.Web.Http type
Client entity HttpClient HttpClient
HTTP request HttpRequestMessage HttpRequestMessage
HTTP response HttpResponseMessage HttpResponseMessage
Entity body of an HTTP request or response HttpContent IHttpContent
Representations of HTTP content as string/stream/etc. StringContent, StreamContent and ByteArrayContent HttpStringContent, HttpStreamContent and HttpBufferContent respectively
HTTP stack/settings HttpClientHandler HttpBaseProtocolFilter
Base class/interface for creating custom handlers/filters DelegatingHandler IHttpFilter

   上面关于 System.Net.Http 的链式处理(chaining of handlers)的讨论,同样适用于 Windows.Web.Http API,这里你可以创建一个自定义 filters链,然后把它们传递到 HttpClient 的构造函数中。

实现常见的 HTTP 场景

  现在我们通过一些代码片段演示一下两个 HttpClient API 的常见使用场景。如果想参考更详细的内容,可以分别浏览 MSDN 文档 Windows.Web.Http.HttpClient 和 System.Net.Http.HttpClient 。

  更改报文头

  System.Net.Http:

  如果修改所有 HttpClient 发出请求的报文头,使用下面的模式:

var myClient = new HttpClient();
myClient.DefaultRequestHeaders.Add("X-HeaderKey", "HeaderValue");
myClient.DefaultRequestHeaders.Referrer = new Uri("http://www.contoso.com");

  如果只修改指定请求的报文头,使用:

HttpRequestMessage myrequest = new HttpRequestMessage();
myrequest.Headers.Add("X-HeaderKey", "HeaderValue");
myrequest.Headers.Referrer = new Uri("http://www.contoso.com");

 Windows.Web.Http:

  上面的模式同样适用于 Windows.Web.Http API。

  注意:

  1、一些 headers 是集合类型的,需要使用 Add 和 Remove 方法来编辑它们

  2、这个 HttpClient.DefaultRequestHeaders 属性表示对 headers 的默认设置,会被加入到 app 的所有请求中。由于这个请求会被操作系统的 HTTP栈处理,在请求被发送出去前会被加入额外的 headers。

超时设置

  System.Net.Http:

  在 System.Net.Http 的 API中,有两种方法设置超时。设置客户端所有请求的超时,使用:

myClient.Timeout = TimeSpan.FromSeconds();

  设置一个请求的超时,使用 cancellation token 模式:

var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds()); var httpClient = new HttpClient();
var resourceUri = new Uri("http://www.contoso.com"); try
{
HttpResponseMessage response = await httpClient.GetAsync(resourceUri, cts.Token);
}
catch (TaskCanceledException ex)
{
// Handle request being canceled due to timeout.
}
catch (HttpRequestException ex)
{
// Handle other possible exceptions.
}

  Windows.Web.Http:

  在 Windows.Web.Http.HttpClient 类型中没有 timeout 属性。因此,你必须使用上面的这种 cancellation token 模式。

身份验证凭据的使用

  System.Net.Http:

  为了保护用户的验证信息,系统的 HTTP 协议栈默认没有为任何发出的请求添加证书。要使用指定用户的证书,使用下面的模式:

var myClientHandler = new HttpClientHandler();
myClientHandler.Credentials = new NetworkCredential(myUsername, myPassword);

  Windows.Web.Http:

  对于 Windows.Web.Http API,发送请求时如果需要用户的证书,默认会弹出一个索要用户证书的对话框。要不想使用这个对话框,可以设置 HttpBaseProtocolFilter 的 AllowUI 属性为 false。可以使用指定的证书取代:

var myFilter = new HttpBaseProtocolFilter();
myFilter.ServerCredential = new PasswordCredential(“fooBar”, myUsername, myPassword);

  注意:

  1、在上面的例子中,myUsername 和 myPassword 是字符串类型的变量,可以弹出 UI来获取用户的输入,或者从 app 的配置中获取。

  2、在 UWP app中,这个 HttpClientHandler.Credentials 属性只能被赋值为 null,DefaultCredentials 或者一个 NetworkCredential 类型的对象。

使用 Client 证书

  System.Net.Http:

  为了保护用户的证书信息,这个API 默认不会发送任何客户端证书到服务器端。想用客户端身份证书验证,使用:

var myClientHandler = new HttpClientHandler();
myClientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;

  Windows.Web.Http:

  有两种选择来使用客户端证书 — 默认弹出一个 UI来让用户选择证书。或者,你可以通过编程的方式来设置客户端证书,例如:

var myFilter = new HttpBaseProtocolFilter();
myFilter.ClientCertificate = myCertificate;

  

  注意:

  1、为了在两个 API 中使用客户端证书,你必须先按照这些步骤把它添加到 app 的商店认证中。在添加了 enterprise capability 的 app,在用户的 “My” store 中也可以使用现有的客户端证书。

  2、对于 HttpClientHandler.ClientCertificateOptions,有两种允许的值:自动和手动(Automatic and Manual)。把它设置为 Automatic 时会在 app的 certificate store 中选择最匹配的 client certificate。把它设置为 Manual 时,会确保没有 client certificate 被发出,即使服务器端需要它。

  代理设置

  对于两个 API,代理的设置会默认使用 Internet Explorer/Microsoft Edge 的设置,并且所有的 HTTP访问都会使用这个设置。这使得 app能自动工作,即使用户通过代理访问互联网。两个 API都没有提供方法让你为 app 设置自定义的代理。不过,你可以选择设置 HttpClientHandler.UserProxy 为 false(System.Net.Http) 或者 HttpBaseProtocolFilter.UseProxy 为 false(Windows.Web.Http)来禁用默认的代理。

Cookie 的处理

  默认情况下,对于同一个 app container 来说,两个 API 都会自动保存服务器端返回的 cookies,然后把它们添加到那个 URI后续的请求中。对于相应 URI的 cookies可以进行读取,并且添加自定义的 cookies。最后,两个 API也可以选择禁用发送 cookies 到服务器端:对于 System.Net.Http ,设置 HttpClientHandler.UseCookies 为 false;对于 Windows.Web.Http,设置 HttpBaseProtocolFilter.CookieUsageBehavior为  HttpCookieUsageBehavior.NoCookies 。

  

Syste.Net.Http:

  为当前 app的所有请求添加一个 cookie:

  

// Add a cookie manually.
myClientHandler.CookieContainer.Add(resourceUri, myCookie);

  为指定的请求添加一个 cookie: 

HttpRequestMessage myRequest = new HttpRequestMessage();
myRequest.Headers.Add("Cookie", "user=foo; key=bar");

  

对于一个给定的 URI,检查所有 Cookies:

var cookieCollection = myClientHandler.CookieContainer.GetCookies(resourceUri);

Windows.Web.Http:

  为当前 app的所有请求添加一个 cookie:

// Add a cookie manually.
filter.CookieManager.SetCookie(myCookie);

  上面为指定的请求添加一个 Cookie的方式,也适用于 Windows.Web.Http API。

  管理 Cookies:

// Get all cookies for a given URI.
var cookieCollection = filter.CookieManager.GetCookies(resourceUri); // Delete a cookie.
filter.CookieManager.DeleteCookie(myCookie);

注意:

1、对于 Windows.Web.Http API,在当前的应用程序容器(app container) 一些 networking APIs 底层使用 WinlNet 栈实现,所以在 cookie manager里的 cookies 是共享的,比如 Windows.Web.Syndication、Windows.Web.AtomPub、XHR 等等。因此,对于同一个服务器的请求,当通过 Syndication API 从服务器收到的一个 cookie 可能被添加到后面的 HttpClient 的请求中。

每台服务器的最大连接数

  默认情况下,对于每台服务器,操作系统底层的 HTTP协议栈最多使用 6个HTTP连接。对于 System.Net.Http 的 HttpClient API没有提供控制这个的方式。对于 Windows.Web.Http API,可以使用:

var myFilter = new HttpBaseProtocolFilter();
myFilter.MaxConnectionsPerServer = ;

最近的更新

  在 Windows10 的 UWP app上,我们为两种 API 都添加了 HTTP/2 的支持。默认这个支持是开启的,所以对开发者来说,你不需要更改代码就可以获得一些好处,比如更低的延迟。对于两个 APIs(System.Net.Http 和 Windows.Web.Http) 都可以显示禁用这个特性,强制使用 HTTP 1.1 或者 1.0 的版本。

  对于未来,我们计划添加呼声较高的特性,比如自定义 server SSL 证书的验证,并且在其他地方创建的 HttpClient 添加 handlers/filters 的能力的支持。为了写出更出色的 Windows 的 app,我们期待你反馈这些 API中需要的新的特性。你可以在 UserVoice提出想法,或者最好加入 Windows Insiders program并且通过论坛或者 Windows Feedback app 提交反馈。

  这篇文章由 Sidharth Nabar 撰写,Windows Networking APIs team 的项目经理。

1、揭秘通用平台的 HttpClient (译)的更多相关文章

  1. &num;一周五&num; win10通用平台,无处不在的Xamarin,msbuild开源,MVP卢建晖的Asp&period;NET 5系列 &lpar;视频&rpar;

    又到周五,本周博主的大部分时间都花在深圳了.最近winhec的消息太多了,我只想补充一点,就是winhec时隔7年之后回归,大多数的媒体都还在沿用之前的“硬件工程大会(Hardware Enginee ...

  2. 利用VBA&plus;OO4O构造CTAIS开放式通用平台

    利用VBA+OO4O构造CTAIS开放式通用平台 2010-06-08 14:59:28 | 来源:税务信息化论文集 | 作者:于非 易飞 摘  要:文立足于CTAIS系统体系,探讨如何通过OO4O技 ...

  3. NET通用平台

    NET通用平台.通用权限.易扩展.多语言.多平台架构框架 先拿出我半前年前平台的设计初稿,经过半年的努力我已经完成了该设计稿的所有功能.并且理念已经远远超出该设计稿. 下面是一些博友对我贴子的评价: ...

  4. 微信公众平台&period;net HttpClient 异步客户端

    微信公众平台.net HttpClient 异步客户端 该客户端实现了对微信公众平台的后台管理,包括获取Token,发生客服消息(文本,图片,语音,视频),自定义菜单的创建,查询,删除,查询订阅用户, ...

  5. 《芒果TV》UWP版利用Windows10通用平台特性,率先支持Xbox One平台

    在Windows开发者中心开放提交Xbox平台应用之后,<芒果TV>UWP版迅速更新v3.1.2版,通过升级兼容目标,利用Windows10通用平台特性,率先覆盖Xbox平台用户. 芒果T ...

  6. ASP&period;NET 让无码编程不在是梦 -&period;NET通用平台、通用权限、易扩展、多语言、多平台架构框架

    先拿出我半前年前平台的设计初稿,经过半年的努力我已经完成了该设计稿的所有功能.并且理念已经远远超出该设计稿. 下面是一些博友对我贴子的评价: 1.楼主,想法很美好,现实很骨感,我们公司就有一套你说的这 ...

  7. Java EE平台介绍(译)

    Java EE平台介绍 2.1 企业应用总览 这一部分将对企业应用及其设计和开发进行简单介绍. 就像之前说的,Java EE 平台是为了帮助开发者开发大规模.多层次.可伸缩.服务可靠.网络安全的应用而 ...

  8. 一文揭秘测试平台中是如何将测试用例一键转化Jmeter压测脚本

    ​    ​接上篇,一键转化将接口测试平台测试用例转化成Jmeter压测脚本思路,这里我首先在java 上面做了一个简单的实验,看看 转化的中间遇到的问题,这里呢,我只是给了一个简单的demo 版本, ...

  9. 使用HttpClient实现对第三方服务器的请求并接受返回数据

    /* * 创建日期 2017-4-7 * * TODO 要更改此生成的文件的模板,请转至 * 窗口 - 首选项 - Java - 代码样式 - 代码模板 */ package com.enfo.int ...

随机推荐

  1. POI导出excel日期格式

    参考帖子: [1]http://www.ithao123.cn/content-2028409.html [2]http://javacrazyer.iteye.com/blog/894850 再读本 ...

  2. 【转】 C&plus;&plus;使用zlib库&lpar;-&rpar;

    来自:  http://blog.chinaunix.net/uid-24607609-id-2118143.html   今天看到一个gzopen函数,搜了一下他的系列函数,及相关用法   C++使 ...

  3. &lbrack;Eclipse&rsqb;&lbrack;SVN&rsqb; 在eclipse上安装SVN

    以前装过好多次SVN,始终没有一次把安装过程记录下来,这次新装机器,安装SVN插件时一波三折,记录下来免得以后又忘记了.   方法一: 1. 直接通过后台添加URL通过互联网进行安装,直接上图: 2. ...

  4. Rsync原理介绍及配置应用

    1.前言 基于LAN或WAN的网络应用之间进行数据传输或者同步非常普遍,比如远程数据镜像.备份.复制.同步,数据下载.上传.共享等等.对此,最简单.直接的做法是对数据进行完全复制.然而,数据在网络上来 ...

  5. iOS - OC 数据持久化

    1.Sandbox 沙箱 iOS 为每个应用提供了独立的文件空间,一个应用只能直接访问为本应用分配的文件目录,不可以访问其他目录,每个应用自己独立的访问空间被称为该应用的沙盒.也就是说,一个应用与文件 ...

  6. python中的pth文件作用

    python中有一个.pth文件,该文件的用法是: 首先xxx.pth文件里面会书写一些路径,一行一个. 将xxx.pth文件放在特定位置,则可以让python在加载模块时,读取xxx.pth中指定的 ...

  7. 欢迎使用 Markdown 编辑器写博客

    本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦: Markdown和扩展Markdown简洁的语法 代码块高亮 图片链接和图片上传 LaTex数学公式 UML ...

  8. HBase协处理器

    说明:类似于RDBMS中触发器,允许用户在region服务器上运行自己的代码,在客户端用户不用关心操作具体在哪进行 使用场景:权限控制,回调函数(钩子函数).扫描统计等 主要类:observer和en ...

  9. swift中的&amp&semi;-备

    参数的传递引用 类是引用类型,其他的数据类型如整型.浮点型.布尔型.字符.字符串.元组.集合.枚举和结构体全部是值类型. 有的时候就是要将一个值类型参数以引用方式传递,这也是可以实现的,Swift提供 ...

  10. Hibernate5-课程笔记2

    单表的增删改查操作: (1)定义获取Session和SessionFactory的工具类: package com.tongji.utils; import org.hibernate.Session ...