While working with WCF, we often need transfer some kind of data in message headers. For example, in order to authenticate the caller of WCF service, we need pass username and password into the message header, then in the service side retrieve them and validate them. But how can we inject a custom message on WCF client side and retrieve it on WCF service side? The key is System.ServiceModel.Dispatcher.IClientMessageInspector and System.ServiceModel.Dispatcher.IDispatchMessageInspector. To learn more about the two interfaces please refer to the following link on MSDN.
http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.iclientmessageinspector.aspx
Now, let's take a look at the definitions of interface IClientMessageInspector and IDispatchMessageInspector.
public interface IClientMessageInspector { void AfterReceiveReply(ref Message reply, object correlationState);
object BeforeSendRequest(ref Message request, IClientChannel channel); }
public interface IDispatchMessageInspector { object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext);
void BeforeSendReply(ref Message reply, object correlationState); }
On the client side, we need to implement a MessageInspector derived from IClientMessageInspector, while on the service side implement a MessageInspector derived from IDispatchMessageInspector.
As you can see both these interfaces define a couple of methods that allow to access the Message (System.ServiceModel.Channels.Message) just before sending it, regardless it is a Request (IClientMessageInspector) or a Response (IDispatchMessageInspector), and just after receiveing it, again regardless its direction.
Next, I will add MessageInspectors into my sample project that I hvae been using in previous blogs.
1. Define IClientMessageInspector Interface
In VS2008 Solution Explorer, right-click "ServiceHost" solution, Add New Project, select "Silverlight" as project type, and "Silverlight Class Library" as template, then input "SilverlightMessageInspector" as project name, click "OK" button.
Add reference to "System.ServiceModel.dll".
Right-click "SilverlightMessageInspector" project, Add New Item, select "Class" as template, Input "IClientMessageInspector.cs" as name,click "OK" button. The IClientMessageInspector.cs looks like below.
using System.ServiceModel; using System.ServiceModel.Channels;
namespace SilverlightMessageInspector { public interface IClientMessageInspector { object BeforeSendRequest(ref Message request, IClientChannel channel);
void AfterReceiveReply(ref Message reply, object correlationState); } }
2. Implement IClientMessageInspector Interface
Add a class "SecurityTokenMessageInspector" to implment IClientMessageInspector interface. Here is the sample code.
using System.ServiceModel; using System.ServiceModel.Channels;
namespace SilverlightMessageInspector
{ public class SecurityTokenMessageInspector : IClientMessageInspector { private string userToken;
public SecurityTokenMessageInspector(string userToken) { this.userToken = userToken; }
public object BeforeSendRequest(ref Message request, IClientChannel channel) { request.Headers.Add(MessageHeader.CreateHeader("SecurityToken", "", userToken)); return null; }
public void AfterReceiveReply(ref Message reply, object correlationState){ } } }
3. Extend BasicHttpBinding for Silverlight
In order to make the SecurityTokenMessageInspector work, we need to define a "MessageInspectorBasicHttpBinding" calss which extends the "BasicHttpBinding" class. We inject the IClientMessageInspector type into MessageInspectorBasicHttpBinding's constructor. Let's look at the code.
using System.ServiceModel; using System.ServiceModel.Channels;
namespace SilverlightMessageInspector { public class MessageInspectorBasicHttpBinding : BasicHttpBinding
{
private MessageInspectorBindingElement channelBindingElement;
public MessageInspectorBasicHttpBinding(IClientMessageInspector messageInspector)
{
channelBindingElement = new MessageInspectorBindingElement();
channelBindingElement.MessageInspector = messageInspector;
}
public override BindingElementCollection CreateBindingElements()
{
BindingElementCollection bindingElements = base.CreateBindingElements();
bindingElements.Insert(bindingElements.Count - 1, channelBindingElement);
return bindingElements;
}
}
}
4. Define a BindingElement for MessageInspectorBasicHttpBinding
public class MessageInspectorBindingElement : BindingElement
{
public IClientMessageInspector MessageInspector { get; set; }
public MessageInspectorBindingElement(){ }
......
}
5. Define MessageInspectorChannel and MessageInspectorChannelFactory
public class MessageInspectorChannel : ChannelBase, IRequestChannel
{
private IRequestChannel innerChannel;
private IClientMessageInspector messageInspector;
......
}
public class MessageInspectorChannelFactory : ChannelFactoryBase
{
private IChannelFactory innerChannelFactory;
private IClientMessageInspector messageInspector;
public MessageInspectorChannelFactory(IChannelFactory innerChannelFactory, IClientMessageInspector messageInspector)
{
this.messageInspector = messageInspector;
this.innerChannelFactory = innerChannelFactory;
}
......
}
The Silverlight side work is ok, now let's turn to the service side.
6. Implement IDispatchMessageInspector Interface
Add a class "WcfSecurityTokenMessageInspector" under the project "WcfServiceAuthentication", which extends the IDispatchMessageInspector interface. Look at the class code.
using System.ServiceModel.Dispatcher;
namespace WcfServiceAuthentication
{
class WcfSecurityTokenMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
int index = request.Headers.FindHeader("SecurityToken", "");
if (index >= 0)
{
string token = request.Headers.GetHeader(index);
// validate the security token here....
}
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
}
}
In order to configure these message inspectors we can use a custom behavior. Behaviros are classes that extend the service model defining custom extensions for: contracts, endpoints, services, operations. Here I use the endpoint bahavior.
7. Implement IEndpointBehavior Interface
using System.ServiceModel.Description;
namespace WcfServiceAuthentication
{
class WcfSecurityTokenHeaderBehavior : IEndpointBehavior
{
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
WcfSecurityTokenMessageInspector inspector = new WcfSecurityTokenMessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public void Validate(ServiceEndpoint endpoint)
{
}
#endregion
}
}
As you can see I implement the IEndpointBehavior interface, which defines three methods (AddBindingParameter, ApplyClientBehavior, ApplyDispatchBehavior). The one I'm interested on is the ApplyDispatchBehavior that relates to the service-side. This method receives a parameter of type EndpointDispatcher that allows to add custom Message Inspectors instance to the service dispatching environment. Because we're defining an Endpoint Behavior, this behavior affects a single endpoint of a service. To map the behavior to the service endpoint we can use a custom configuration element in the configuration file of the service host. In this sample I used a custom configuration element. To do that we need a custom type describing the configuration element. It is a type inherited from BehaviorExtensionElement.
8. Extends BehaviorExtensionElement Class
Add reference to "System.Configuration.dll" under project "WcfServiceAuthentication".
Add a class "WcfSecurityTokenHeaderBehaviorExtensionElement" which extends System.ServiceModel.Configuration.BehaviorExtensionElement class. It look like below.
using System;
using System.ServiceModel.Configuration;
namespace WcfServiceAuthentication
{
class WcfSecurityTokenHeaderBehaviorExtensionElement : BehaviorExtensionElement
{
public WcfSecurityTokenHeaderBehaviorExtensionElement()
{
}
public override Type BehaviorType
{
get
{
return typeof(WcfSecurityTokenHeaderBehavior);
}
}
protected override object CreateBehavior()
{
return new WcfSecurityTokenHeaderBehavior();
}
}
}
9. Config the MessageInspector and EndpointBehavior
The Web.config changed as below.
......
<!-- Service Endpoints -->
behaviorConfiguration="MessageInspectorBehavior" />
behaviorConfiguration="MessageInspectorBehavior" />
So far we finished the whole prcess: implement MessageInspector, extend EndpointBehavior and make it configurable. Next I will use this technology to implement a WCF authentication by injecting a custom security token into message header.
[@more@]来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/13651903/viewspace-1034100/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/13651903/viewspace-1034100/