在ASP.NET MVC&IIS7记录原始HTTP请求/响应在ASP.NET MVC&IIS7记录原

2019-05-13 17:34发布

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

什么我不知道的是如何得到这个数据在最不“错位”的方式举行。 我可以重新构成什么,我相信请求看起来像通过检查的所有属性HttpRequest对象,并从中构建一个字符串(以及类似的反应),但我真的很想得到实际的请求/响应数据的保持这对线路发送。

我很高兴地用任何拦截机制,如过滤器,模块等,并且该解决方案可以具体到IIS7。 不过,我更愿意把它放在只有托管代码。

任何建议?

编辑:我注意到HttpRequest具有SaveAs方法,可以节约请求到磁盘但这种重建使用的,可以不公开(相当为什么不允许访问内部辅助方法保存到用户的负载从内部状态的要求-provided流,我不知道)。 所以它开始看起来像我必须尽我所能,重建从对象请求/响应文本......呻吟。

编辑2:请注意,我说的包括整个请求方法,路径,页眉等电流响应只看不包括该信息的体流。

编辑3:难道没人看的问题在这里? 五个答案为止可是并没有一个连暗示的方式来获得整个原料上了线的要求。 是的,我知道我可以从请求对象捕获输出流和标题和URL和所有的东西。 我已经说过,在这个问题,请参阅:

我可以重新构成什么,我相信请求看起来像通过检查HttpRequest对象的所有属性,并从中构建一个字符串(以及类似的反应),但我真的很想得到实际的请求/响应数据的保持这对线路发送。

如果您知道完整的原始数据(包括标题,URL,HTTP方法等),根本无法再取回这将是了解的。 同样,如果你知道如何得到这一切在原始格式(是的,我的意思还是包括标题,URL,HTTP方法等),而不必重建它,这是我问,那么这将是非常有用的。 但是,告诉我,我可以从重建它HttpRequest / HttpResponse物体是没有作用的。 我知道。 我已经说过了。


请注意:有人开始前说这是一个坏主意,或将限制可扩展性等,我们也将实现节流,连续交付,以及抗重播机制在分布式环境中,因此,无论如何,需要数据库日志。 我不是在寻找的,这是否是一个好主意的讨论,我在寻找如何可以做到。

Answer 1:

当然使用IHttpModule和实施BeginRequestEndRequest事件。

所有的“原始”数据是之间存在HttpRequestHttpResponse ,它只是没有在一个单一的原始格式。 下面是打造提琴手式转储(约接近原始的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;

商店filter在那里你可以得到它在EndRequest处理程序。 我建议在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);
    }
}


Answer 2:

上的HttpRequest下面的扩展方法创建一个可以粘贴到提琴手和回放的字符串。

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


Answer 3:

您可以使用ALL_RAW服务器变量获得与请求发送的原始HTTP头,那么你就可以得到的InputStream和往常一样:

string originalHeader = HttpHandler.Request.ServerVariables["ALL_RAW"];

退房: http://msdn.microsoft.com/en-us/library/ms524602%28VS.90%29.aspx



Answer 4:

好吧,我使用的请求参数工作的一个项目,做了,也许不是太深,日志:

看一看:

public class LogAttribute : ActionFilterAttribute
{
    private void Log(string stageName, RouteData routeData, HttpContextBase httpContext)
    {
        //Use the request and route data objects to grab your data
        string userIP = httpContext.Request.UserHostAddress;
        string userName = httpContext.User.Identity.Name;
        string reqType = httpContext.Request.RequestType;
        string reqData = GetRequestData(httpContext);
        string controller = routeData["controller"];
        string action = routeData["action"];

        //TODO:Save data somewhere
    }

    //Aux method to grab request data
    private string GetRequestData(HttpContextBase context)
    {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < context.Request.QueryString.Count; i++)
        {
            sb.AppendFormat("Key={0}, Value={1}<br/>", context.Request.QueryString.Keys[i], context.Request.QueryString[i]);
        }

        for (int i = 0; i < context.Request.Form.Count; i++)
        {
            sb.AppendFormat("Key={0}, Value={1}<br/>", context.Request.Form.Keys[i], context.Request.Form[i]);
        }

        return sb.ToString();
    }

可以装饰你的控制器类完全记录它:

[Log]
public class TermoController : Controller {...}

或登录只是一些个人的行动方法

[Log]
public ActionResult LoggedAction(){...}


Answer 5:

你需要保持它在托管代码的原因吗?

值得一提的是,您可以启用无法跟踪记录在IIS7如果你不喜欢重新发明轮子。 该记录头,请求和响应主体,以及许多其他的事情。



Answer 6:

我去McKAMEY的做法。 下面是我写的一个模块,这将让你开始,希望为您节省一些时间。 你需要的东西,你的作品明显堵塞记录器:

public class CaptureTrafficModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(context_BeginRequest);
        context.EndRequest += new EventHandler(context_EndRequest);
    }

    void context_BeginRequest(object sender, EventArgs e)
    {
        HttpApplication app = sender as HttpApplication;

        OutputFilterStream filter = new OutputFilterStream(app.Response.Filter);
        app.Response.Filter = filter;

        StringBuilder request = new StringBuilder();
        request.Append(app.Request.HttpMethod + " " + app.Request.Url);
        request.Append("\n");
        foreach (string key in app.Request.Headers.Keys)
        {
            request.Append(key);
            request.Append(": ");
            request.Append(app.Request.Headers[key]);
            request.Append("\n");
        }
        request.Append("\n");

        byte[] bytes = app.Request.BinaryRead(app.Request.ContentLength);
        if (bytes.Count() > 0)
        {
            request.Append(Encoding.ASCII.GetString(bytes));
        }
        app.Request.InputStream.Position = 0;

        Logger.Debug(request.ToString());
    }

    void context_EndRequest(object sender, EventArgs e)
    {
        HttpApplication app = sender as HttpApplication;
        Logger.Debug(((OutputFilterStream)app.Response.Filter).ReadStream());
    }

    private ILogger _logger;
    public ILogger Logger
    {
        get
        {
            if (_logger == null)
                _logger = new Log4NetLogger();
            return _logger;
        }
    }

    public void Dispose()
    {
        //Does nothing
    }
}


Answer 7:

OK,所以它看起来像答案是“不,你不能得到的原始数据,你必须重建从解析对象的属性的请求/响应”。 噢,我已经做了重建的事情。



Answer 8:

使用IHttpModule的 :

    namespace Intercepts
{
    class Interceptor : IHttpModule
    {
        private readonly InterceptorEngine engine = new InterceptorEngine();

        #region IHttpModule Members

        void IHttpModule.Dispose()
        {
        }

        void IHttpModule.Init(HttpApplication application)
        {
            application.EndRequest += new EventHandler(engine.Application_EndRequest);
        }
        #endregion
    }
}

    class InterceptorEngine
    {       
        internal void Application_EndRequest(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;

            HttpResponse response = application.Context.Response;
            ProcessResponse(response.OutputStream);
        }

        private void ProcessResponse(Stream stream)
        {
            Log("Hello");
            StreamReader sr = new StreamReader(stream);
            string content = sr.ReadToEnd();
            Log(content);
        }

        private void Log(string line)
        {
            Debugger.Log(0, null, String.Format("{0}\n", line));
        }
    }


Answer 9:

如果偶尔使用,避开狈,怎么样东西原油象下面这样?

Public Function GetRawRequest() As String
    Dim str As String = ""
    Dim path As String = "C:\Temp\REQUEST_STREAM\A.txt"
    System.Web.HttpContext.Current.Request.SaveAs(path, True)
    str = System.IO.File.ReadAllText(path)
    Return str
End Function


Answer 10:

您可以在完成这一DelegatingHandler不使用OutputFilter使用在.NET 4.5中其他的答案中提到Stream.CopyToAsync()函数。

我不知道的细节,但它不会触发所有的不好的事情发生,当你试图直接读取响应流。

例:

public class LoggingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        DoLoggingWithRequest(request);
        var response = await base.SendAsync(request, cancellationToken);
        await DoLoggingWithResponse(response);
        return response;
    }

    private async Task DologgingWithResponse(HttpResponseMessage response) {
        var stream = new MemoryStream();
        await response.Content.CopyToAsync(stream).ConfigureAwait(false);     
        DoLoggingWithResponseContent(Encoding.UTF8.GetString(stream.ToArray()));

        // The rest of this call, the implementation of the above method, 
        // and DoLoggingWithRequest is left as an exercise for the reader.
    }
}


Answer 11:

我知道这不是托管代码,但我要建议的ISAPI筛选器。 这是一个两三年,因为我之前和ASP.Net做这件事后有“乐趣”保持我自己的ISAPI,但据我记得你可以得到所有这些东西的访问。

http://msdn.microsoft.com/en-us/library/ms524610.aspx

如果一个HTTP模块是不是你所需要的足够好,那么我不认为这是在所需的详细金额这样做的任何管理方式。 这会是一个痛苦的,虽然做。



Answer 12:

我同意其他人,请使用IHttpModule的。 看看在这个问题的答案,这确实几乎你都在问同样的事情。 它记录的请求和响应,但没有头。

如何跟踪ScriptService WebService的请求?



Answer 13:

这可能是最好的做您的应用程序之外。 您可以设置一个反向代理做这样的事情(以及更多)。 反向代理基本上是在您的服务器机房位于一个Web服务器和Web服务器(S)和客户端之间的立场。 见http://en.wikipedia.org/wiki/Reverse_proxy



Answer 14:

与FigmentEngine同意, IHttpModule似乎是要走的路。

考虑httpworkerrequestreadentitybodyGetPreloadedEntityBody

要获得httpworkerrequest你需要这样做:

(HttpWorkerRequest)inApp.Context.GetType().GetProperty("WorkerRequest", bindingFlags).GetValue(inApp.Context, null);

其中inApp是HttpApplication对象。



Answer 15:

HttpRequestHttpResponse预MVC曾经有过GetInputStream()GetOutputStream()可能被用于该目的。 有没有考虑在MVC的一部分,所以我不确定他们是availavle但可能是一个想法:)



文章来源: Logging raw HTTP request/response in ASP.NET MVC & IIS7