Whether the extension will work autonomously for every request to a Web service or will work only against certain methods
Whether the extension will alter or add to the contents of the message it intercepts
Next we’ll look at extensions for three scenarios
An Autonomous SOAP Extension
our first example is the simplest scenario for a SOAP extension—one that works autonomously and does not alter the contents of the request or response. We’ll build it, deploy it, and then go back over it to fill in the gaps we left along the way.
public class SimpleExtension : SoapExtension
{
public override object GetInitializer(Type serviceType)
{
return null;
}
public override object GetInitializer(LogicalMethodInfo methodInfo,
SoapExtensionAttribute attribute)
{
return null;
}
public override void Initialize(object initializer)
{
return;
}
public override void ProcessMessage(SoapMessage message)
{
switch(message.Stage)
{
case SoapMessageStage.BeforeDeserialize:
throw new SoapException("This service is not available",
SoapException.ClientFaultCode);
break;
case SoapMessageStage.AfterDeserialize:
break;
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
break;
default:
break;
}
}
}
⋮
Here are the four stages represented by the SoapMessage.Stage property:
BeforeDeserialize Indicates that the SOAP request message is about to be deserialized into a SoapMessage object. You can still access it as XML here.
AfterDeserialize Indicates that the request has been deserialized into objects. Information contained in the XML message can now be accessed through the SoapMessage object that was passed to ProcessMessage as a parameter.
BeforeSerialize Indicates that the SoapMessage object is about to be serialized into a SOAP response message.
AfterSerialize Indicates that the SOAP response message has been created and is ready to be sent back through the pipeline and out to the client.
Creating a SOAP Extension Attribute
[AttributeUsage(AttributeTargets.Method)]
public class LessThanAttribute : SoapExtensionAttribute
{
private int priority = 9;
public override Type ExtensionType
{
get { return typeof(LessThanExtension); }
}
public override int Priority
{
get { return priority; }
set { priority = value; }
}
Changing the SOAP Message Using Your Extension
for example, if you want to compress and decompress or encrypt and decrypt the SOAP message for transmission across the wire
The key to using an extension in this way is working with the stream containing the raw requests and responses. When we’re working in ProcessMessage, we can access the stream in the SoapMessage.Stream property. The only problem is that the stream is read-only, so if we want to alter its contents, we have to pull them out and copy them into another stream. This presents another problem. How do we tell the handler to use the stream with the new contents for any future processing? The answer is simple: we use the ChainStream method.
Implementing ChainStream
The method signature for ChainStream looks like this:
public override Stream ChainStream(Stream stream)
{
}
The read-only stream it presents as the parameter contains the content you want to alter. The stream it expects as a return value is a new read-write stream that it will hook up to the message’s destination in place of the old one. Thus a simple implementation of ChainStream in an extension class looks like this:
public class AnExtension : SoapExtension
{
public Stream currentStream;
public Stream newStream;
public override Stream ChainStream(Stream stream)
{
currentStream = stream;
newStream = new MemoryStream();
return newStream;
}
⋮
}
Remember that all ChainStream does is present the SOAP extension with a new stream that points at the message’s recipient. The actual processing of the current stream’s contents and the subsequent writing to the new stream must be done during one of the four calls to ProcessMessage. For this task, ProcessMessage needs access to both streams, and the simplest way to give it that access is to store the streams in member variables, as we did earlier.
With ChainStream set up, all that’s left to do is to implement ProcessMessage itself. If you’re developing an extension for encrypting or compressing a SOAP message, you should access currentStream, process it, and copy the results into newStream at the BeforeDeserialize and AfterSerialize stages. In outline form, this looks something like the following:
public override void ProcessMessage(SoapMessage message)
{
switch(message.Stage)
{
case SoapMessageStage.BeforeDeserialize:
DecodeAndCopyRequestStream(currentStream, newStream);
break;
case SoapMessageStage.AfterDeserialize:
break;
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
EncodeAndCopyResponseStream(currentStream, newStream);
break;
default:
throw new Exception("Invalid stage");
}
}
Extensions that alter the content of the request or its response can be deployed as either autonomous or targetable SOAP extensions.
SOAP Extensions on the Client
A dog is not just for Christmas, and a SOAP extension isn’t just for the server. If you consider an extension that decrypts a request and encrypts a response, you’ll realize that there’s not much point in it existing on the server if it doesn’t also exist on the client where it will encrypt requests and decrypt responses. Fortunately, you can incorporate a targetable SOAP extension into your client by using Visual Studio .NET, in just five easy steps:
Generate a proxy class (Web reference) to the Web service for your client project.
Add the class files for your extension and its attribute to your client project.
Open the class file for your proxy class—usually called reference.cs—and tag the methods that need processing by your extension with its attribute.
Save reference.cs and build your client as you normally would.
Compile and run the extension.
Important If you regenerate the proxy class for a Web service, you lose any changes made to incorporate the extension into the client and you’ll have to add the attributes again.
By and large, an extension written for the server will work as expected on the client without any code modifications. This is because the four message stages in ProcessMessage are in a different order on the client. ProcessMessage generates a request and receives a response in this order: BeforeSerialize, AfterSerialize, BeforeDeserialize, AfterDeserialize. However, if the extension really needs to know whether it’s working on the client or the server, you can find out in ProcessMessage by calling typeof on its SoapMessage parameter. The typeof operator will return SoapServerMessage if the extension is on the server, and it will return SoapClientMessage if the extension is on the client. Both these classes inherit from SoapMessage.

No comments:
Post a Comment