Захват SOAP-запросов в веб-службу ASP.NET ASMX - PullRequest
30 голосов
/ 12 апреля 2010

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

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

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

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

Ответы [ 4 ]

19 голосов
/ 13 апреля 2010

Один из способов получения необработанного сообщения - использовать 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();
    }
}
18 голосов
/ 02 апреля 2011

Вы также можете реализовать, разместив код в 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();
        }
    }
12 голосов
/ 12 октября 2011

Вы знаете, что вам на самом деле не нужно создавать 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;
    }
  }
}
0 голосов
/ 13 апреля 2010

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

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


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

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

...