【问题标题】:Capturing SOAP requests to an ASP.NET ASMX web service捕获对 ASP.NET ASMX Web 服务的 SOAP 请求
【发布时间】:2011-02-07 03:39:35
【问题描述】:

考虑将传入的 SOAP 请求记录到 ASP.NET ASMX Web 服务的要求。任务是捕获发送到 Web 服务的原始 XML。

需要记录传入消息以进行调试检查。该应用程序已经使用了自己的日志库,因此理想的用法是这样的:

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

Logger.LogMessage(incomingSoapRequest);
  • 是否有任何简单的解决方案来捕获传入 SOAP 请求的原始 XML?
  • 您将处理哪些事件来访问此对象和相关属性?
  • IIS 是否可以捕获传入的请求并推送到日志?

【问题讨论】:

    标签: asp.net web-services soap asmx


    【解决方案1】:

    也可以通过将代码放在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);
    
    }
    

    我的 Web 服务中有一个 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();
            }
        }
    

    【讨论】:

    • 不错。我们同意了,谢谢。
    【解决方案2】:

    捕获原始消息的一种方法是使用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();
        }
    }
    

    【讨论】:

    • 我不认为这对 UTF-8 编码效果不好。另外,您确定流是可搜索的吗?总是?
    • 对于输入流,是的。这是我目前用来调试传入请求的,我还没有遇到任何问题。然而,响应流是不可搜索的。我必须先包装响应流(原始/副本),然后才能记录它。
    • 输出流呢??如何使用 IHttpModule 实现捕获它?
    • 这给了我 soapMessage="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0"
    【解决方案3】:

    您知道您实际上不需要创建HttpModule 对吗?

    您还可以从您的 asmx WebMethod 中读取 Request.InputStream 的内容。

    这里是an article I wrote on this approach

    代码如下:

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

    【讨论】:

    • 我尝试了上面的 Steven de Salas 示例,但我的 HttpContext.Current 始终为空。我在这里做错了什么吗?谢谢
    【解决方案4】:

    没有简单的方法可以做到这一点。您必须实现SoapExtension。上一个链接中的示例显示了一个可用于记录数据的扩展。

    如果您一直在使用 WCF,那么您可以简单地设置配置以生成消息日志。


    根据Steven de Salas,您可以在web 方法中使用Request.InputStream 属性。我没有尝试过,但他说它有效。

    我想同时使用 http 和 https 以及同时运行和不运行其他 SoapExtensions 来测试它。这些可能会影响InputStream 设置的流类型。例如,某些流无法查找,这可能会使您的流位于数据末尾之后,并且您无法移动到开头。

    【讨论】:

    • -1 有一个简单的方法可以做到这一点。只需在 WebMethod 中使用 Request.InputStream。
    • 你试过了吗?那是可搜索的流吗?如果没有,那么在调用 web 方法时,流已经被读取到末尾。
    • 是的,它有效,您可以强制 Stream.Position = 0 但在 SOAP 请求期间实际上不需要。
    • 我很惊讶。我曾与 SoapExtension 合作过。在这种情况下,您获得的流是不可搜索的。您应该添加您的评论作为答案,而不是投反对票。
    • 我在投反对票之前添加了一个帖子(见上文)。我投反对票的原因是因为我不希望人们认为这样做实际上很难。 Request.InputStream 在标准 SOAP 请求期间调用 WebMethod 的阶段可用,并且可以毫无问题地访问。我想很有可能在其他情况下它可能无法访问或“可搜索”,但这不是问题所在。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-29
    • 2010-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-26
    相关资源
    最近更新 更多