601
技術社區[雲棲]
[WCF REST] WebHttpBinding與消息編碼
不論是我們采用SOAP還是REST架構風格,運行時框架體係依然不曾改變,終結點也仍舊是通信的核心。在Web HTTP編程模型中,我們采用基於WebHttpBinding綁定的終結點。綁定是一組相關綁定元素的有序組合,綁定的特性與能力決定於它包含的綁定元素,在這裏我們通過分析綁定元素的方式來剖析WebHttpBinding綁定與其它綁定有何不同。采用HTTP/HTTPS通信協議的WebHttpBinding具有一些與WSHttpBinding/WS2007HttpBinding相同的屬性,在這裏我們隻關心如下定義的Security屬性。
1: public class WebHttpBinding : Binding, IBindingRuntimePreferences
2: {
3: //其它成員
4: public WebHttpSecurity Security { get; }
5: }
6: public sealed class WebHttpSecurity
7: {
8: // 其它成員
9: public WebHttpSecurityMode Mode { get; set; }
10: public HttpTransportSecurity Transport { get; }
11: }
12: public enum WebHttpSecurityMode
13: {
14: None,
15: Transport,
16: TransportCredentialOnly
17: }
基於SOAP的綁定一般具有兩種基本的安全模式,即Message和Transport模式。對於前者,它是完全建立在WS-Security為核心的安全協議之上的,而整個WS-*協議簇都是基於SOAP的,所以自然不能應用在WebHttpBinding上,所以它隻能通過HTTPS提供針對Transport模式的安全支持。具體來說,通過枚舉WebHttpSecurityMode表示的安全模式具有如下三個選項:
- None:HTTP 請求未使用任何安全性;
- Transport:HTTP 請求使用傳輸級安全性;
- TransportCredentialOnly:僅提供基於 HTTP 的客戶端身份驗證。
現在我們根據上述三種不同的安全模式創建相應的WebHttpBinding對象,然後通過如下的程序在控製台中答應出所有的綁定元素類型。
1: static void Main(string[] args)
2: {
3: WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.None);
4: ListBindingElements(binding);
5:
6: binding = new WebHttpBinding(WebHttpSecurityMode.Transport);
7: ListBindingElements(binding);
8:
9: binding = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly);
10: ListBindingElements(binding);
11: }
12: static void ListBindingElements(WebHttpBinding binding)
13: {
14: int index = 1;
15: Console.WriteLine(binding.Security.Mode + ":");
16: foreach (var element in binding.CreateBindingElements())
17: {
18: Console.WriteLine("{0}. {1}", index++, element.GetType().FullName);
19: }
20: Console.WriteLine();
21: }
上述的程序執行之後會在控製台上產生如下的輸出,從中我們不難看出三個WebHttpBinding均由一個消息編碼元素和傳輸元素組成,我們知道這兩種綁定元素最所有類型的綁定所必需的。
1: None:
2: 1. System.ServiceModel.Channels.WebMessageEncodingBindingElement
3: 2. System.ServiceModel.Channels.HttpTransportBindingElement
4:
5: Transport:
6: 1. System.ServiceModel.Channels.WebMessageEncodingBindingElement
7: 2. System.ServiceModel.Channels.HttpsTransportBindingElement
8:
9: TransportCredentialOnly:
10: 1. System.ServiceModel.Channels.WebMessageEncodingBindingElement
11: 2. System.ServiceModel.Channels.HttpTransportBindingElement
對於WebHttpBinding的兩個綁定元素來說,由於它通過HTTPS提供針對Transport安全的支持,所以當安全模式為Transport時對應的傳輸綁定元素為HttpsTransportBindingElement,對於其餘兩種安全模式則直接采用HttpTransportBindingElement作為傳輸綁定元素。現在我們著重討論是作為消息編碼綁定元素的WebMessageEncodingBindingElement類型,以及它涉及的消息編碼機製。
我們先來看看WebMessageEncodingBindingElement的基本的定義。如下麵的代碼片斷所示,它是MessageEncodingBindingElement的子類,並且具有與TextMessageEncodingElement類似的屬性定義。其中MaxReadPoolSize和MaxWritePoolSize表示表示無需分配新的XmlDictionaryReader/XmlDictionaryWriter便可以讀取的便可同時讀取/寫入的最大消息數,默認值分別是64和16。ReaderQuotas屬性返回用於約束讀取的XML的複雜度的XmlDictionaryReaderQuotas對象,而WriteEncoding屬性表示采用的字符編碼類型,默認采用UTF-8編碼方式。由於WebHttpBinding不使用SOAP,表示消息版本的MessageVersion屬性自然返回None,如果我們對該屬性進行設置,指定的屬性值也隻能是MessageVersion.None。
1: public sealed class WebMessageEncodingBindingElement : MessageEncodingBindingElement,...
2: {
3: //其它成員
4: public override MessageEncoderFactory CreateMessageEncoderFactory();
5:
6: public bool CrossDomainScriptAccessEnabled {get; set; }
7: public WebContentTypeMapper ContentTypeMapper { get; set; }
8:
9: public int MaxReadPoolSize { get; set; }
10: public int MaxWritePoolSize { get; set; }
11: public override MessageVersion MessageVersion { get; set; }
12: public XmlDictionaryReaderQuotas ReaderQuotas { get; }
13: public Encoding WriteEncoding { get; set; }
14: }
除此之外,WebMessageEncodingBindingElement具有CrossDomainScriptAccessEnabled 和ContentTypeMapper這兩個重要的屬性。前者表示是否支持跨域(Corss-Domain)腳本訪問,默認值為False。後者類型為WebContentTypeMapper。WebContentTypeMapper用於進行消息的內容類型(Content Type,有時候也成為媒體類型或者MIME類型)與具體的格式(比如XML、JSON等)之間的映射。
1: public abstract class WebContentTypeMapper
2: {
3: protected WebContentTypeMapper();
4: public abstract WebContentFormat GetMessageFormatForContentType(string contentType);
6: }
7: public enum WebContentFormat
8: {
9: Default,
10: Xml,
11: Json,
12: Raw
13: }
如上麵的代碼所示,WebContentTypeMapper是一個抽象類,包含的唯一的抽象方法GetMessageFormatForContentType用於根據指定的內容類型返回與之匹配的通過枚舉WebContentFormat表示的內容格式。WebContentFormat枚舉的Xml、JSON和Raw體現了Web HTTP編程模型支持三種基本格式,其中Raw表示原始的二進製。
最終的消息編碼/解碼工作是通過繼承自MessageEncoder的消息編碼器實現的,消息編碼器又是通過繼承自MessageEncoderFactory的編碼器工廠創建出來的,而消息編碼綁定元素最終通過方法CreateMessageEncoderFactory創建了編碼器工廠。對於WebMessageEncodingBindingElement來說,它的CreateMessageEncoderFactory方法會創建一個具有如下定義的WebMessageEncoderFactory對象。
1: internal class WebMessageEncoderFactory : MessageEncoderFactory
2: {
3: public WebMessageEncoderFactory(Encoding writeEncoding, int maxReadPoolSize,int maxWritePoolSize, XmlDictionaryReaderQuotas quotas,
4: WebContentTypeMapper contentTypeMapper, bool javascriptCallbackEnabled);
5: public override MessageEncoder Encoder { get; }
6: public override MessageVersion MessageVersion { get; }
7: }
8:
WebMessageEncoderFactory是一個繼承自MessageEncoderFactory的內部類型。除了布爾類型的參數javascriptCallbackEnabled對應著WebMessageEncodingBindingElement的屬性CrossDomainScriptAccessEnabled之外,WebMessageEncoderFactory的構造函數參數與WebMessageEncodingBindingElement的同名屬性一一對應。代表消息版本的MessageVersion屬性依然返回None,而真正用於最終消息編碼/解碼工作的是通過Encoder屬性返回的具有如下定義的WebMessageEncoder對象。
1: internal class WebMessageEncoderFactory : MessageEncoderFactory
2: {
3: private class WebMessageEncoder : MessageEncoder
4: {
5: //其他成員
6: private MessageEncoder TextMessageEncoder { get; }
7: private MessageEncoder JsonMessageEncoder { get; }
8: private MessageEncoder RawMessageEncoder { get; }
9: }
10: }
如上麵的代碼片斷所示,WebMessageEncoder實際上是內嵌於WebMessageEncoderFactory類型中繼承自MessageEncoder的內部類型。WebMessageEncoder本身並不真正地實施消息的編碼/解碼,最終的消息編碼/解碼工作是通過三個屬性TextMessageEncoder、JsonMessageEncoder和RawMessageEncoder體現的具體消息編碼器完成的,而它們又分別對應著通過WebContentFormat枚舉表示的三種內容類型Xml、JSON和Raw。
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-26 14:04:21