2010-04-12 16 views
27

Prendere in considerazione il requisito per registrare le richieste SOAP in arrivo su un servizio Web ASMX ASP.NET. L'attività consiste nel catturare l'XML non elaborato inviato al servizio web.Acquisizione di richieste SOAP in un servizio Web ASP.NET ASMX

Il messaggio in arrivo deve essere registrato per l'ispezione di debug. L'applicazione ha già una propria libreria di registrazione in uso, quindi l'utilizzo ideale sarebbe qualcosa di simile:

//string or XML, it doesn't matter. 
string incomingSoapRequest = GetSoapRequest(); 

Logger.LogMessage(incomingSoapRequest); 
  • Esistono soluzioni semplici per catturare l'XML grezzo delle richieste SOAP in arrivo?
  • Quali eventi gestirai per accedere a questo oggetto e alle proprietà pertinenti?
  • C'è comunque IIS in grado di acquisire la richiesta in arrivo e passare a un log?

risposta

19

Un modo per acquisire il messaggio non elaborato è utilizzare SoapExtensions.

Un'alternativa a SoapExtensions è quello di implementare IHttpModule e afferrare il flusso di input come è in arrivo.

public class LogModule : IHttpModule 
{ 
    public void Init(HttpApplication context) 
    { 
     context.BeginRequest += this.OnBegin; 
    } 

    private void OnBegin(object sender, EventArgs e) 
    { 
     HttpApplication app = (HttpApplication)sender; 
     HttpContext context = app.Context; 

     byte[] buffer = new byte[context.Request.InputStream.Length]; 
     context.Request.InputStream.Read(buffer, 0, buffer.Length); 
     context.Request.InputStream.Position = 0; 

     string soapMessage = Encoding.ASCII.GetString(buffer); 

     // Do something with soapMessage 
    } 

    public void Dispose() 
    { 
     throw new NotImplementedException(); 
    } 
} 
+0

Questo non funzionerà molto bene con la codifica UTF-8, non credo. Inoltre, sei sicuro che il flusso sia ricercabile? Sempre? –

+0

Per il flusso di input, sì. Questo è quello che sto attualmente usando per eseguire il debug delle richieste in arrivo e non ho ancora riscontrato alcun problema. Il flusso di risposta tuttavia non era ricercabile. Ho dovuto avvolgere il flusso di risposta (originale/copia) prima che potessi registrarlo. – nivlam

+1

E il flusso di uscita ?? come posso catturarlo utilizzando un'implementazione IHttpModule? – gillyb

-2

Non ci sono modi semplici per fare questo. Dovrai implementare un SoapExtension. L'esempio al collegamento precedente mostra un'estensione che può essere utilizzata per registrare i dati.

Se si stesse utilizzando WCF, è possibile semplicemente impostare la configurazione per produrre registri dei messaggi.


Secondo Steven de Salas, è possibile utilizzare la proprietà Request.InputStream all'interno del WebMethod. Non ho provato questo, ma dice che funziona.

Vorrei testarlo sia con http e https, sia con e senza altre SoapExtensions in esecuzione allo stesso tempo. Queste sono cose che potrebbero influenzare il tipo di stream su cui è impostato lo InputStream. Alcuni stream non possono cercare, ad esempio, che potrebbero lasciarti con un flusso posizionato dopo la fine dei dati e che non puoi spostare all'inizio.

+2

- 1 C'è un modo semplice per farlo. Basta usare Request.InputStream all'interno del tuo WebMethod. –

+0

Hai provato questo? È un flusso cercabile? Altrimenti, lo stream sarà già stato letto fino alla fine del webmethod. –

+1

Sì, funziona, è possibile applicare Stream.Position = 0 ma in realtà non è necessario durante la richiesta SOAP. –

16

È anche possibile implementare inserendo il codice Global.asax.cs

protected void Application_BeginRequest(object sender, EventArgs e) 
{ 
    // Create byte array to hold request bytes 
    byte[] inputStream = new byte[HttpContext.Current.Request.ContentLength]; 

    // Read entire request inputstream 
    HttpContext.Current.Request.InputStream.Read(inputStream, 0, inputStream.Length); 

    //Set stream back to beginning 
    HttpContext.Current.Request.InputStream.Position = 0; 

    //Get XML request 
    string requestString = ASCIIEncoding.ASCII.GetString(inputStream); 

} 

Ho un metodo di utilità nel mio servizio web che uso per catturare la richiesta quando succede qualcosa che non mi aspettavo come un'eccezione non gestita.

/// <summary> 
    /// Captures raw XML request and writes to FailedSubmission folder. 
    /// </summary> 
    internal static void CaptureRequest() 
    { 
     const string procName = "CaptureRequest"; 

     try 
     { 
      log.WarnFormat("{0} - Writing XML request to FailedSubmission folder", procName); 

      byte[] inputStream = new byte[HttpContext.Current.Request.ContentLength]; 

      //Get current stream position so we can set it back to that after logging 
      Int64 currentStreamPosition = HttpContext.Current.Request.InputStream.Position; 

      HttpContext.Current.Request.InputStream.Position = 0; 

      HttpContext.Current.Request.InputStream.Read(inputStream, 0, HttpContext.Current.Request.ContentLength); 

      //Set back stream position to original position 
      HttpContext.Current.Request.InputStream.Position = currentStreamPosition; 

      string xml = ASCIIEncoding.ASCII.GetString(inputStream); 

      string fileName = Guid.NewGuid().ToString() + ".xml"; 

      log.WarnFormat("{0} - Request being written to filename: {1}", procName, fileName); 

      File.WriteAllText(Configuration.FailedSubmissionsFolder + fileName, xml); 
     } 
     catch 
     { 
     } 

    } 

Poi nel web.config ho memorizzare diversi valori appSetting che definiscono il livello che voglio usare per catturare la richiesta.

<!-- true/false - If true will write to an XML file the raw request when any Unhandled exception occurrs --> 
    <add key="CaptureRequestOnUnhandledException" value="true"/> 

    <!-- true/false - If true will write to an XML file the raw request when any type of error is returned to the client--> 
    <add key="CaptureRequestOnAllFailures" value="false"/> 

    <!-- true/false - If true will write to an XML file the raw request for every request to the web service --> 
    <add key="CaptureAllRequests" value="false"/> 

Quindi nel mio Application_BeginRequest lo ho modificato in questo modo. Nota che Configuration è una classe statica che creo per leggere le proprietà da web.config e altre aree.

protected void Application_BeginRequest(object sender, EventArgs e) 
    { 

     if(Configuration.CaptureAllRequests) 
     { 
      Utility.CaptureRequest(); 
     } 
    } 
+0

Bello. Siamo andati con questo Grazie. – David

11

Sai che tu non realtà necessità di creare un HttpModule giusto?

È inoltre possibile leggere il contenuto di Request.InputStream dall'interno del proprio asmx WebMethod.

Qui è an article I wrote on this approach.

codice è il seguente:

using System; 
using System.Collections.Generic; 
using System.Web; 
using System.Xml; 
using System.IO; 
using System.Text; 
using System.Web.Services; 
using System.Web.Services.Protocols; 

namespace SoapRequestEcho 
{ 
    [WebService(
    Namespace = "http://soap.request.echo.com/", 
    Name = "SoapRequestEcho")] 
    public class EchoWebService : WebService 
    { 

    [WebMethod(Description = "Echo Soap Request")] 
    public XmlDocument EchoSoapRequest(int input) 
    { 
     // Initialize soap request XML 
     XmlDocument xmlSoapRequest = new XmlDocument(); 

     // Get raw request body 
     Stream receiveStream = HttpContext.Current.Request.InputStream; 

     // Move to beginning of input stream and read 
     receiveStream.Position = 0; 
     using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8)) 
     { 
     // Load into XML document 
     xmlSoapRequest.Load(readStream); 
     } 

     // Return 
     return xmlSoapRequest; 
    } 
    } 
} 
+0

Ho provato Steven de Salas esempio di cui sopra e la mia HttpContext.Current è sempre nullo. C'è qualcosa che sto facendo male qui? Grazie –

+0

Hai chiamato la punto di ingresso SOAP con una richiesta SOAP valida? –

+1

Questa soluzione funziona bene. Bel lavoro Steven. – atconway

-2

so di quattro anni in ritardo ... ma ora dobbiamo Fiddler a fare lo stesso.

+1

Tranne .. 1) Non è lo stesso, o anche la domanda; e 2) "Fiddler", Wireshark, ecc. non sono nuovi. – user2864740

Problemi correlati