揭秘Windows10 UWP中的httpclient接口[2]
阅读目录:
概述
作为一个Universal Windows Platform (UWP)开发者,如果你尝试使用http与web服务或其他服务端通讯时,有多个API可以选择。 UWP中最常见并推荐使用的HTTP客户端API实现是System.Net.Http.HttpClient和Windows.Web.Http.HttpClient。 这些APIs相比旧的应该优先使用,比如旧APIs的WebClient和HttpWebRequest(尽管它的子集在UWP中是向后兼容的)。
我们收到一些关于问题反馈,关于这些APIs不同之处,从功能上来说两组APIs是上相等的,那在不同场景下选择哪一个呢,诸如此类的问题。 在这篇文章中,我们会去尝试定位这些问题,理清楚这两组APIs的用途及使用场景。
第一个推荐AIP是System.Net.Http.HttpClient,它在Net 4.5中第一次出现,通过Nuget可以安装这个API的兼容版本,这样就可以在Net 4.0和windows Phone 8 Silverlight apps中使用。相比旧的HttpWebRequest API,这个API的目标是提供一个简单的,干净的抽象层,比较灵活的实现http客户端功能。 比如,它允许链接自定义处理器,开发者可以拦截每个request和response,去实现自定义逻辑。 在windows8.1之后,所有功能都在.NET下面实现。 在windows10 UWP中这个API实现移到Windows.Web.Http和WinINet Http层上。
另外一个推荐API是Windows.Web.Http.HttpClient,这个API是Windows 8.1时开始引进的,在Windows Phone 8.1也是可以使用的。 增加这个API的主要目是,把不同windows应用开发语言(C#, VB, C++, JavaScript)下,不同Http APIs合成一个,它支持上述APIs的所有特性。 大多数基础API都是从System.Net.Http派生的,在Windows HTTP基础上实现。
在Windows商店APP中使用这些API时,其支持的系统版本和程序语言如下所示:
如何选择
在UWP中这些HTTP API都是可以使用的,对于开发者来说最大的问题是在APP中应该使用哪一个。其答案取决去几个因素:
-
是否需要结合本地UI收集用户证书,控制HTTP缓存读和写,或者通过指定的ssl客户端证书去做认证? 如果需要认证,那是应使用Windows.Web.Http.HttpClient。在现在的UWP中,Windows.Web.Http提供HTTP设置,它比System.Net.Http API更好的控制这些。 在未来的版本,也会加强支持System.Net.Http在UWP中的特性。
-
是否考虑写跨平台的.NET代码(跨UWP/ASP.NET 5/IOS和Android)? 如果需要,那使用System.Net.Http API。它可以让你写的代码复用在其他.Net平台上,比如ASP.Net 5和.NET桌面平台应用。 通过使用Xamarin,这些API在IOS和Android中也得到支持。
现在就比较好理解为什么会有两个相似APIs了,也了解怎么在二者之间进行选择,下面进一步了解这两个对象模型。
System.Net.Http
其HttpClient对象是最顶端的抽象模型,在HTTP协议client-server模型中它表示client这部分。其client能发出多个request请求(用HttpRequestMessage表示)到服务端上,从服务端接收响应(用HttpResponseMessage表示)。用HttpContent基类和它派生出的类,表示对象body和每个request或response的content头部,比如StreamContent,MultipartContent和StringContent。它们表示各种http实体body内容。这些类都会提供ReadAs开头的一组方法,它能从请求或响应实体body中,以字符串形式、字节数组、流形式读取内容。
每一个HttpClient对象下都有一个处理者对象,它表示client下所有与HTTP相关的配置。从概念上来说,可以认为它是client部分下HTTP协议栈的代表。在客户端发送HTTP请求到服务端和传输数据到客户端上,它是非常可靠的。
在System.Net.Http API中默认处理者是HttpClientHandler。当你创建HttpClient对象实例时,会使用默认HTTP stack设置,自动帮你创建一个HttpClientHandler。如果你想修改默认一些设置,比如缓存行为,自动压缩,证书或代理,可以直接创建一个HttpClientHandler实例,修改它的属性,把它当做HttpClient构造函数的参数传入。这样HttpClient对象就会使用我们自定义的处理器,如下:
HttpClientHandler myHandler = new HttpClientHandler();
myHandler.AllowAutoRedirect = false;
HttpClient myClient = new HttpClient(myHandler);
链式处理器
System.Net.Http.HttpClient API设计中一个重要优势是:能够插入自定义处理器、在HttpClient对象下创建一连串的处理器。例如:构建一个app,它从web服务中请求一些数据。这时就可以自定义逻辑去处理HTTP服务端响应的4xx (客户端错误)和5xx (服务端错误),使用具体的重试步骤,比如尝试不同的端口请求或添加一个用户认证。 还可能会想从业务逻辑部分分离出HTTP相关的工作,它只关心web服务的数据返回。
这就可以使用自定义处理器类来完成,它从DelegatingHandler派生出,例如CustomHandler1,然后创建一个新实例,把它传入HttpClient构造函数。 DelegatingHandler类的InnerHandler属性被用指定下一个处理器,比如,可以添加个新的自定处理器(例CustomHandler2)到处理链上。处理链上最后一个处理者的InnerHandler,可以设置成HttpClientHandler的实例,它将传递请求到系统的HTTP协议栈上。 从概念上来看如下图:
下面是完成这部分的例子代码:
public class CustomHandler1 : DelegatingHandler { protected async override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { Debug.WriteLine("Processing request in Custom Handler 1"); HttpResponseMessage response = await base.SendAsync(request, cancellationToken); 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); } }
说明:
如果你试图发送一个请求到远程服务器端口上,其链上最后的处理器通常是HttpClientHandler,它实际是从系统HTTP协议栈层面发送这个请求或接收这个响应。作为一种选择,可以使用一个模拟处理器,模拟发送请求到服务器上,返回一个伪造的响应,这可以用来单元测试。
在传递请求到内部处理器之前或响应处理器之上,添加一个处理逻辑,能减少性能消耗。这个处理器场景下,最好能避免使用耗时的同步操作。
关于链式处理概念的详细信息,可以看Henrik Nielsen的这篇博客,(注意文章参考的是ASP.NET Web API的API版本。它和本文讨论的.NET framework有一些细微的不同,但在链式处理器上的概念是一样的)
Windows.Web.Http
Windows.Web.Http API的对象模型跟上面描述的System.Net.Http版本非常 ,它也有client entity的概念,一个处理器(在这叫“filter”过滤器),及在client和系统默认过滤器之间选择是否插入自定义逻辑。
其大多数类型是直接类似于System.Net.Http的类型的,如下:
在上面关于System.Net.Http API的链式处理器讨论,也可应用于Windows.Web.Http API,这里你可以创建自定义链式过滤器,传递它们到HttpClient对象的构造函数中。
HTTP的常用功能
关于HttpClient APIs中的大多数HTTP功能的通用实现,都能在网上或书上找到一些代码片段和相应介绍说明。关于完整的细节和指导,请查看Windows.Web.Http.HttpClient和System.Net.Http.HttpClient API各自的MSDN文档。
修改头部
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。
说明
一些头部是用集合表示的,要使添加和移除方法去编辑它们。
HttpClient.DefaultRequestHeaders属性表示默认头部集合,它会在App层添加到头部。请求会在操作系统协议栈上被处理,附加的头部会在数据通过网卡发送之前被添加。
设置超时
System.Net.Http:
在the System.Net.Http API中,有两个方式去设置超时。 在client部分上设置所有请求的超时时间,使用:
myClient.Timeout = TimeSpan.FromSeconds(30);
在单个请求上设置超时,使用删除token方式:
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(30));
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上没有超时属性,因此,必须使用上面介绍的删除token方式实现超时功能。
使用身份验证凭据
System.Net.Http:
为了保护用户凭据信息,默认情况下Http协议栈在请求发出时,不能添加任务身份验证信息。如需要使用指定用户验证,使用下面的模式:
var myClientHandler = new HttpClientHandler(); myClientHandler.Credentials = new NetworkCredential(myUsername, myPassword);
Windows.Web.Http:
对于Windows.Web.Http API,默认情况下,如果发出的请求是一个资源请求,会提供一个UI对话框,它要求用户进行确认。如想禁用这个UI对话框,设置HttpBaseProtocolFilter的属性AllowUI为false。 使用指定的验证替代:
var myFilter = new HttpBaseProtocolFilter();
myFilter.ServerCredential = new PasswordCredential(“fooBar”, myUsername, myPassword);
说明
-
在上面的例子中,myUsername和myPassword是一个string字符串变量,通常是从用户UI输入或app配置设置中获得。
-
在UWP应用中,HttpClientHandler.Credentials能被设置为NULL,DefaultCredentials,类型NetworkCredential等值。
使用客户端证书
System.Net.Http:
为了保护用户凭据信息,默认情况下API不会发送任何客户端凭据到服务器上。 使用客户端凭据认证代码如下:
var myClientHandler = new HttpClientHandler();
myClientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
Windows.Web.Http:
使用客户端凭据认证有两个选项,默认是提供UI给用户选择一个证书信息。作为一种选择,你可以用程序设置一个客户端证书,如下:
var myFilter = new HttpBaseProtocolFilter(); myFilter.ClientCertificate = myCertificate;
说明:
-
为在二者API中任意一个使用客户端证书,你必须添加它到app的证书商店里,参考连接的这些构造。在“My”中企业APP也能使用已经存在的客户端证书。
-
对于HttpClientHandler.ClientCertificateOptions来说,这有两个值可以设置:Automatic和Manual。设置Automatic会从APP证书商店里选择一个最匹配的客户端证书,用它来认证。设置Manual会确保不会发送客户端证书,即使服务器请求它。
代理设置
对与二者APis来说,代理设置会自动从IE/Edge浏览器中获得,它被所有的Http请求默认调用。这确保了,即使用户通过一个代理上网,也能自动连接工作。 二者API都不能再APP中提供一种方式去指定一个自定义的代理。不论如何,你可以选择设置HttpClientHandler.UseProxy(System.Net.Http中)为false不使用默认代理设置,在Windows.Web.Http设置HttpBaseProtocolFilter.UseProxy为false。
cookie处理
默认情况下,二者APIs都保存通过服务器发送的cookies,在相同的app容器内,自动添加上Cookies到那个URL的后续请求上。 这些Cookies被那个明确的URL读取, 添加新的自定义cookies。 二者APIs都有一个选项能禁止发送cookies到服务器上:在System.Net.Http上设置HttpClientHandler.UseCookies为false,在Windows.Web.Http设置HttpBaseProtocolFilter.CookieUsageBehavior为HttpCookieUsageBehavior.NoCookies。
System.Net.Http:
在client处理器上,添加一个cookie到所有的请求上:
// 手工添加一个cookie 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:
通过client,添加一个cookie到所有的发送请求上:
// 手工添加一个cookie filter.CookieManager.SetCookie(myCookie);
添加一个cookie到单个请求上,这个模式使用和上面的Windows.Web.Http API上是相同的。
管理cookies:
// 从一个指定URI上获取所有的cookies。 var cookieCollection = filter.CookieManager.GetCookies(resourceUri);
// 删除一个cookie。 filter.CookieManager.DeleteCookie(myCookie);
补充:
Windows.Web.Http API中,对于这几个APIs来说,cookie管理器中的这些cookies都是共享的,因为它们都是在WinINet栈上实现的,比如:Windows.Web.Syndication, Windows.Web.AtomPub, XHR和其他的。因此无论使用哪个api,都能通过服务器对请求的响应中获得cookie, 也可能会添加cookie到一个后续的HttpClient请求中,到同样的服务器中。
每台服务器的最大连接数
在操作系统的HTTP协议栈下,对每台服务器默认连接数是6。System.Net.Http HttpClient API不能提供一个方式去控制它,但在Windows.Web.Http API下是可以的,使用:
var myFilter = new HttpBaseProtocolFilter(); myFilter.MaxConnectionsPerServer = 15;
最新更新
在windows10 UWP apps中,二者APIs都添加了对HTTP/2的默认支持。作为一个开发者,可以很好的利用这些优势,比如不需要代码变动就能降低延迟。 二者APIs(System.Net.Http和Windows.Web.Http)也允许明确禁止这项特性和强制使用HTTP 1.1或1.0.
从目前开始,我尝试继续添加一些高级请求特性,比如自定义服务器ssl证书的生效,在所有的地方都能添加处理器/过滤器到HttpClient对象上。为了在windows上写出优秀的apps,我们很期待听到你需要在这些API上增加新特性。 你也可以在UserVoice提出想法。也可以加入 Windows Insiders program ,通过论坛或Windows Feedback app提交反馈。
这篇博客是微软网络APIs团队成员Sidharth Nabar写的。
译自 http://blogs.windows.com/buildingapps/2015/11/23/demystifying-httpclient-apis-in-the-universal-windows-platform/
题外话
这篇在上月底翻译的,一直没来得及整理。新工作在张江,原先住的太远单程一小时多,然后找房子、搬家、新工作环境适应。本准备要做win10开发,所以边研究win10边翻译了几篇,后来调整为优先桌面开发,开始研究hybird相关开发。
如果您认为这篇文章还不错或者有所收获,可以点击右下角的【推荐】按钮,因为你的支持是我继续写作,分享的最大动力!