Подтвердить что ты не робот

Захват запросов 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 может захватить входящий запрос и нажать на журнал?
4b9b3361

Ответ 1

Одним из способов захвата необработанного сообщения является использование 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();
    }
}

Ответ 2

Вы также можете реализовать, поместив код в 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 я изменил его так. Обратите внимание, что Конфигурация - это статический класс, который я создаю для чтения свойств из web.config и других областей.

    protected void Application_BeginRequest(object sender, EventArgs e)
    {

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

Ответ 3

Вы знаете, что вам не нужно на самом деле создавать 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

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

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


Согласно Стивен де Салас, вы можете использовать свойство Request.InputStream в веб-методе. Я не пробовал это, но он говорит, что это работает.

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

Ответ 5

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