Memorizzare Richieste e Risposte SOAP con le SoapExtension
Posted by Ricibald on 15th January 2008
In .NET le richieste e le risposte vengono sottoposte a wrapping e serializzate nella nostra comoda struttura ad oggetti, come visto in questo precedente post. Ma a volte è necessario proprio conoscere la richiesta SOAP “nuda e cruda”. Anche se ciò che stiamo richiedendo non è niente di apparentemente complicato, la cosa si rileva però molto più complicata del previsto.
Il ciclo di vita di un web service è mostrato nella figura seguente, preso dall’articolo di Microsoft:
.gif)
Le varie fasi possono essere intercettate utilizzando le Soap Extension. Normalmente vengono utilizzate per decorare il messaggio SOAP con cifratura o compressione, ma nel nostro caso saranno semplicemente utilizzate per memorizzare le richieste e risposte SOAP.
Innanzitutto è necessario creare una classe che estende dalla classe SoapExtension:
public class MessagesPrinterSoapExtension : SoapExtension
{
public override object GetInitializer(Type obj)
{
return null;
}
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attr)
{
return null;
}
public override void Initialize(object initializer)
{
}
}
Bisogna poi implementare i seguenti metodi:
private Stream _workingStream = null;
private Stream _originalStream = null;
private static string _soapRequestMessage = null;
private static string _soapResponseMessage = null;
private SoapMessageStage? _previousStage = null;
/// <summary>
/// Allows a SOAP extension access to the memory buffer containing the SOAP request or response
/// </summary>
/// <param name="stream">A memory buffer containing the SOAP request or response.</param>
/// <returns>New memory buffer that this SOAP extension can modify</returns>
public override Stream ChainStream(Stream originalStream)
{
_originalStream = originalStream;
_workingStream = new MemoryStream();
bool isResponseMessage = _previousStage != null && _previousStage == SoapMessageStage.AfterSerialize;
if (isResponseMessage) // response message
{
// Get the SOAP body response as a string and copy content in a seekable stream
_soapResponseMessage = GetMessageAndCopyStream(_originalStream, _workingStream);
}
return _workingStream;
}
public override void ProcessMessage(SoapMessage message)
{
// a SOAP message has 4 stages. We're interested in AfterSerialize
if (message.Stage == SoapMessageStage.AfterSerialize)
{
// Get the SOAP body request as a string and copy content in a seekable stream
_soapRequestMessage = GetMessageAndCopyStream(_workingStream, _originalStream);
}
_previousStage = message.Stage;
}
Che utilizzano il metodo di utilità seguente:
/// <summary>
/// Extracts the content from input and copies it in the specified stream
/// </summary>
private static string GetMessageAndCopyStream(Stream inStream, Stream outStream)
{
string inStreamContent;
// If allowed, rewind
if (inStream.CanSeek)
{
inStream.Position = 0;
}
// Read content
StreamReader inReader = new StreamReader(inStream);
inStreamContent = inReader.ReadToEnd();
// Copy to output
StreamWriter writer = new StreamWriter(outStream);
writer.Write(inStreamContent);
writer.Flush();
// If allowed, rewind
if (outStream.CanSeek)
{
outStream.Position = 0;
}
return inStreamContent;
}
Ma ora viene naturale chiedersi: come fa il mio proxy verso il web service a sapere che esisto? Semplice: basta dichiarare la classe appena creata nel file di configurazione:
<configuration>
<system.web>
<webServices>
<soapExtensionTypes>
<add type="MyNamespace.MessagesPrinterSoapExtension,MyDll" priority="1" group="Low" />
</soapExtensionTypes>
</webServices>
</system.web>
</configuration>
E’ tutto! Consigli, critiche?…
Posted in .net, web service | 4 Comments »