Captura de solicitudes SOAP en un servicio web ASP.NET ASMX

Considere el requisito de registrar las solicitudes SOAP entrantes en un servicio web ASP.NET ASMX. La tarea es capturar el XML sin formato que se envía al servicio web.

El mensaje entrante debe registrarse para la inspección de depuración. La aplicación ya tiene su propia biblioteca de registro en uso, por lo que el uso ideal sería algo como esto:

//string or XML, it doesn't matter. string incomingSoapRequest = GetSoapRequest(); Logger.LogMessage(incomingSoapRequest); 
  • ¿Hay alguna solución fácil para capturar el XML sin procesar de las solicitudes SOAP entrantes?
  • ¿Qué eventos manejarías para acceder a este objeto y a las propiedades relevantes?
  • ¿Hay alguna forma en que IIS pueda capturar la solicitud entrante y presionar en un registro?

Una forma de capturar el mensaje en bruto es usar SoapExtensions .

Una alternativa a SoapExtensions es implementar IHttpModule y capturar el flujo de entrada a medida que entra.

 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(); } } 

También puede implementar colocando el código en 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); } 

Tengo un método de utilidad en mi servicio web que uso para capturar la solicitud cuando sucede algo que no espero como una excepción no controlada.

  ///  /// Captures raw XML request and writes to FailedSubmission folder. ///  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 { } } 

Luego en web.config almaceno varios valores de AppSetting que definen qué nivel quiero usar para capturar la solicitud.

        

Luego en mi Application_BeginRequest lo he modificado así. Tenga en cuenta que Configuración es una clase estática que creo para leer las propiedades de web.config y otras áreas.

  protected void Application_BeginRequest(object sender, EventArgs e) { if(Configuration.CaptureAllRequests) { Utility.CaptureRequest(); } } 

¿Sabes que en realidad no necesitas crear un HttpModule verdad?

También puede leer los contenidos de Request.InputStream desde su método WebMethod .

Aquí hay un artículo que escribí sobre este enfoque .

El código es el siguiente:

 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; } } } 

No hay formas fáciles de hacer esto. Tendrás que implementar una SoapExtension . El ejemplo en el enlace anterior muestra una extensión que se puede usar para registrar los datos.

Si hubiera estado usando WCF, simplemente podría configurar la configuración para generar registros de mensajes.


De acuerdo con Steven de Salas , puede usar la propiedad Request.InputStream dentro del método web. No lo he intentado, pero él dice que funciona.

Me gustaría probar esto con http y https, y con y sin otras SoapExtensions ejecutándose al mismo tiempo. Estas son las cosas que pueden afectar el tipo de flujo en el que se establece InputStream . Algunas secuencias no pueden buscar, por ejemplo, lo que podría dejarle con una secuencia posicionada después del final de los datos, y que no puede mover al principio.