Custom Message Interceptor
The MessageInterceptor sample demonstrates the use of the channel extensibility model. In particular, it shows how to implement a custom binding element that creates channel factories and channel listeners to intercept all incoming and outgoing messages at a particular point in the run-time stack. The sample also includes a client and server that demonstrate the use of these custom factories.
In this sample, both the client and the service are console programs (.exe). The client and service both make use of a common library (.dll) that contains the custom binding element and its associated run-time objects.
Note
The setup procedure and build instructions for this sample are located at the end of this topic.
The sample describes the recommended procedure for creating a custom layered channel in Windows Communication Foundation (WCF), by using the channel framework and following WCF best practices. The steps to create a custom layered channel are as follows:
Decide which of the channel shapes your channel factory and channel listener will support.
Create a channel factory and a channel listener that support your channel shapes.
Add a binding element that adds the custom layered channel to a channel stack.
Add a binding element extension section to expose the new binding element to the configuration system.
Channel Shapes
The first step in writing a custom layered channel is to decide which shapes are required for the channel. For our message inspector, we support any shape that the layer below us supports (for example, if the layer below us can build IOutputChannel and IDuplexSessionChannel, then we also expose IOutputChannel and IDuplexSessionChannel).
Channel Factory and Listener Factory
The next step in writing a custom layered channel is to create an implementation of IChannelFactory for client channels and of IChannelListener for service channels.
These classes take an inner factory and listener, and delegate all but the OnCreateChannel
and OnAcceptChannel
calls to the inner factory and listener.
class InterceptingChannelFactory<TChannel> : ChannelFactoryBase<TChannel>
{
//...
}
class InterceptingChannelListener<TChannel> : ListenerFactoryBase<TChannel>
{
//...
}
Adding a Binding Element
The sample defines a custom binding element: InterceptingBindingElement
. InterceptingBindingElement
takes a ChannelMessageInterceptor
as an input, and uses this ChannelMessageInterceptor
to manipulate messages that pass through it. This is the only class that must be public. The factory, listener, and channels can all be internal implementations of the public run-time interfaces.
public class InterceptingBindingElement : BindingElement
{
}
Adding Configuration Support
To integrate with binding configuration, the library defines a configuration section handler as a binding element extension section. The client and server configuration files must register the binding element extension with the configuration system. Implementers that want to expose their binding element to the configuration system can derive from this class.
public abstract class InterceptingElement : BindingElementExtensionElement
{
//...
}
Adding Policy
To integrate with our policy system, InterceptingBindingElement
implements IPolicyExportExtension to signal that we should participate in generating policy. To support importing policy on a generated client, the user can register a derived class of InterceptingBindingElementImporter
and override CreateMessageInterceptor
() to generate their policy-enabled ChannelMessageInterceptor
class.
Example: Droppable Message Inspector
Included in the sample is an example implementation of ChannelMessageInspector
which drops messages.
class DroppingServerElement : InterceptingElement
{
protected override ChannelMessageInterceptor CreateMessageInterceptor()
{
return new DroppingServerInterceptor();
}
}
You can access it from configuration as follows:
<configuration>
...
<system.serviceModel>
...
<extensions>
<bindingElementExtensions>
<add name="droppingInterceptor"
type=
"Microsoft.ServiceModel.Samples.DroppingServerElement, library"/>
</bindingElementExtensions>
</extensions>
</system.serviceModel>
</configuration>
The client and server both use this newly created configuration section to insert the custom factories into the lowest-level of their run-time channel stacks (above the transport level).
<customBinding>
<binding name="sampleBinding">
<droppingInterceptor/>
<httpTransport/>
</binding>
</customBinding>
The client uses the MessageInterceptor
library to add a custom header to even numbered messages. The service on the other hand uses MessageInterceptor
library to drop any messages that do not have this special header.
You should see the following client output after running the service and then the client.
Reporting the next 10 wind speed
100 kph
Server dropped a message.
90 kph
80 kph
Server dropped a message.
70 kph
60 kph
Server dropped a message.
50 kph
40 kph
Server dropped a message.
30 kph
20 kph
Server dropped a message.
10 kph
Press ENTER to shut down client
The client reports 10 different wind speeds to the service, but only tags half of them with the special header.
On the service, you should see the following output:
Press ENTER to exit.
Dangerous wind detected! Reported speed (90) is greater than 64 kph.
Dangerous wind detected! Reported speed (70) is greater than 64 kph.
5 wind speed reports have been received.
To set up, build, and run the sample
Install ASP.NET 4.0 using the following command.
%windir%\Microsoft.NET\Framework\v4.0.XXXXX\aspnet_regiis.exe /i /enable
Ensure that you have performed the One-Time Setup Procedure for the Windows Communication Foundation Samples.
To build the solution, follow the instructions in Building the Windows Communication Foundation Samples.
To run the sample in a single- or cross-machine configuration, follow the instructions in Running the Windows Communication Foundation Samples.
Run Service.exe first then run Client.exe and watch both console windows for output.