SOAP Message Modification Using SOAP Extensions
This topic is specific to a legacy technology. XML Web services and XML Web service clients should now be created using Windows Communication Foundation.
SOAP extensions allow developers to augment the functionality of a Web service by altering the SOAP messages sent to and from a Web service or a Web service client. For instance, you can implement an encryption or compression algorithm to run with an existing Web service.
To understand how a SOAP extension works, it is useful to first understand the lifetime of a Web service. For more information, see Anatomy of an XML Web Service Lifetime.
The following illustration outlines the major phases of a call from a client to a Web service.
As you can see, the .NET Framework serializes and deserializes XML during phases on both the Web service computer and the Web service client computer. A SOAP extension can be injected into the infrastructure to inspect or modify the SOAP messages before and after each of these serialize and deserialize phases. For instance, an encryption SOAP extension might encrypt the XML portion of the SOAP message after the .NET Framework serializes the client's arguments, and then decrypt the SOAP message on the Web server before the .NET Framework deserializes the SOAP message. These phases, where a SOAP extension might inspect or modify the SOAP message, are defined in the SoapMessageStage enumeration. In this case, the SOAP extension is encrypting in the AfterSerialize stage and decrypting in the BeforeDeserialize stage.
Typically, when a SOAP extension modifies the contents of a SOAP message, the modifications must be done on both the client and the server. That is, if a SOAP extension were to run on the client and encrypt the SOAP message, a corresponding SOAP extension must decrypt the SOAP message on the server. If the SOAP message is not decrypted, then the ASP.NET infrastructure cannot deserialize the SOAP message into an object.
Of course, a SOAP extension that does not modify the SOAP message, such as a SOAP extension that simply logs the SOAP messages, can run on only the client or server. In this case, the recipient receives the same SOAP message it would have if a SOAP extension were not running and the ASP.NET infrastructure can deserialize the SOAP message. Also, if the SOAP extension does not modify the SOAP in a way that makes deserialization impossible, the SOAP extension does not need to run on both the client and server.
Extending the SOAPExtension Class
To implement a SOAP extension, derive a class from the SoapExtension class. There are three methods of the SOAPExtension class that should, or must, be implemented:
ChainStream, a virtual method
GetInitializer, an abstract method with two signatures
Initialize, an abstract method
ProcessMessage, an abstract method
How to implement these methods is explained in the step-by-step topic Walkthrough: Altering the SOAP Message Using SOAP Extensions.
The ChainStream method is passed a Stream object and returns a Stream object. As the SOAP extension executes during each SoapMessageStage and modifies the SOAP message, a SOAP extension should read from the Stream passed into ChainStream and write to the Stream returned from ChainStream. Therefore, it is important within the ChainStream method to assign both Stream references to member variables.
The class deriving from SoapExtension uses the GetInitializer and Initialize methods to initialize internal data, based on the Web service or Web service method it is applied to. For instance, a SOAP extension that logs the SOAP message sent to and from a Web service method might initialize the name of a file to save the logging information (based on the name of the Web service or the Web service method the SOAP extension is running with).
At what point the Web services infrastructure calls the GetInitializer method and what parameters are passed to the method depend on how the SOAP extension is configured, as follows:
If the SOAP extension is configured using an attribute, GetInitializer is called by the Web services infrastructure the first time a Web service method is accessed.
If the SOAP extension is configured in a configuration file, GetInitializer is called by the Web services infrastructure only the first time the entire Web service is accessed.
The Web services infrastructure caches the object that the GetInitializer method returns. Then each time the SOAP extension runs with that Web service or Web service method, the infrastructure passes the initializer object to the Initialize method.
Actual extended processing beyond the standard SOAP processing is performed by the ProcessMessage method. Each time the Web services infrastructure calls ProcessMessage, it passes (as an argument) an instance of a class derived from SoapMessage that contains information about the SOAP message at that particular stage. If the SOAP extension is running with a Web service, then a SoapServerMessage object is passed in. If the SOAP extension is running with a Web service client, then a SoapClientMessage object is passed in.
Soap Extensions and Exceptions
Soap extensions must never throw exceptions themselves. They can, however, add exception information to the Exception property on the SoapMessage object passed in the ProcessMessage method.
They can also serve as application-wide exception handler using the same facility to catch all exceptions in the application for which the soap extension is installed and perform some behavior including modifying the returned SOAP fault.
Order in Which SOAP Extension Methods are Invoked
Now that you have looked at the methods a SOAP extension overrides, look at when the Web services infrastructure invokes SOAP extension methods throughout the invocation of a Web service method. The following steps assume that the SOAP extension is running on both the client and server. If the SOAP extension is not running on both the client and the server, the steps associated with the SOAP extension running on each are ignored by the .NET Framework.
Client Side Prepares a Request Message
A client invokes a method on the proxy class.
A new instance of the SOAP extension is created on the client.
If this is the first time this SOAP extension has executed with this Web service on the client, then the GetInitializer method is invoked on the SOAP extension running on the client.
The Initialize method is invoked.
The ChainStream method is invoked.
The ProcessMessage method is invoked with SoapMessageStage set to BeforeSerialize.
ASP.NET on the client computer serializes the arguments of the Web service method into XML.
The ProcessMessage method is invoked with SoapMessageStage set to AfterSerialize.
ASP.NET on the client computer sends the SOAP message over the network to the Web server hosting the Web service.
Server Side Receives a Request Message and Prepares a Response
ASP.NET on the Web server receives the SOAP message.
A new instance of the SOAP extension is created on the Web server.
On the Web server, if this is the first time this SOAP extension has executed with this Web service on the server side, the GetInitializer method is invoked on the SOAP extension running on the server.
The Initialize method is invoked.
The ChainStream method is invoked.
The ProcessMessage method is invoked with SoapMessageStage set to BeforeDeserialize.
ASP.NET deserializes the arguments within the XML.
The ProcessMessage method is invoked with SoapMessageStage set to AfterDeserialize.
ASP.NET creates a new instance of the class implementing the Web service and invokes the Web service method, passing in the deserialized arguments. This object resides on the same computer as the Web server.
The Web service method executes its code, eventually setting the return value and any out parameters.
The ProcessMessage method is invoked with SoapMessageStage set to BeforeSerialize.
ASP.NET on the Web server serializes the return value and out parameters into XML.
The ProcessMessage method is invoked with SoapMessageStage set to AfterSerialize.
ASP.NET sends the SOAP response message over the network back to the Web service client.
Client Side Receives a Response Message
ASP.NET on the client computer receives the SOAP message.
The ProcessMessage method is invoked with SoapMessageStage set to BeforeDeserialize.
ASP.NET deserializes the XML into the return value and any out parameters.
The ProcessMessage method is invoked with SoapMessageStage set to AfterDeserialize.
ASP.NET passes the return value and any out parameters to the instance of the proxy class.
The client receives the return value and any out parameters.
Implementing the SOAP Extension
There are two ways to run a SOAP extension on either a client or server application. First, you can configure the application to run the extension. To configure your SOAP extension to run for all Web methods on all Web services, especially a vroot, edit the <soapExtensionTypes> Element section within the Web.config file. The following code shows that the type attribute value must be on one line and include the fully qualified name of the extension, plus the version, culture, and public key token of the signed assembly.
<configuration>
<system.web>
<webServices>
<soapExtensionTypes>
<add type="Contoso.MySoapExtension, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
priority="1" group="0"/>
</soapExtensionTypes>
</webServices>
</system.web>
</configuration>
Second, you can create a custom attribute that is applied to a Web service method. To create the custom attribute, create a class that derives from SoapExtensionAttribute. For details about creating a custom attribute, see How to: Implement a SOAP Extension. For more information about creating custom attributes, see Creating Custom Attributes.
Note
When implementing a SOAP extension, there is a possibility that a Denial of Service (DOS) attack could be attempted if your extension uses an XmlTextReader to read the data stream. One way of preventing such an attack is to ensure that the ProhibitDtd property is set to true.
Priorities and Priority Groups
Using either attributes or configuration, SOAP extensions can be assigned a priority that helps determine the relative order of execution when multiple SOAP extensions are configured to run with an XML Web service method. The higher the priority a SOAP extension is, the closer it executes to the SOAP message being sent or received over the network. SOAP extensions belong to any of three priority groups. Within each group, the priority property distinguishes each member. The lower the priority property is, the higher the relative priority (0 being the highest).
The three relative priority groups for SOAP extensions are: SOAP extensions configured using an attribute and SOAP extensions specified in the configuration file with a group setting of 0 or 1. Their priorities are ordered as follows:
Highest priority group: SOAP extensions configured using a configuration file with a group setting of 0.
Medium priority group: SOAP extensions configured using an attribute.
Lowest priority group: SOAP extensions configured using a configuration file with a group setting of 1.
The following code example is a configuration file that specifies that the Logger.LoggerExtension
SOAP extension runs within the relative priority group 0
and has a priority of 1
.
<configuration>
<system.web>
<webServices>
<soapExtensionTypes>
<add type="Logger.LoggerExtension,logger"
priority="1"
group="0" />
</soapExtensionTypes>
</webServices>
</system.web>
</configuration>
See Also
Tasks
How to: Implement a SOAP Extension
Reference
SoapExtension
SoapExtensionAttribute
SoapMessageStage
LogicalMethodInfo
Concepts
SOAP Message Modification Using SOAP Extensions
Anatomy of an XML Web Service Lifetime
Building XML Web Service Clients