Захват SOAP-запросов в веб-сервис ASP.NET ASMX

Рассмотрим требование для записи входящих запросов SOAP в веб-службу ASP.NET ASMX. Задача состоит в том, чтобы перехватить необработанный XML, отправляемый веб-службе.

Входящее сообщение должно быть зарегистрировано для проверки отладки. Приложение уже имеет свою собственную библиотеку журналов, поэтому идеальное использование будет примерно таким:

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

Logger.LogMessage(incomingSoapRequest);
  • Существуют ли простые решения для захвата необработанного XML входящих запросов SOAP?
  • Какие события вы бы обработали, чтобы получить доступ к этому объекту и соответствующим свойствам?
  • Есть ли в любом случае IIS может перехватить входящий запрос и нажать на логи?

4 ответа

Решение

Одним из способов получения необработанного сообщения является использование SoapExtensions.

Альтернативой SoapExtensions является реализация IHttpModule и получение входного потока по мере его поступления.

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

Вы также можете реализовать, разместив код в 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);

}

У меня есть метод Utility в моем веб-сервисе, который я использую для захвата запроса, когда происходит что-то, чего я не ожидаю как необработанное исключение.

    /// <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
        {
        }

    }

Затем в web.config я храню несколько значений AppSetting, которые определяют, какой уровень я хочу использовать для захвата запроса.

    <!-- 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"/>

Затем в моем Application_BeginRequest я изменил его так. Обратите внимание, что Configuration - это статический класс, который я создаю для чтения свойств из web.config и других областей.

    protected void Application_BeginRequest(object sender, EventArgs e)
    {

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

Вы знаете, что вам на самом деле не нужно создавать HttpModule право?

Вы также можете прочитать содержимое Request.InputStream из вашего asmx WebMethod,

Вот статья, которую я написал об этом подходе.

Код выглядит следующим образом:

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

Я знаю, что я опоздал на 4 года... но теперь у нас есть Fiddler, чтобы сделать то же самое.

Нет простых способов сделать это. Вам нужно будет реализовать SoapExtension. В примере по предыдущей ссылке показано расширение, которое можно использовать для регистрации данных.

Если вы использовали WCF, то вы могли бы просто настроить конфигурацию для создания журналов сообщений.


По словам Steven de Salas, вы можете использовать Request.InputStream собственность в рамках веб-метода. Я не пробовал это, но он говорит, что это работает.

Я хотел бы проверить это как с http и https, так и с и без других SoapExtensions, работающих одновременно. Это вещи, которые могут повлиять на то, какой поток InputStream установлено в. Например, некоторые потоки не могут искать, что может оставить вас в положении потока после конца данных, и которое вы не можете переместить в начало.

Другие вопросы по тегам