下載 Web Services Enhancements 1.0 for Microsoft .NET(英文)。
簡介Web Services Enhancements 1.0 for Microsoft .NET (WSE) 是一個類庫,用于實現高級 Web 服務協議。WSE 的體系結構模型基于處理入站和出站 SOAP 消息的過濾器管道。過濾器可以與 ASP.NET Web 服務基礎結構集成在一起,也可以單獨使用。本文深入探討了 WSE 管道技術的工作原理,介紹了有關單獨過濾器和過濾器管道的工作原理、配置默認管道的方法、創建自定義過濾器的方法以及 DIME 適合圖片的特點。
以過濾器為中心的模型WSE 是一種將高級 Web 服務協議應用到 SOAP 消息的引擎。它要求向出站 SOAP 消息寫入標頭,從入站 SOAP 消息讀取標頭;它還要求轉換 SOAP 消息正文。例如,按照 WS-Security 規范中定義的那樣加密出站消息正文和解密入站消息正文。在 WSE 中,這種功能通過過濾器來實現。輸出過濾器向消息寫入標頭,輸入過濾器從消息讀取標頭并檢查標頭的有效性。此外輸出和輸入過濾器都可以轉換消息的內容。圖 1 說明了 WSE 過濾器模型。
圖 1:Web Services Enhancements 的過濾器模型
使用單獨的過濾器了解 WSE 如何使用過濾器的最好方法是從一個簡單的例子開始。WSE 提供了一對過濾器,用于讀寫時間戳標頭。時間戳標頭包含了一些元素,用來表示消息的創建時間和過期時間,指明消息的周期以及何時可以認為該消息失效。時間戳過濾器在 Microsoft.Web.Services.Timestamp 命名空間中定義。顧名思義,TimestampOutputFilter 是一個輸出過濾器,用于向 SOAP 消息寫入時間戳標頭;而 TimestampInputFilter 是一個輸入過濾器,用于從 SOAP 消息讀取時間戳標頭。以下是它們各自的定義:
public class TimestampOutputFilter : SoapOutputFilter public class TimestampInputFilter : SoapInputFilter |
兩個類都有一個 ProcessMessage 方法,該方法帶有一個 SoapEnvelope 類型的參數。Microsoft.Web.Services.SoapEnvelope 類是標準 .NET XML DOM API System.Xml.XmlDocument 的擴展。它具有驗證邏輯,可以驗證包含有效 SOAP 消息的文檔內容;它還具有一些快捷方法和屬性,可以創建和訪問消息的特定部分,即 Envelope、Header 和 Body 元素。
以下是一個使用時間戳過濾器的簡單應用程序。
static void Main(string[] args) // 打印原始消息 // 創建時間戳輸出過濾器 // 打印輸出過濾后的消息 // 創建時間戳輸入過濾器 // 處理消息,讀取時間戳標頭 // 打印輸入過濾的消息 |
本程序首先創建一個 SoapEnvelope 對象,并添加空白的消息正文。然后,創建一個 TimestampOutputFilter 并使用它來處理 SoapEnvelope,向消息寫入時間戳標頭。最后,該程序創建一個 TimestampInputFilter 并使用它來處理 SoapEnvelope,從消息讀取時間戳標頭。程序執行的每一步都將消息內容打印到控制臺。以下是輸出結果(已經過格式編排)。
原始消息:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body/> </soap:Envelope> |
輸出過濾后的消息:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <wsu:Timestamp xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility"> <wsu:Created>2002-11-14T19:03:27Z</wsu:Created> <wsu:Expires>2002-11-14T19:08:27Z</wsu:Expires> </wsu:Timestamp> </soap:Header> <soap:Body /> </soap:Envelope> |
輸入過濾后的消息:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> </soap:Header> <soap:Body /> </soap:Envelope> |
原始的消息只包含一個空白的 Body 元素,沒有其他內容。當 TimestampOutputFilter 處理消息時,它用表示消息創建時間和過期時間的元素寫入時間戳標頭。而當 TimestampInputFilter 處理消息時,則讀取時間戳標頭。如果到了過期時間,TimestampInputFilter 將拋出一個異常。
輸出過濾器通過 Microsoft.Web.Services.SoapContext 類來管理。每個 SoapContext 對象使用簡單的對象模型來記錄特定的協議選項,例如是否存在用戶名標記或數字證書、創建和過期時間戳、路由路徑等。SoapEnvelope 類具有 Context 屬性,該屬性是 SoapContext 類的一個實例。當 SoapEnvelope 由輸出過濾器處理時,是由 SoapContext 對象中的數據來告訴輸出過濾器進行什么操作。
例如,TimestampOutputFilter 實際通過檢查 SoapEnvelope 的 SoapContext 對象的屬性(尤其是 SoapEnvelope.Context.Timestamps.Ttl)來設置消息的過期時間。如果您希望將消息的過期時間設置為 10 分鐘,只需要按照如下所示設置該值:
// 創建封裝 SoapEnvelope env = new SoapEnvelope(); ... // 以毫秒為單位,將過期時間設置為 10 分鐘 env.Context.Timestamps.Ttl = 600000; |
WSE 輸入過濾器也依賴于 SoapContext 類,但是其用途不同。當輸入過濾器處理 SoapEnvelope 對象時,會更新它的 SoapContext 對象,以反映封裝包含的協議標頭。例如,TimestampInputFilter 處理消息之后,SoapEnvelope.Context.Timestamps.Created 屬性會反映消息的創建時間。
在管道中使用多個過濾器前面展示的時間戳輸入和輸出過濾器僅僅是 WSE 所帶的 10 種內置過濾器中的兩種。表 1 列出了所有內置過濾器,包括它們所在的命名空間、輸入和輸出過濾器類型名以及簡單的功能描述。
表 1:Web Services Enhancements 中的 WSE 內置過濾器
命名空間 | 輸入過濾器 | 輸出過濾器 | 用途 |
---|---|---|---|
Microsoft.Web.Services.Diagnostics | TraceInputFilter | TraceOutputFilter | 向日志文件寫入信息,以幫助調試 |
Microsoft.Web.Services.Security | SecurityInputFilter | SecurityOutputFilter | 身份驗證、簽名和加密支持 (WS-Security(英文)) |
Microsoft.Web.Services.Timestamp | TimestampInputFilter | TimestampOutputFilter | 時間戳支持 (WS-Security(英文)) |
Microsoft.Web.Services.Referral | ReferralInputFilter | ReferralOutputFilter | 動態更新路由路徑 (WS-Referral(英文)) |
Microsoft.Web.Services.Routing | RoutingInputFilter | RoutingOutputFilter | 消息路由 (WS-Routing(英文)) |
表 1 中最右邊的列清楚地說明了每對 WSE 過濾器的功能。一般來說,您可能希望將多個輸入或輸出過濾器結合起來,應用到給定的 SOAP 消息 en masse。為此,WSE 提供了 Microsoft.Web.Services.Pipeline 類,它的定義如下。
public class Pipeline public SoapInputFilterCollection inputFilters { get; } public void ProcessInputMessage(SoapEnvelope envelope); |
每個 Pipeline 對象封裝一個輸入過濾器集合和一個輸出過濾器集合,它們分別由 Microsoft.Web.Services.SoapInputFilterCollection 和 Microsoft.Web.Services.SoapOutputFilterCollection 類的實例來表示。這些集合通過構造函數進行初始化,并作為屬性提供。Pipeline.ProcessInputMessage 和 Pipeline.ProcessOutputMessage 方法在相應的過濾器集合中進行簡單的迭代,依次傳遞提供的 SoapEnvelope 對象。
下面是一個使用 Pipeline 類的簡單程序,它使用多個輸出過濾器來處理消息。特別是使用了 WSE 中內置的 TraceOutputFilter 和 TimestampOutputFilter 類。
static void Main(string[] args) // 創建輸出過濾器集合 // 添加所需的輸出過濾器 // 創建空白的 SOAP 消息 // 打印原始消息 // 創建管道,封裝過濾器集合 // 使用管道的集合中的所有輸出過濾器 // 打印輸出過濾后的消息 |
此應用程序首先創建一個空的 SoapInputFilterCollection 對象。然后創建一個 SoapOutputFilterCollection 對象,并向其添加一個 TraceOutputFilter 對象和一個 TimestampOutputFilter 對象。接著使用這兩個集合來初始化一個新的 Pipeline 對象。然后使用 Pipeline 來處理空的 SoapEnvelope。程序的輸出結果如下。
原始消息:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body/> </soap:Envelope> |
輸出過濾后的消息:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <wsu:Timestamp xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility"> <wsu:Created>2002-11-14T23:17:48Z</wsu:Created> <wsu:Expires>2002-11-14T23:22:48Z</wsu:Expires> </wsu:Timestamp> </soap:Header> <soap:Body /> </soap:Envelope> |
過濾器處理的順序
在上例中,TimestampOutputFilter 向消息添加一個時間戳標頭,TraceOutputFilter 將消息寫入跟蹤文件。問題是向跟蹤文件寫入的消息是否包括時間戳標頭?答案是肯定的。乍看起來這似乎違反常理。SoapOutputFilterCollection.Add 方法將過濾器附加到集合的末尾,因此 TraceOutputFilter 比 TimestampOutputFilter 早出現,這意味著在添加時間戳之前,消息就已經被寫入跟蹤文件中。然而,結果卻是輸出過濾器逆序執行,也就是說,集合中最后的輸出過濾器最先處理消息。我給大家解釋一下原因。
考慮兩個會變更輸出消息內容的過濾器 TimetampOutputFilter 和 SecurityOutputFilter。如果 TimetampOutputFilter 要向出站消息添加時間戳標頭,就需要在 SecurityOutputFilter 加密消息之前進行。因此,輸出過濾器處理的順序非常重要。而且,當輸入消息到達時,SecurityInputFilter 必須先解密消息,然后 TimestampInputFilter 才能檢查消息是否過期。因此,輸入過濾器處理的順序也很重要,一般需要反轉相應輸出過濾器的順序。
Pipeline 類考慮了這種情形,下面是它的工作原理。過濾器集合中的第一個過濾器最“接近線路”,最后一個過濾器最“接近代碼”。對于輸出過濾器和輸入過濾器來說都是這樣。輸入過濾器始終按它們出現在集合中的順序進行處理,而輸出過濾器始終按相反的順序進行處理。所有這些都封裝在 ProcessInputMessage 和 ProcessOutputMessage 方法中。圖 2 說明了這種體系結構。
圖 2:Pipeline 類中的過濾器處理順序
Pipeline 類有充分的理由采用這種工作方式。如果輸入和輸出過濾器集合以相同的順序移動的話,您就必須在代碼中手動反轉一組過濾器的順序。通過在 ProcessOutputMessage 中使用逆序處理,Pipeline 類就將您從其中解放出來。您可以“按照相同的順序”向各自的集合中添加“對應的”輸入和輸出過濾器,而把其余的工作留給 Pipeline 去做。
默認管道在大多數應用程序中,您很可能希望 Pipeline 對象使用相同的輸入和輸出過濾器。為此,WSE 支持默認 Pipeline 配置。Microsoft.Web.Services.Configuration.WebServicesConfiguration 類提供了一個靜態 FilterConfiguration 屬性,它維護了一對過濾器集合,分別通過 InputFilters 和 OutputFilters 屬性來提供。當使用默認構造函數來實例化 Pipeline 類時,對默認過濾器的引用被復制到新對象的過濾器集合中。您可以針對每個 AppDomain 來修改默認的 Pipeline 配置。
使用內置 WSE 過濾器來預先配置默認的輸入和輸出過濾器集合。表 2 顯示了這些配置。
表 2:預先配置的默認 Pipeline 配置
索引 | 輸入過濾器 | 輸出過濾器 |
---|---|---|
0 | SecurityInputFilter | SecurityOutputFilter |
1 | TimestampInputFilter | TimestampOutputFilter |
2 | ReferralInputFilter | ReferralOutputFilter |
3 | RoutingInputFilter | RoutingOutputFilter |
請注意,索引較小的過濾器更“接近線路”,而索引較大的過濾器更“接近代碼”。如果在 WSE 中啟用跟蹤功能,則 TraceInputFilter 和 TraceOutputFilter 將被插入到默認過濾器集合的最前面,即 0 位置。這樣做很有意義,因為跟蹤過濾器的目標就是記錄消息出入線路的情況。
如果您愿意,可以修改 AppDomain 的默認過濾器集合。例如,如果您希望應用程序使用 WS-Security 而不是 WS-Routing 和 WS-Referral,則可以刪除路由和參照過濾器。一般來說,刪除不需要的過濾器可以減少必須檢查每個 SOAP 消息的對象數,進而提高應用程序的性能。
以下方法說明如何從默認的輸入和輸出過濾器中刪除路由和參照標頭。
public static void ReconfigureDefaultPipeline() // 刪除路由和參照輸入過濾器 // 檢索默認的輸出過濾器集合 // 刪除路由和參照輸出過濾器 |
值得注意的是,修改默認過濾器集合對現有的 Pipeline 對象沒有影響。只有在更改默認集合后創建的新 Pipeline 對象才會受到此修改的影響。例如,在以下代碼中,兩個 Pipeline 對象使用不同的過濾器。
static void Main(string[] args) // 第二個管道具有安全性和 ... // 使用管道 |
如果您結合使用 WSE 和 ASP.NET Web 服務,可以使用 Global.asax 文件來修改服務在應用程序啟動時的默認過濾器集合。下面是一個示例:
<%@ import namespace="Microsoft.Web.Services" %> <script runat="server" language="C#" > public void Application_OnStart() SoapOutputFilterCollection defaultOutputFilters = </script> |
WSE 過濾器體系結構可以擴展。您或許已經注意到了,TimestampOutputFilter 類擴展了 Microsoft.Web.Services.SoapOutputFilter,而 TimestampInputFilter 類擴展了 Microsoft.Web.Services.SoapInputFilter。SoapOutputFilter 和 SoapInputFilter 都是抽象基類。如果您希望自己編寫過濾器,只需要從相應的基類中派生出一個子類并重寫抽象 ProcessMessage 方法。
自定義過濾器有許多用途。以下是一個自定義過濾器,它檢查 Web 服務生成的輸出消息是否包含 SOAP Fault 元素。如果包含,則將錯誤內容寫到 Windows 事件日志中。此輸出過濾器不會修改消息。
public class SoapFaultOutputFilter : SoapOutputFilter // 如果有錯誤... // 將錯誤的內容格式化為字符串 // 將原始的錯誤正文寫入字節數組 sw.Close(); private void WriteToEventLog(string msg, string src, byte[] buf) |
SoapFaultOutputFilter.ProcessMessage 方法使用 XPath 來探測作為參數傳遞的 SoapEnvelope。它查找 SOAP Fault 元素。如果找到,則提取錯誤信息,將其格式化為字符串,然后傳遞給 WriteToEventLog 方法。WriteToEventLog 使用 System.Diagnostics.EventLog 類將錯誤信息寫入名為“WSEFaultLog”的自定義事件日志中,并在必要時創建該文件。值得注意的是,此過濾器僅查看服務本身生成的 SOAP Faults。如果由于消息簽名不正確,SecurityInputFilter 拒絕客戶端發送的消息,則此過濾器不會查看該消息。
SoapFaultOutputFilter 用于需要記錄向客戶端發送 SOAP Fault 情況的任意 Web 服務。它可以很容易地實現類似的輸入過濾器,以記錄客戶收到來自服務的 SOAP Fault 的情況,但是由于不需要這樣的實現,因此就沒有什么意義。WSE 不要求所有的輸出過濾器都有匹配的輸入過濾器,也不要求所有的輸入過濾器都有匹配的輸出過濾器。許多任務(例如記錄錯誤或按照架構驗證消息正文的內容)都可以采用非對稱的方式完成,即僅針對輸入或輸出消息,而不是同時針對兩者。
使用 SoapContext 與自定義過濾器通信內置 WSE 過濾器與應用程序其他部分的通信是通過附加到 SoapEnvelope 的 SoapContext 對象屬性來實現的。對于自定義過濾器,可以使用相同的技術。SoapContext 類包含一個 System.Collections.Hashtable 對象,用于存儲任意數據,因此以下方法間接公開了 Hashtable。
public class SoapContext { public void Add(string key, object value); public void Clear(); public bool Contains(string key); public IDictionaryEnumerator GetEnumerator(); public void Remove(string key); ... // 省略其他屬性和方法 } |
可以通過修改 SoapFaultOutputFilter 來使用 SoapContext,從而允許應用程序指定記錄錯誤時所使用的源字符串。在上面的 SoapFaultOutputFilter.ProcessMessage 實現中,源字符串被“硬編碼”為“WSE”:
// 將錯誤數據寫入事件日志
WriteToEventLog(msg, "WSE", buf);
下面是修改過的代碼,如果 SoapContext 存在,則從中提取源字符串,字符串的值由 FaultSource 指定:
// 將錯誤數據寫入事件日志 string src = "WSE"; if (envelope.Context.Contains("FaultSource")) src = envelope.Context["FaultSource"]; WriteToEventLog(msg, src, buf); |
對于其他更復雜的自定義過濾器,您可以使用此技術來研究 SoapContext 的所有附加對象模型。
配置自定義過濾器實現了自定義過濾器之后,必須配置 Pipeline 才能使用該過濾器??梢酝ㄟ^向應用程序的 .config 文件中添加一行來進行配置,如下所示。
<configuration> <microsoft.web.services> <filters> <!-- 請注意,您可以不對稱地添加自定義過濾器, 即僅添加輸出過濾器或僅添加輸入過濾器 --> <output> <add type="SoapFaultOutputFilter,WSEFilters" /> </output> </filters> </microsoft.web.services> </configuration> |
采用這種方法配置的自定義過濾器被自動添加到默認過濾器集合的末尾,因而它始終比內置的 WSE 過濾器更“接近代碼”。您也可以通過修改 AppDomain 默認過濾器集合,手動配置自定義過濾器,如下所示:
WebServicesConfiguration.FilterConfiguration.OutputFilters.Add( new SoapFaultOutputFilter(); |
如果您不希望將自定義過濾器附加到默認過濾器集合的末尾,則需要使用后一種方法。
關于 DIMEWSE 唯一不是通過輸入和輸出過濾器實現的重要功能是支持 DIME 和 WS-Attachments。DIME 規范定義了消息的二進制格式。WS-Attachments 規范說明了如何使用 DIME 將二進制數據附加到 SOAP 消息中。Pipeline 類使用的過濾器鏈將各個 SoapEnvelope 對象“就地”轉換。SOAP 消息與 DIME 記錄之間的映射要求進行從 SOAP 消息到二進制流或相反方向的不同格式轉換。這反映了一個事實:DIME 與其他 Web 服務協議不同,DIME 實際上不是基于 SOAP 的。簡而言之,當您使用 DIME 時,已超出了 SOAP 的范圍,WSE 過濾器模型不支持這種轉換,因此需要獨立的機制。
Microsoft.Web.Services.Dime 命名空間中的類實現了 WSE DIME 功能。DimeWriter 和 DimeReader 類為 DIME 消息提供了基于流的 IO。兩個類均使用 DimeRecord 對象來表示 DIME 消息中的各個記錄。調用 Pipeline.ProcessOutputMessage 之后,可以使用 DimeWriter 將出站 SOAP 消息轉換為 DIME 消息。與此類似,調用 Pipeline.ProcessInputMessage 之前,可以使用 DimeReader 將入站 DIME 消息轉換為 SOAP 消息。
大多數 Web 服務開發人員認為 DIME(與 WS-Attachments 一起)的功能是將二進制數據與 SOAP 消息一起發送,實際上 DIME 還有另一個更有趣的用途。如果您打算使用基于流的協議來發送一個 SOAP 消息,則需要某種方法來指定消息的大小,以便接收者了解消息何時結束以及下一個消息何時開始。當通過 HTTP 發送 SOAP 消息時,可以使用內容長度標頭來指定消息的大小。如果使用其他方法發送 SOAP 消息,則可以使用 DIME。以下是一個用一條記錄將 SOAP 消息轉換為 DIME 消息的函數。
public void EnvelopeToDIMEStream(SoapEnvelope env, Stream stm) // 生成新的記錄 id // 創建新記錄,指定內容將 // 將 SOAP 消息寫入 DIME 記錄 // 清除 |
而下面是與之對應的將 DIME 消息轉換為 SOAP 消息的函數。
public DIMEStreamToEnvelope(Stream stm, SoapEnvelope env) // 讀取包含 SOAP 消息的記錄 // 從 DIME 記錄讀取 SOAP 消息 // 確認沒有其他記錄 |
如果您使用 DIME 向 SOAP 消息附加任意數據,進行讀寫操作的代碼將變得更加復雜。WSE 的關鍵之處是它為作為 SoapEnvelope 對象中 SoapContext 的一部分的附件數據提供了保存場所。尤其是,DimeAttachmentCollection 類型的 SoapContext.Attachments 屬性可以為您保存 DimeAttachment 對象。每個 DimeAttachment 與一個 DIME 記錄相對應,包括 id、類型標識符、區塊大小以及二進制數據流。
小結Web Services Enhancements (WSE) 功能(主要)是通過使用處理入站和出站消息的過濾器來實現的。您可以單獨使用過濾器或者在管道中使用過濾器,也可以控制進程中管道的默認配置。還可以創建自定義過濾器,添加您需要的功能??傊?,WSE 提供了無比靈活且可擴展的體系結構。不管您是結合使用 WSE 與 ASP.NET Web 服務還是單獨使用 WSE,深入理解過濾器和管道的工作原理將有助于充分利用 WSE 提供的強大功能。
原文轉自:http://www.anti-gravitydesign.com