I'm trying to create a logging service for all SOAP method calls to my ASP.NET webservice. I've been looking at Log SOAP messages from a console application and the walkthrough for SOAP extensions at MSDN (http://msdn.microsoft.com/en-us/library/s25h0swd%28v=vs.100%29.aspx) but they don't seem to cover it completely.
I don't want to alter the SOAP message, just log it to a database table. What I'm trying to do is read the SOAP message stream, parse it as an XML, log the XML and the let the call be on its merry way. But when I read the stream it is spent/disposed. I've tried copying the stream contents to not interrupt the flow.
According to the walkthrough the ProcessMessage
method should look something like this:
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
break;
case SoapMessageStage.AfterSerialize:
// Write the SOAP message out to a file.
WriteOutput( message );
break;
case SoapMessageStage.BeforeDeserialize:
// Write the SOAP message out to a file.
WriteInput( message );
break;
case SoapMessageStage.AfterDeserialize:
break;
default:
throw new Exception("invalid stage");
}
}
I've managed to parse the stream without problems during the BeforeDeserialize
stage, but then ProcessMessage
is called again in the AfterSerialize
stage and by then the stream is used and no longer contains any data.
According to SOAP Message Modification Using SOAP Extensions (http://msdn.microsoft.com/en-us/library/esw638yk%28v=vs.100%29.aspx) the SOAP call goes through the following steps:
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.
Step 6 is performed correctly and the SOAP XML is logged. Then it shouldn't do anything more until after the server has processed the call, done what it needs (step 10), and returned a response (step 13). Instead it immediately calls ProcessMessage
again in the AfterSerialize
stage, but this time the stream is already spent and throws an exception when I try to log it.
According to the walkthrough I should use the ChainStream
method, and it should be run in step 5 above. When I make the call it is run twice, once before BeforeDeserialize
and once before AfterSerialize
.
I've tried copying the message stream to a separate stream and use that for the logging, also to set some kind of status if BeforeDeserialize
already have run, but problem still persists.
I still need the code in AfterSerialize
to handle the response that is sent back to the client. But if I try to remove my code in AfterSerialize
and only run the code in BeforeDeserialize' I get a
HTTP 400: Bad Request`.
This all happens before the actual method call, so I never even get to the code inside the method (step 10).