學習完本章,你將掌握:
1.在你的工作流中調用web服務
2.添加和配置web服務代理
3.在你的工作流中進行會話(sessions)管理
拿我來說,一些基於網絡進行數據的發送和接收的事情很吸引我,由此多年以來我很喜歡寫基於通信的代碼。當我看到wf內置了連接web服務和作為web服務的能力時,我就更深入地對其進行了研究。
wf集成了幾個基於xml web服務的活動,我們將在本章調查客戶端(client)一側的活動。(在最後一章“把工作流作為web服務”中我們將討論服務器一側的活動)。在實際使用invokewebservice活動之前,我認為我應該描述一下web服務是怎樣工作的,因為在本章以及最後一章中我們需要去理解該術語。
web服務架構
一個觀點是任何基於internet的根據你的請求來執行任務的服務器就是一個web服務。但是,在本章以及下一章我將用這個術語來和一個更為精確的解釋作對比。這裡,我將提到xml web服務,它是一個基於soap協議的服務。
備注:“soap”來源於simple object access protocol(簡單對象訪問協議)的首字母縮寫,它是一個基於xml的通信框架。但是,在當前soap的正式發行版本中,該協議就被簡單地稱為soap協議。你能在http://www.w3.org/tr/soap/找到最新的soap規范。
促使創建soap協議的基本的想法是意圖能把原有的數據類型甚至是復雜的數據結構從原來的二進制格式轉換到xml中,然後該xml就能在internet之上發送,就像通過使用html發送網頁一樣。起初的soap規范清晰地勾勒了“序列化格式(serialization format)”來對整數、字符串、枚舉到數組和復雜結構的描述。後來的規范隨著序列化描述語言(serialization descripton languages)的出現而對序列化格式進行了放寬。當前,選擇的序列化描述語言是web服務描述語言(web service description language),或者稱作wsdl。
wsdl可讓你的soap消息以你認為合適的內容進行格式化。在wsdl格式中你可以簡單方便地對內容進行描述並把你的wsdl提供給用戶,因此,使用你的服務,從中它們能對你的web服務輸出的結構和方法進行解釋。雖然原來的soap規范想把soap用在遠程方法調用方面,但是最新的規范加上了wsdl,允許用在更多的基於消息的架構中,消息的內容可以是你能序列化進xml中的任何東西。
因此,當你引用一個遠程的web服務的時候,你將下載並解釋該服務為你提供的wsdl。當你使用.net的時候,.net framework內置的工具會為你做這些解釋工作並為你創建一個c#類,你能使用該類去調用遠程服務的方法。這個c#類被稱作代理(proxy),因為它的行為是替換真正的服務。最終,除了通信延遲,你的軟件甚至不會知道它實際上是在和一個internent上的服務通信。該代理(proxy)使它看起來仿佛該服務就在你的本地計算機上。
再回顧一下,你可以引用一個web服務,屆時它會把它的wsdl發送給你。.net為你創建一個代理,你能使用它根據遠程服務器的規范(按照它的wsdl)去和遠程服務器進行通信。當你進行通信的時候,你發送的數據會被轉換到xml中並在(通信)線路上使用soap協議進行發送。遠程服務器對你的請求和響應進行解釋。當你接收到服務器的響應時,響應的xml會被轉化回你的代碼所要使用的二進制數據。
備注:對你來說,繼續討論web服務以及它們是如何執行的來說將是非常容易的,但是我們真正需要的是繼續深入並把它遷移進工作流中。但是,假如你想學習更多東西的話,這方面知識的許多的書籍和在線的參考文獻都是可以得到的。一個很不錯的起點是:http://msdn.microsoft.com/webservices/。
使用invokewebservice活動
wf內置的面向客戶端的xml web服務支持來源於invokewebservice活動。在許多方面,invokewebservice活動僅僅是你的一個代理,但是它新增了使用一個單一的session cookie來控制多個調用的能力。
在使用invokewebservice活動的時候會需要用到一些關鍵的屬性,它們如表18-1所示。
表18-1 invodewebservice活動的一些關鍵屬性
屬性 功能 methodname 獲取或設置在執行活動時所要調用的方法。這個屬性代表了你想調用的遠程方法的方法名稱。 proxyclass 獲取或設置代理類的類型名稱。你既可以親自提供它也可以在你拖拽該活動到你的工作流的時候通過創建一個代理類來讓wf幫你提供。 sessionid 獲取或設置要被使用的session。你可以使用這個機制來把不同的xml web服務調用全部都捆在一起。我將在“帶會話(session)協同工作”一節中更進一步地討論這個屬性。 url 獲取或設置要用來和xml web服務進行通信的url。盡管你能容易地使用屬性面板來為你的invokewebservice活動對它進行修改,但實際上該url本身被保存在你工作流項目的設置屬性包(settings property bag)中。
除了這些屬性,你可能還不時需要去為invoking和invoked事件提供事件處理程序。你將在本章的“配置代理”一節中看到在你配置你的代理的時候為什麼可能會需要去做這些工作。
使用invokewebservice活動是一件簡單的事情,只需把它拖拽進你的工作流中。當你開始使用的時候,microsoft visual studio會對服務器發送請求,以便它能檢索wsdl並創建其代理,然後會幫你配置該活動。假如你想自己創建該代理,可以簡單地取消visual studio彈出的對話框。假如你想讓visual studio來創建代理的話,你將要為通常的visual studio項目選中一個web引用,其對話框如圖18-1所示。
圖18-1 visual studio的“添加 web 引用”對話框用戶界面
一旦你添加了web引用,你就需要從列表中可獲取的方法中指定methodname並綁定任何可能需要的參數。
添加web引用
正如圖18-1所指出的,有四種方式來把xml web服務綁定到你的invokewebservice活動上。有三個鏈接很顯眼:此解決方案中的web服務,本地計算機上的 web 服務 ,浏覽本地網絡上的 uddi 服務器。當然,第四個方式是直接輸入url然後按下“前往”按鈕。
備注:uddi指統一描述、發現和集成(universal description, discovery, and integration)協議,它是一個基於xml的跨平台的描述規范,可以使世界范圍內的企業在互聯網上發布自己所提供的服務。但是,假如你使用的是windows server 2003的話,你能在你的本地網絡上使用uddi。可以參考下面的在線信息資源:http://www.microsoft.com/windowsserver2003/technologies/idm/uddi/default.mspx。
假如你選擇的是本地解決方案鏈接的話(可選列表中的第一個鏈接),visual studio會檢查你當前項目中的xml web服務項目。假如有任何發現的話,它們就會被呈現給你並且你能選中一個去進行綁定(一旦你選中了一個服務,就會激活“添加引用”按鈕,然後再點擊該按鈕)。
從你的本地服務器上選擇一個服務來讓visual studio在本地iis的控制執行下對本地的iis元數據庫中配置的xml web服務進行掃描。假如找到了任何的xml web服務的話,會為你把它們呈現在一個列表中供你選擇,你可以選擇你想要的服務。同樣,一旦你選擇了一個服務就將會激活“添加引用”按鈕。點擊它,然後visual studio會為你創建你的代理。注意,假如你想引用你本地硬盤驅動器上的wsdl文件的話,你需要通過使用文件協議(http://www.cnblogs.com/gyche/admin/file://%7bfilename/})來輸入url的名稱。
至於uddi的使用,假如你選中了這個選項並且在你的本地網絡上至少有一個uddi服務器可以使用的話,你就能使用uddi提供的服務來檢索並選擇一個你感興趣的xml web服務。只要有一個服務被發現,uddi就會為visual studio提供相關的服務的url,然後visual studo會再次為你創建你的代理。
在你添加web服務引用的時候要牢記的是在你選擇服務的時候你可以指定代理的名稱。這是很有用的,尤其是在你引用一個以上的服務時候。你指定的名稱會成為代理的命名空間的一部分。
假如你的web服務真正被放在internent上的話,你需要在位於圖18-1中對話框的頂部的url名稱中自己去提供它的url然後點擊“前往”按鈕。visual studio會為獲取它的wsdl而對該url進行查詢,然後會創建該代理就像該web服務就位於你的本地系統中一樣。
配置代理
一旦你添加了invokewebservice活動,在許多方面其實就和把一個web引用添加到一個標准的visual studio項目中是相似的,你需要去配置該代理。有兩種方式可以完成這項任務:靜態配置和動態配置
靜態代理配置
靜態代理配置通過設置visual studio屬性面板中的invokewebservice活動的屬性來進行。當你添加一個新的invokewebservice活動並綁定到一個web服務的時候(使用我們已經談到的“添加 web 引用”對話框),幾乎所有的工作都已完成並可以准備去使用了。網址是已知的並保存到了工作流項目的設置屬性包(settings property bag)中,代理的類型已被指定。(proxyclass屬性將有一個值。)methodname屬性則例外。
visual studio在解釋wsdl並創建代理的時候“了解”了你可以使用哪些有效的方法。指定一個要調用的方法就和拖拽一組可使用的有效方法然後從中選擇一個同樣簡單。除非該方法有可進行綁定的參數,否則的話你的配置就已經完成了。假如有傳入或者傳出的參數的話,你就需要把一個當前的字段或者依賴屬性綁定到這些參數的值上。在創建了methodname並綁定了該方法的屬性後,你就真正做好了使用invokewebservice來訪問xml web服務的准備。
動態代理配置
但是,任何東西一旦被設置為靜態的,我們看起來總是有重新把它配置為動態的需求。例如,或許你想動態地指定url而不是把它放到屬性的設置中,或者你可能需要在調用時分配憑證(credentials)以便能獲得對一個安全的web服務的訪問。而這些事情通過在visual studio中靜態地配置參數是不能做到的。
解決辦法在於處理invoking事件。假如你為invokewebservice活動的invoking事件提供了一個事件處理程序的話,你就能通過該事件的invokewebserviceeventargs參數獲得對代理類本身的訪問能力。invokewebserviceeventargs有一個名稱為webserviceproxy的屬性,它是用來調用該xml web服務的invokewebservice活動的代理的一個實例。通過簡單地把這個對象設置為你正使用的代理類型,你就能動態地更改為你需要的任何值,包含url和憑證(credentials)。
備注:對於關於怎樣訪問被一個代理服務器所保護的internet的信息,可以看看http://msdn2.microsoft.com/en-us/library/system.net.configuration.proxyelement(vs.80).aspx。對於訪問安全的xml web服務方面的信息描述可以看看http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetch10.asp,尤其是“將身份驗證的憑證傳遞給 web 服務 (passing credentials for authentication to web services)”一節。
帶會話(session)協同工作
sessions,在web用語中是指一組套接的請求-響應對。也就是說,假如你在一個調用中對web資源發出請求的話,你期望接下來的調用要和前面的調用保持聯系。這絕對不是超文本傳輸協議(http)的工作原理。http的開發目的是支持一個單一的請求-響應對。任何前面處理過的內容服務器都會完全把它們遺忘。
但是,作為web的用戶,我們需要記住web的請求-響應。最顯而易見的例子是基於web的購物車。當你在internet上訂購商品並且這些商品被搜集到一個虛擬的購物車中去的時候,你期望你選中的東西將可在一段時間後進行結帳操作。我們該如何協調http所支持的特性和我們作為用戶所需要的特性之間的差異呢?
答案是web應用程序來保持會話狀態(session state)。當你開始使用web應用程序的時候,你就初始化了一個session。該session會一直被跟蹤,直到你停止使用該web應用程序,或者是你注銷了應用程序(假如它是安全的),或者是你簡單地關閉了你的浏覽器的時候。
跟蹤會話狀態的一個常見的方式是使用cookie。cookie是http的真實載體(基於soap的xml,html或者其它任何東西)的請求-響應包之外的輔助數據容器。在服務器上,他們通常被加載到內存中並且可通過你的web應用程序進行訪問以便你跟蹤session信息。在客戶端,它們通常以文件的形式存儲起來。當你訪問指定的web資源的時候,任何打算傳送的cookies資源都從cookie文件夾中檢索而來並被傳回服務器。盡管這個過程不是唯一的保持session信息的方式,但這是一個常見的情況。
但是,xml web服務有點小的差異。其中一種情況是它們不會使用浏覽器進行調用,至少在使用wf的時候不會。由於這個原因,和一個xml web服務相聯系的cookie都不是基於文件的,而是僅僅存在於內存中或者在上線時和服務器來回傳送。
另外一個不同的地方是它們可能並不總是存在著,因此你不能依賴於它們的存在。假如xml web服務不是被配置為發送基於session的cookie的話,session繼續是不可能的。
提示:假如你使用.net framework來寫你的xml web服務並且你想讓session起作用的話,需要確認在webmethod的特性中使用了enablesession關鍵字來標注你的基於web的方法,以便打開session管理。默認情況下,session管理子系統在.net的xml web服務的操作中是被停用了的。
在你的需要時間,在有些情況下會是大量的時間來完成工作的工作流中,至少有一個比較好的理由來支持讓session管理可以使用。假如xml web服務要長時間運行,你就需要把相關的查詢組織在一起以便檢索長時間運行的服務的執行結果。
長時間運行的xml web服務
涉及到人的工作流自然會長時間運行。往往在等待某人進行響應或者進行操作的時候你還要維持你的工作流。
假如一個xml web服務因為一些原因需要人為干預,或者,如果把它設計為需要很長的時間來完成(就像等待訂單被履行)的話,你的客戶端工作流也會變成長時間運行的工作流。不要自始至終維持連接,這通常最容易導致停止處理,而是要等待一段時間,然後再次查詢該xml web服務以便查看它是否完成。已經發布了基於web通知的規范,例如ws-eventing,但是.net迄今為止還沒有實現。)
想像你對一個xml web服務進行了請求,然後停止處理了一段時間,最後啟動備份並再次查詢該服務。假如你沒有使用同一個session的話,通常服務器將很有可能拋出一個異常,除非有一些其它的機制來把已使用的對服務器的查詢聯系起來。
接下來就看你了,通過檢索由第一次xml web服務調用所創建的session cookie,然後復制它的信息並保存起來以便以後使用。假想session cookie是能夠使用的,為了創建一個副本,你要為第一個invokewebservice活動的invoked事件提供一個仿照如下代碼的事件處理程序:
firstwebmethodinvokedhandler
private void firstwebmethodinvokedhandler(object sender, invokewebserviceeventargs e)
{
mywebserviceproxy ws = e.webserviceproxy as mywebserviceproxy;
foreach (system.net.cookie cookie in ws.cookiecontainer.getcookies(new uri(ws.url)))
{
// the following values must be saved in order to re-create
// the cookie, there should be only one cookie, but just in
// case, you can examine them all if there is more than one,
string a = cookie.name; // (persist values for later recall)
string b = cookie.path;
string c = cookie.value;
string d = cookie.domain;
}
}
你在變量a,b,c和d中保存了字符串的值以便在以後使用。隨後的調用必定要使用相同的session cookie,然後就使用以前保存在a,b,c和d中的信息來重新創建下一次也許是要請求查詢狀態或者結果的調用所需要的cookie。在下面的例子中,處理了接下來的invokewebservice的invoking事件:
nextwebmethodinvokinghandler
private void nextwebmethodinvokinghandler(object sender, invokewebserviceeventargs e)
{
// create the cookie from persisted values.
system.net.cookie cookie = new system.net.cookie();
cookie.name = a; // (values from previous invoking handler)
cookie.path = b;
cookie.value = c;
cookie.domain = d;
// add the cookie to the cookie collection going to the
// remote server.
mywebserviceproxy ws = e.webserviceproxy as mywebserviceproxy;
ws.cookiecontainer.add(cookie);
}
注意,這種方式只有在工作流實例被持久化或者以其它的方式從內存中移除後才是必要的。如果兩個session相互關聯的invokewebservice活動在相同的工作流實例執行中使用的話,只要它們共享相同的sessionid值,該cookie就會被傳給每一個xml web服務的方法調用。
創建一個使用了xml web服務的工作流
我已創建了一個包含幾個項目的應用程序:一個驅動工作流的控制台應用程序,一個返回股票代碼信息的web服務以及一個基本的順序工作流庫。我把該解決方案稱作quoteretrieve,它使用了你在第10章的范例應用程序中用過的三個相同的股票代碼。我們就來開始該范例的工作並從工作流中調用一個xml web服務。
向你的工作流中添加一個invokewebservice活動
1.本范例同樣為你提供了兩個版本:完整版本和非完整版本。你需要下載本章源代碼,打開“quoteretrieve”目錄中的解決方案。
2.找到quoteflow項目中的workflow1.cs文件,然後在解決方案資源管理器的右鍵菜單上點擊“視圖設計器”按鈕。這將打開工作流視圖設計器。
3.該工作流此時是空的,因此讓我們來添加它的第一個活動。拖拽一個invokewebservice活動到設計器界面上。
4.盡管你可能期望看到你貫穿本書看到過的界面效果:在設計器界面上呈現一個圓角矩形,其實呈現給你的是visual studio的“添加 web 引用”對話框。因為在這個項目中並沒有識別到之前創建的要使用的一個web引用,因此我們要完成web引用的添加以及初始化invokewebservice活動的屬性設置。點擊“此解決方案中的 web 服務”鏈接。
5.呈現出的項目中的xml web服務只有quoteservice。點擊該“quoteservice”鏈接。
備注:quoteservice預先填充了一個在第10章中使用過的,包含了三只股票代碼並對它們的值進行了初始化的dictionary對象。然後把該dictionary對象放到asp.net的緩存(cashe)中以便在調用該xml web服務的時候使用它。(對於初始化該緩存的代碼可以看看global.asax。)
6.visual studio要工作一會兒:它正為該服務加載wsdl並對該服務暴露的方法進行解析。一旦它加載完wsdl並為你呈現出該服務所暴露的方法後,“添加引用”按鈕就能使用了。點擊該按鈕把該web引用添加到你的項目中。
visual studio同時也會添加相似的圓角矩形到工作流視圖設計器中,這就是你剛才拖拽到你的工作流中去的invokewebservice活動。
7.invokewebservice1的大多數設置已經由visual studio為你完成了。但是你必須通知visual studio你想去調用該invokewebservice活動的哪些方法。因此在屬性面板中,選中methodname屬性,這將激活下拉箭頭,然後點擊該下拉箭頭。從列表中選中getquote。
8.選擇該方法名將導致visual studio去檢查該方法並把該方法所支持的參數添加到屬性面板中。選擇(return value)屬性將激活熟悉的浏覽(...)按鈕。點擊該浏覽按鈕將打開“將‘(returnvalue)’綁定到活動的屬性”對話框。點擊“綁定到新成員”選項卡,在“新成員名稱”字段中輸入stockvalue,確認選擇的是“創建屬性”選項。點擊“確定”將把stockvalue依賴屬性添加到你的工作流中。
9.為symbol屬性做同樣的工作,把它的依賴屬性命名為symbol。
10.對本工作流來說,invokewebservice活動現在就完全配置好了,因此拖拽一個code活動到工作流視圖設計器界面上並把它放到invokewebservice1的下面。指定它的executecode屬性為displayquotevalue。
11.visual studio添加displayquotevalue方法後會自動為你切換到代碼編輯器界面下。為visual studio剛剛為你添加這個displayquotevalue方法添加如下這些代碼:
displayquotevalue 方法
if (stockvalue >= 0)
{
// found the stock value.
console.writeline("the value for '{0}' is: {1}", symbol, stockvalue.tostring("c"));
}
else
{
// unknown stock.
console.writeline("stock symbol '{0}' is unknownplease try" +
" again using a valid stock symbol.", symbol);
}
12.編譯整個解決方案。糾正任何可能出現的編譯錯誤。
13.這個主應用程序只需要所期望的股票代碼這一個唯一的命令行參數。該xml web服務只有三只股票代碼的信息,它們是:cont,litw,和tspt。因此,你只能向應用程序中傳送這三個值中的一個。假如你執行該應用程序並傳入了cont股票代碼的話,該程序的輸出結果就像下面所呈現的一樣。
股票的輸出值將會是變化的,因為該xml web服務執行模擬過程的時候使用了隨機數,但該應用程序應該仍然會執行並從xml web服務中檢索股票的值。假如存在問題,反饋回來的方便進行調試的異常信息會被顯示出來。一般來說,你所有需要去做的事就是重新啟動一下web服務。
本文配套源碼