Захват 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
установлено в. Например, некоторые потоки не могут искать, что может оставить вас в положении потока после конца данных, и которое вы не можете переместить в начало.