如何在ASP.NET MVC和IIS7中记录原始HTTP请求/响应?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (261)

我正在编写一个Web服务(使用ASP.NET MVC),并且为了支持的目的,我们希望能够尽可能接近地记录请求和响应到原始的在线格式(即包括HTTP方法,路径,所有标题和正文)到数据库中。

我不确定的是如何以“最坏”的方式获得这些数据。我可以通过检查HttpRequest对象的所有属性并从它们构建一个字符串(以及类似的响应)来重新构成我认为请求看起来像的内容,但我真的想要掌握实际的请求/响应数据在电线上发送。

我很高兴使用任何拦截机制,如过滤器,模块等,解决方案可以特定于IIS7。不过,我宁愿只保留在托管代码中。

任何建议?

我注意到,HttpRequest有一种SaveAs方法可以将请求保存到磁盘,但这使用内部帮助程序方法重建内部状态的请求,无法公开访问(为什么这不允许保存到用户提供的流我不知道)。所以它开始看起来像我必须尽我所能重建来自对象的请求/响应文本。

请注意,我说过整个请求,包括方法,路径,标题等等。当前响应仅查看不包含此信息的正文流。

提问于
用户回答回答于

下面的HttpRequest扩展方法将创建一个字符串,该字符串可以粘贴到fiddler中并重放。

namespace System.Web
{
    using System.IO;

    /// <summary>
    /// Extension methods for HTTP Request.
    /// <remarks>
    /// See the HTTP 1.1 specification http://www.w3.org/Protocols/rfc2616/rfc2616.html
    /// for details of implementation decisions.
    /// </remarks>
    /// </summary>
    public static class HttpRequestExtensions
    {
        /// <summary>
        /// Dump the raw http request to a string. 
        /// </summary>
        /// <param name="request">The <see cref="HttpRequest"/> that should be dumped.       </param>
        /// <returns>The raw HTTP request.</returns>
        public static string ToRaw(this HttpRequest request)
        {
            StringWriter writer = new StringWriter();

            WriteStartLine(request, writer);
            WriteHeaders(request, writer);
            WriteBody(request, writer);

            return writer.ToString();
        }

        private static void WriteStartLine(HttpRequest request, StringWriter writer)
        {
            const string SPACE = " ";

            writer.Write(request.HttpMethod);
            writer.Write(SPACE + request.Url);
            writer.WriteLine(SPACE + request.ServerVariables["SERVER_PROTOCOL"]);
        }

        private static void WriteHeaders(HttpRequest request, StringWriter writer)
        {
            foreach (string key in request.Headers.AllKeys)
            {
                writer.WriteLine(string.Format("{0}: {1}", key, request.Headers[key]));
            }

            writer.WriteLine();
        }

        private static void WriteBody(HttpRequest request, StringWriter writer)
        {
            StreamReader reader = new StreamReader(request.InputStream);

            try
            {
                string body = reader.ReadToEnd();
                writer.WriteLine(body);
            }
            finally
            {
                reader.BaseStream.Position = 0;
            }
        }
    }
}
用户回答回答于

绝对使用IHttpModule和实施BeginRequestEndRequest事件。

所有的“原始”数据是之间存在HttpRequest并且HttpResponse,它只是没有在一个单一的原始格式。以下是构建Fiddler风格转储所需的部分(与获取的原始HTTP大致接近):

request.HttpMethod + " " + request.RawUrl + " " + request.ServerVariables["SERVER_PROTOCOL"]
request.Headers // loop through these "key: value"
request.InputStream // make sure to reset the Position after reading or later reads may fail

关于回应:

"HTTP/1.1 " + response.Status
response.Headers // loop through these "key: value"

请注意无法读取响应流。因此,必须向输出流中添加一个过滤器,并捕获一个副本。

在你的BeginRequest,需要添加一个响应筛选器:

HttpResponse response = HttpContext.Current.Response;
OutputFilterStream filter = new OutputFilterStream(response.Filter);
response.Filter = filter;

存储filterEndRequest处理程序中可以到达的位置。我建议在HttpContext.Items。然后可以获得完整的响应数据filter.ReadStream()

然后OutputFilterStream使用Decorator模式作为流的包装:

/// <summary>
/// A stream which keeps an in-memory copy as it passes the bytes through
/// </summary>
public class OutputFilterStream : Stream
{
    private readonly Stream InnerStream;
    private readonly MemoryStream CopyStream;

    public OutputFilterStream(Stream inner)
    {
        this.InnerStream = inner;
        this.CopyStream = new MemoryStream();
    }

    public string ReadStream()
    {
        lock (this.InnerStream)
        {
            if (this.CopyStream.Length <= 0L ||
                !this.CopyStream.CanRead ||
                !this.CopyStream.CanSeek)
            {
                return String.Empty;
            }

            long pos = this.CopyStream.Position;
            this.CopyStream.Position = 0L;
            try
            {
                return new StreamReader(this.CopyStream).ReadToEnd();
            }
            finally
            {
                try
                {
                    this.CopyStream.Position = pos;
                }
                catch { }
            }
        }
    }


    public override bool CanRead
    {
        get { return this.InnerStream.CanRead; }
    }

    public override bool CanSeek
    {
        get { return this.InnerStream.CanSeek; }
    }

    public override bool CanWrite
    {
        get { return this.InnerStream.CanWrite; }
    }

    public override void Flush()
    {
        this.InnerStream.Flush();
    }

    public override long Length
    {
        get { return this.InnerStream.Length; }
    }

    public override long Position
    {
        get { return this.InnerStream.Position; }
        set { this.CopyStream.Position = this.InnerStream.Position = value; }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return this.InnerStream.Read(buffer, offset, count);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        this.CopyStream.Seek(offset, origin);
        return this.InnerStream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        this.CopyStream.SetLength(value);
        this.InnerStream.SetLength(value);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        this.CopyStream.Write(buffer, offset, count);
        this.InnerStream.Write(buffer, offset, count);
    }
}

扫码关注云+社区

领取腾讯云代金券