揭秘Windows10 UWP中的httpclient接口[2]


閱讀目錄:

  1. 概述
  2. 如何選擇
  3. System.Net.Http
  4. Windows.Web.Http
  5. HTTP的常用功能
  6. 修改http頭部
  7. 設置超時
  8. 使用身份驗證憑據
  9. 使用客戶端證書
  10. cookie處理

概述

作為一個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;

說明:

  1. 為在二者API中任意一個使用客戶端證書,你必須添加它到app的證書商店里,參考連接的這些構造。在“My”中企業APP也能使用已經存在的客戶端證書。

  2. 對於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相關開發。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM