概述
在WCF專題系列(3):深入WCF尋址Part 3—消息過濾引擎一文 中,詳細介紹了WCF中的消息篩選引擎,包括消息篩選器和篩選器表,每個 EndpointDispatcher都包含了兩個消息篩選器,默認的地址過濾器是 EndpointAddressMessageFilter,默認的契約過濾器是ActionMessageFilter, 這些是可以通過Behavior來改變的。本文我們將學習如何創建一個自定義的消息 過濾器,並通過自定義Behavior來改變EndpointDispatcher的默認過濾器。
自定義過濾器
在默認情況下,默認情況下,僅當消息的 “To”標頭為終結點的 EndpointAddress 並且消息的動作與終結點 操作的動作之一匹配時,終結點的消息篩選器才與此消息匹配。在本文中,我們 將自定義一個消息過濾器,它不要求消息的“To”標頭完全與 EndpointAddress完全匹配,而只是檢測SOAP消息中的“To”標頭中 是否包含某些特定的字符。所有的消息過濾器都從MessageFilter基類繼承,如 下代碼所示:
/// <summary>
/// Author: TerryLee
/// Url: http://www.cnblogs.com/terrylee
/// </summary>
public class SpecialCharactersMessageFilter : MessageFilter
{
private String _characters = String.Empty;
public SpecialCharactersMessageFilter(string characters)
{
this._characters = characters;
}
public override bool Match(Message message)
{
Uri to = message.Headers.To;
if (to == null)
return false;
return to.AbsoluteUri.Contains(_characters);
}
public override bool Match(MessageBuffer buffer)
{
return Match(buffer.CreateMessage());
}
}
SpecialCharactersMessageFilter的實現非常簡單,僅僅是查找 “To”標頭是否包含某些特定字符,這些字符我們會在配置文件中進 行配置。
定義EndpointBehavior
現在我們自定義一個 EndpointBehavior,使用它來替換EndpointDispatcher上的地址過濾器和契約過 濾器,它實現自IendpointBehavior接口,如下代碼所示:
/// <summary>
/// Author: TerryLee
/// Url: http://www.cnblogs.com/terrylee
/// </summary>
public class FilteringEndpointBehavior : IEndpointBehavior
{
MessageFilter addressFilter;
MessageFilter contractFilter;
public FilteringEndpointBehavior(MessageFilter addressFilter,
MessageFilter contractFilter)
{
this.addressFilter = addressFilter;
this.contractFilter = contractFilter;
}
public void AddBindingParameters (ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{
throw new InvalidOperationException(
"This behavior should only be used on the server.");
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.AddressFilter = this.addressFilter;
endpointDispatcher.ContractFilter = this.contractFilter;
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
這裡只是實現了ApplyDispatchBehavior方法 ,其它方法暫時先不用考慮,另外,由於該Behavior只是用在服務端,所以在 ApplyClientBehavior方法中拋出一個異常。
定義行為擴展
接下來定 義一個行為擴展元素,這裡定義了一個Characters的屬性,用來在配置文件中指 定消息過濾器中用到的特殊字符,如下代碼所示:
/// <summary>
/// Author: TerryLee
/// Url: http://www.cnblogs.com/terrylee
/// </summary>
public class FilteringEndpointBehaviorExtension
: BehaviorExtensionElement
{
protected override object CreateBehavior()
{
MessageFilter addressFilter =
new SpecialCharactersMessageFilter(Characters);
MessageFilter contractFilter =
new MatchAllMessageFilter();
return new FilteringEndpointBehavior(addressFilter, contractFilter);
}
public override Type BehaviorType
{
get
{
return typeof (FilteringEndpointBehavior);
}
}
[ConfigurationProperty("characters",
DefaultValue = "terrylee", IsRequired = true)]
public String Characters
{
get {
return base["characters"].ToString();
}
set {
base["characters"] = value;
}
}
}
配置服務
定義一個簡單 的服務契約以及服務的實現,代碼如下,不再多說:
/// <summary>
/// Author: TerryLee
/// Url: http://www.cnblogs.com/terrylee
/// </summary>
[ServiceContract(Namespace = "http://www.cnblogs.com/terrylee/")]
public interface IEchoService
{
[OperationContract]
string Echo (string msg);
}
public class EchoService : IEchoService
{
public string Echo(string msg)
{
return "Hello:" + msg;
}
}
現在來看一 下服務端的配置,除了必須的終結點配置之外,為服務注冊一個新的Behavior, 代碼如下所示:
<extensions>
<behaviorExtensions>
<add name="filteringEndpointBehavior"
type="TerryLee.CustomizeMessageFilter.Service.
FilteringEndpointBehaviorExtension, Service,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
創建 EndpointBehavior配置:
<endpointBehaviors>
<behavior name="filterBehavior">
<filteringEndpointBehavior characters="terrylee" />
</behavior>
</endpointBehaviors>
服務終 結點使用behaviorConfiguration:
<endpoint address=""
binding ="wsHttpBinding"
contract="TerryLee.CustomizeMessageFilter.Service.IEchoService&qu ot;
behaviorConfiguration="filterBehavior">
</endpoint>
最終完整的配置文件:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="TerryLee.CustomizeMessageFilter.Service.EchoService" behaviorConfiguration="echoBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8887/EchoService"/>
</baseAddresses>
</host>
<endpoint address=""
binding ="wsHttpBinding"
contract="TerryLee.CustomizeMessageFilter.Service.IEchoService&qu ot;
behaviorConfiguration="filterBehavior">
</endpoint>
</service>
</services>
<extensions>
<behaviorExtensions>
<add name="filteringEndpointBehavior"
type="TerryLee.CustomizeMessageFilter.Service.
FilteringEndpointBehaviorExtension, Service,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior name="echoBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="filterBehavior">
<filteringEndpointBehavior characters="terrylee" />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
至此服務端配置完成,根據配置,只有 SOAP消息“To”標頭中包含有“terrylee”的字符時,此 時終結點的消息過濾器才與此消息匹配。現在在控制台中輸出一下,看看針對配 置的終結點它所使用的地址過濾器和契約過濾器分別是什麼,如圖1所示:
圖1
可以看到,終結點所用的地址過濾器不再是默認的 EndpointAddressMessageFilter,而是我們自定義的 SpecialCharactersMessageFilter。
客戶端測試
通過上面的配置,我 們知道,只有SOAP消息的“To”標頭中帶有“terrylee” 字符,消息才會被分發到相應的終結點上。現在編寫一個簡單的測試客戶端程序 ,如下代碼所示:
/// <summary>
/// Author: TerryLee
/// Url: http://www.cnblogs.com/terrylee
/// </summary>
static void Main()
{
Uri serviceVia = new Uri ("http://localhost:8887/EchoService");
WSHttpBinding binding = new WSHttpBinding();
ChannelFactory<IEchoService> factory = new ChannelFactory<IEchoService>
(binding, new EndpointAddress(serviceVia));
String address = "http://localhost/terrylee";
Console.WriteLine (String.Format("Sending message to {0}...", address));
IEchoService channel = factory.CreateChannel(
new EndpointAddress(address), serviceVia);
try
{
String reply = channel.Echo("cnblogs");
Console.WriteLine(reply.ToString());
((IClientChannel) channel).Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("Exception: {0} ", ce.Message);
((IClientChannel)channel).Abort ();
}
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();
}
由於定義了終結點的邏輯地址為 “http://localhost/terrylee”,符合我們上面所講的條件,可以 看到輸出結果如圖2所示:
圖2
可以看到,服務正確的返回我們想要的消息,但是如果修改 一下address為“http://localhost/anytao”,由於沒有包含特定的 字符“terrylee”,服務端將返回錯誤的信息:
String address = "http://localhost/anytao";
輸出結果如圖3所示:
圖3
結束語本文我介紹了如何自定義消息篩選器,以及如何通 過Behavior來修改EndpointDispatcher的默認過濾器,消息過濾是消息分發過程 中非常重要的一個環節,也是WCF尋址中一個重要的部分,希望對於大家有所幫 助。