在ASP中记录原始HTTP请求/响应。净MVC & IIS7

时间:2022-12-04 19:00:07

I'm writing a web service (using ASP.NET MVC) and for support purposes we'd like to be able to log the requests and response in as close as possible to the raw, on-the-wire format (i.e including HTTP method, path, all headers, and the body) into a database.

我正在编写一个web服务(使用ASP)。出于支持的目的,我们希望能够尽可能接近原始的在线格式(i)记录请求和响应。包括HTTP方法、路径、所有头部和正文)到数据库中。

What I'm not sure of is how to get hold of this data in the least 'mangled' way. I can re-constitute what I believe the request looks like by inspecting all the properties of the HttpRequest object and building a string from them (and similarly for the response) but I'd really like to get hold of the actual request/response data that's sent on the wire.

我不确定的是如何以最不“混乱”的方式获取这些数据。通过检查HttpRequest对象的所有属性并从中构建一个字符串(对于响应也是如此),我可以重新构建我认为的请求看起来是什么样子的,但是我确实希望获得在连接上发送的实际请求/响应数据。

I'm happy to use any interception mechanism such as filters, modules, etc. and the solution can be specific to IIS7. However, I'd prefer to keep it in managed code only.

我很乐意使用任何拦截机制,比如过滤器、模块等,解决方案可以针对IIS7。但是,我更愿意将它保存在托管代码中。

Any recommendations?

你有什么推荐吗?

Edit: I note that HttpRequest has a SaveAs method which can save the request to disk but this reconstructs the request from the internal state using a load of internal helper methods that cannot be accessed publicly (quite why this doesn't allow saving to a user-provided stream I don't know). So it's starting to look like I'll have to do my best to reconstruct the request/response text from the objects... groan.

编辑:我注意到HttpRequest有一个SaveAs方法,它可以将请求保存到磁盘上,但是这可以使用不能被公开访问的内部助手方法负载从内部状态重新构造请求(这就是为什么不允许将请求保存到用户提供的流中,我不知道)。因此,看起来我必须尽最大努力重构对象的请求/响应文本……呻吟。

Edit 2: Please note that I said the whole request including method, path, headers etc. The current responses only look at the body streams which does not include this information.

编辑2:请注意,我说的整个请求包括方法、路径、标题等。当前的响应只关注不包含此信息的主体流。

Edit 3: Does nobody read questions around here? Five answers so far and yet not one even hints at a way to get the whole raw on-the-wire request. Yes, I know I can capture the output streams and the headers and the URL and all that stuff from the request object. I already said that in the question, see:

编辑3:这里没有人阅读问题吗?到目前为止,有5个答案,但没有一个甚至暗示可以通过网络来获取整个原始请求。是的,我知道我可以捕获输出流和header和URL以及来自请求对象的所有内容。我在这个问题中已经说过,看

I can re-constitute what I believe the request looks like by inspecting all the properties of the HttpRequest object and building a string from them (and similarly for the response) but I'd really like to get hold of the actual request/response data that's sent on the wire.

通过检查HttpRequest对象的所有属性并从中构建一个字符串(对于响应也是如此),我可以重新构建我认为的请求看起来是什么样子的,但是我确实希望获得在连接上发送的实际请求/响应数据。

If you know the complete raw data (including headers, url, http method, etc.) simply cannot be retrieved then that would be useful to know. Similarly if you know how to get it all in the raw format (yes, I still mean including headers, url, http method, etc.) without having to reconstruct it, which is what I asked, then that would be very useful. But telling me that I can reconstruct it from the HttpRequest/HttpResponse objects is not useful. I know that. I already said it.

如果您知道无法检索完整的原始数据(包括头、url、http方法等),那么了解这些数据将非常有用。类似地,如果您知道如何以原始格式(是的,我仍然是指包含头信息、url、http方法等)获取所有信息,而不需要重构它(我就是这么要求的),那么这将非常有用。但是告诉我我可以从HttpRequest/HttpResponse对象中重构它是没有用的。我知道。我已经说过它。


Please note: Before anybody starts saying this is a bad idea, or will limit scalability, etc., we'll also be implementing throttling, sequential delivery, and anti-replay mechanisms in a distributed environment, so database logging is required anyway. I'm not looking for a discussion of whether this is a good idea, I'm looking for how it can be done.

请注意:在有人开始说这是一个坏主意或限制可伸缩性等之前,我们还将在分布式环境中实现节流、顺序交付和反重播机制,因此无论如何都需要数据库日志记录。我不是在讨论这是不是一个好主意,我在寻找它是如何做到的。

15 个解决方案

#1


82  

Definitely use an IHttpModule and implement the BeginRequest and EndRequest events.

一定要使用IHttpModule并实现BeginRequest和EndRequest事件。

All of the "raw" data is present between HttpRequest and HttpResponse, it just isn't in a single raw format. Here are the parts needed to build Fiddler-style dumps (about as close to raw HTTP as it gets):

所有的“原始”数据都存在于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

For the response:

为响应:

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

Note that you cannot read the response stream so you have to add a filter to the Output stream and capture a copy.

注意,您不能读取响应流,因此必须向输出流添加过滤器并捕获副本。

In your BeginRequest, you will need to add a response filter:

在你的BeginRequest中,你需要添加一个响应过滤器:

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

Store filter where you can get to it in the EndRequest handler. I suggest in HttpContext.Items. There can then get the full response data in filter.ReadStream().

存储过滤器,您可以在EndRequest处理程序中访问它。我建议在httpcontext . items。然后,可以在filter.ReadStream()中获得完整的响应数据。

Then implement OutputFilterStream using the Decorator pattern as a wrapper around a stream:

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

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

#2


45  

The following extension method on HttpRequest will create a string that can be pasted into fiddler and replayed.

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

#3


35  

You can use the ALL_RAW server variable to get the original HTTP headers sent with the request, then you can get the InputStream as usual:

您可以使用ALL_RAW服务器变量获取随请求发送的原始HTTP头,然后您可以像往常一样获得InputStream:

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

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

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

#4


14  

Well, I'm working on a project and did, maybe not too deep, a log using the request params:

我在做一个项目,可能不太深,使用请求参数的日志:

Take a look:

看一看:

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

You can decorate your controllers class for log it entirely:

你可以装饰你的控制器类来完整地记录它:

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

or log just some individual action methods

或者只记录一些单独的操作方法

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

#5


9  

Any reason you need to keep it in managed code?

您需要将其保存在托管代码中吗?

It is worth mentioning that you can enable Failed Trace logging in IIS7 if you don't like re-inventing the wheel. This logs headers, the request and response body as well as many other things.

值得一提的是,如果不喜欢重新发明轮,可以在IIS7中启用失败的跟踪日志记录。它记录头信息、请求和响应主体以及许多其他内容。

在ASP中记录原始HTTP请求/响应。净MVC & IIS7

#6


7  

I went with McKAMEY's approach. Here's a module I wrote that will get you started and hopefully save you some time. You'll need to plug the Logger obviously with something that works for you:

我采用了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
    }
}

#7


5  

OK, so it looks like the answer is "no you can't get the raw data, you have to reconstruct the request/response from the properties of the parsed objects". Oh well, I've done the reconstruction thing.

看起来答案是“不,你不能得到原始数据,你必须从解析对象的属性中重构请求/响应”。好吧,我做了重建。

#8


4  

use a IHttpModule:

使用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));
        }
    }

#9


3  

if for occasional use, to get around a tight corner, how about something crude like below?

如果只是为了偶尔的用途,为了在一个狭窄的角落里转一转,下面这种粗糙的东西怎么样?

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

#10


0  

I agree with the others, use an IHttpModule. Take a look at the answer to this question, which does almost the same thing that you are asking. It logs the request and response, but without headers.

我同意其他的,使用IHttpModule。看看这个问题的答案,它几乎和你问的一样。它记录请求和响应,但没有头文件。

How to trace ScriptService WebService requests?

如何跟踪ScriptService web服务请求?

#11


0  

It might be best to do this outside of your application. You can set up a reverse proxy to do things like this (and much more). A reverse proxy is basically a web server that sits in your server room, and stands between your web server(s) and the client. See http://en.wikipedia.org/wiki/Reverse_proxy

最好在应用程序之外执行此操作。您可以设置一个反向代理来执行类似的操作(以及更多操作)。反向代理基本上是位于服务器空间中的web服务器,位于web服务器和客户机之间。参见http://en.wikipedia.org/wiki/Reverse_proxy

#12


0  

Agree with FigmentEngine, IHttpModule appears to be the way to go.

同意FigmentEngine, IHttpModule似乎是解决方案。

Look into httpworkerrequest, readentitybody and GetPreloadedEntityBody.

查看httpworkerrequest、readentitybody和GetPreloadedEntityBody。

To get the httpworkerrequest you need to do this:

要获取httpworkerrequest,您需要这样做:

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

where inApp is the httpapplication object.

inApp是httpapplication对象。

#13


0  

You can accomplish this in a DelegatingHandler without using the OutputFilter mentioned in other answers in .NET 4.5 using the Stream.CopyToAsync() function.

您可以在delegate . inghandler中实现这一点,而不用使用. net 4.5中其他答案中提到的OutputFilter,而是使用Stream.CopyToAsync()函数。

I'm not sure on the details, but it does not trigger all of the bad things that happen when you attempt to directly read the response stream.

我不确定细节,但是当您试图直接读取响应流时,它不会触发所有的坏事情。

Example:

例子:

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

#14


0  

HttpRequest and HttpResponse pre MVC used to have a GetInputStream() and GetOutputStream() that could be used for that purpose. Haven't look into those part in MVC so Im not sure they are availavle but might be an idea :)

HttpRequest和HttpResponse pre MVC曾经有一个GetInputStream()和GetOutputStream(),可以用于这个目的。还没有在MVC中查看过这些部分,所以我不确定它们是否可用,但可能是一个想法:)

#15


-1  

I know it's not managed code, but I'm going to suggest an ISAPI filter. It's been a couple of years since I've had the "pleasure" of maintaining my own ISAPI but from what I recall you can get access to all this stuff, both before and after ASP.Net has done it's thing.

我知道它不是托管代码,但我将建议一个ISAPI过滤器。我已经有好几年没有维护我自己的ISAPI的“乐趣”了,但是我记得你可以访问所有这些东西,在ASP之前和之后。Net已经做到了。

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

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

If a HTTPModule isn't good enough for what you need, then I just don't think there is any managed way of doing this in the required amount of detail. It's gonna be a pain to do though.

如果HTTPModule不足以满足您的需要,那么我认为在所需的详细信息中没有任何管理方法可以实现这一点。但是这样做会很痛苦。

#1


82  

Definitely use an IHttpModule and implement the BeginRequest and EndRequest events.

一定要使用IHttpModule并实现BeginRequest和EndRequest事件。

All of the "raw" data is present between HttpRequest and HttpResponse, it just isn't in a single raw format. Here are the parts needed to build Fiddler-style dumps (about as close to raw HTTP as it gets):

所有的“原始”数据都存在于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

For the response:

为响应:

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

Note that you cannot read the response stream so you have to add a filter to the Output stream and capture a copy.

注意,您不能读取响应流,因此必须向输出流添加过滤器并捕获副本。

In your BeginRequest, you will need to add a response filter:

在你的BeginRequest中,你需要添加一个响应过滤器:

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

Store filter where you can get to it in the EndRequest handler. I suggest in HttpContext.Items. There can then get the full response data in filter.ReadStream().

存储过滤器,您可以在EndRequest处理程序中访问它。我建议在httpcontext . items。然后,可以在filter.ReadStream()中获得完整的响应数据。

Then implement OutputFilterStream using the Decorator pattern as a wrapper around a stream:

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

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

#2


45  

The following extension method on HttpRequest will create a string that can be pasted into fiddler and replayed.

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

#3


35  

You can use the ALL_RAW server variable to get the original HTTP headers sent with the request, then you can get the InputStream as usual:

您可以使用ALL_RAW服务器变量获取随请求发送的原始HTTP头,然后您可以像往常一样获得InputStream:

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

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

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

#4


14  

Well, I'm working on a project and did, maybe not too deep, a log using the request params:

我在做一个项目,可能不太深,使用请求参数的日志:

Take a look:

看一看:

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

You can decorate your controllers class for log it entirely:

你可以装饰你的控制器类来完整地记录它:

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

or log just some individual action methods

或者只记录一些单独的操作方法

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

#5


9  

Any reason you need to keep it in managed code?

您需要将其保存在托管代码中吗?

It is worth mentioning that you can enable Failed Trace logging in IIS7 if you don't like re-inventing the wheel. This logs headers, the request and response body as well as many other things.

值得一提的是,如果不喜欢重新发明轮,可以在IIS7中启用失败的跟踪日志记录。它记录头信息、请求和响应主体以及许多其他内容。

在ASP中记录原始HTTP请求/响应。净MVC & IIS7

#6


7  

I went with McKAMEY's approach. Here's a module I wrote that will get you started and hopefully save you some time. You'll need to plug the Logger obviously with something that works for you:

我采用了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
    }
}

#7


5  

OK, so it looks like the answer is "no you can't get the raw data, you have to reconstruct the request/response from the properties of the parsed objects". Oh well, I've done the reconstruction thing.

看起来答案是“不,你不能得到原始数据,你必须从解析对象的属性中重构请求/响应”。好吧,我做了重建。

#8


4  

use a IHttpModule:

使用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));
        }
    }

#9


3  

if for occasional use, to get around a tight corner, how about something crude like below?

如果只是为了偶尔的用途,为了在一个狭窄的角落里转一转,下面这种粗糙的东西怎么样?

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

#10


0  

I agree with the others, use an IHttpModule. Take a look at the answer to this question, which does almost the same thing that you are asking. It logs the request and response, but without headers.

我同意其他的,使用IHttpModule。看看这个问题的答案,它几乎和你问的一样。它记录请求和响应,但没有头文件。

How to trace ScriptService WebService requests?

如何跟踪ScriptService web服务请求?

#11


0  

It might be best to do this outside of your application. You can set up a reverse proxy to do things like this (and much more). A reverse proxy is basically a web server that sits in your server room, and stands between your web server(s) and the client. See http://en.wikipedia.org/wiki/Reverse_proxy

最好在应用程序之外执行此操作。您可以设置一个反向代理来执行类似的操作(以及更多操作)。反向代理基本上是位于服务器空间中的web服务器,位于web服务器和客户机之间。参见http://en.wikipedia.org/wiki/Reverse_proxy

#12


0  

Agree with FigmentEngine, IHttpModule appears to be the way to go.

同意FigmentEngine, IHttpModule似乎是解决方案。

Look into httpworkerrequest, readentitybody and GetPreloadedEntityBody.

查看httpworkerrequest、readentitybody和GetPreloadedEntityBody。

To get the httpworkerrequest you need to do this:

要获取httpworkerrequest,您需要这样做:

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

where inApp is the httpapplication object.

inApp是httpapplication对象。

#13


0  

You can accomplish this in a DelegatingHandler without using the OutputFilter mentioned in other answers in .NET 4.5 using the Stream.CopyToAsync() function.

您可以在delegate . inghandler中实现这一点,而不用使用. net 4.5中其他答案中提到的OutputFilter,而是使用Stream.CopyToAsync()函数。

I'm not sure on the details, but it does not trigger all of the bad things that happen when you attempt to directly read the response stream.

我不确定细节,但是当您试图直接读取响应流时,它不会触发所有的坏事情。

Example:

例子:

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

#14


0  

HttpRequest and HttpResponse pre MVC used to have a GetInputStream() and GetOutputStream() that could be used for that purpose. Haven't look into those part in MVC so Im not sure they are availavle but might be an idea :)

HttpRequest和HttpResponse pre MVC曾经有一个GetInputStream()和GetOutputStream(),可以用于这个目的。还没有在MVC中查看过这些部分,所以我不确定它们是否可用,但可能是一个想法:)

#15


-1  

I know it's not managed code, but I'm going to suggest an ISAPI filter. It's been a couple of years since I've had the "pleasure" of maintaining my own ISAPI but from what I recall you can get access to all this stuff, both before and after ASP.Net has done it's thing.

我知道它不是托管代码,但我将建议一个ISAPI过滤器。我已经有好几年没有维护我自己的ISAPI的“乐趣”了,但是我记得你可以访问所有这些东西,在ASP之前和之后。Net已经做到了。

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

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

If a HTTPModule isn't good enough for what you need, then I just don't think there is any managed way of doing this in the required amount of detail. It's gonna be a pain to do though.

如果HTTPModule不足以满足您的需要,那么我认为在所需的详细信息中没有任何管理方法可以实现这一点。但是这样做会很痛苦。