Лесозаготовки необработанные HTTP-запрос/ответ в ASP.NET в MVC & для IIS7



Я пишу веб-сервис (с помощью ASP.NET MVC) и для целей поддержки мы хотели бы иметь возможность регистрировать запросы и ответы как можно ближе к необработанному, проводному формату (т. е. включая метод HTTP, путь, все заголовки и тело) в базе данных.



то, что я не уверен в том, как получить эти данные в наименее "искаженном" виде. Я могу воссоздать то, что я считаю, что запрос выглядит, проверяя все свойства HttpRequest объект и построение a строка из них (и аналогично для ответа), но я действительно хотел бы получить фактические данные запроса/ответа, которые отправляются по проводу.



Я рад использовать любой механизм перехвата, таких как фильтры, модули и т. д. и решение может быть специфичным для IIS7. Однако я бы предпочел сохранить его только в управляемом коде.



какие рекомендации?



Edit: замечу, что HttpRequest есть SaveAs метод, который может спасти запросу на диск, но это восстанавливает запрос из внутреннего состояния, используя нагрузку внутренних вспомогательных методов, которые не могут быть доступны публично (почему это не позволяет сохранять в предоставленный пользователем поток, я не знаю). Так что это начинает выглядеть так, как будто мне придется сделать все возможное, чтобы восстановить текст запроса/ответа из объектов... стон.



Edit 2: обратите внимание, что я сказал весь запрос, в том числе способ, путь, заголовки и т. д. Электрический ток ответы только смотрят на потоки тела, которые не включают эту информацию.



Edit 3: здесь никто не читает вопросы? Пять ответов до сих пор, и все же ни один даже не намекает на способ получить весь сырой запрос на провод. Да, я знаю, что могу захватить выходные потоки и заголовки, URL-адрес и все это из объекта запроса. Я уже сказал, что в вопросе, см.:




Я могу воссоздать то, что я считаю просьбой похоже, что путем проверки всех свойств объекта HttpRequest и построения строки из них (и аналогично для ответа), но я действительно хотел бы получить фактические данные запроса/ответа, которые отправляются по проводу.




если вы знаете полное необработанные данные (включая заголовки, url, метод http и т. д.) просто не может быть восстановлен, то что было бы полезно знать. Аналогично, если вы знаете, как получить все это в формате raw (да, я все еще имею в виду включая заголовки, url, метод http и т. д.) без необходимости реконструировать его, что я и спросил, тогда это было бы очень полезно. Но говорит мне, что я могу восстановить его из HttpRequest/HttpResponse объекты не полезны. Я знаю это. Я уже сказал это.





обратите внимание: прежде чем кто-нибудь начнет говорить, что это плохая идея, или ограничит масштабируемость и т. д. мы также будем реализовывать механизмы дросселирования, последовательной доставки и защиты от воспроизведения в распределенном режиме среда, поэтому ведение журнала базы данных требуется в любом случае. Я не ищу обсуждения того, является ли это хорошей идеей, я ищу, как это можно сделать.

580   15  

15 ответов:

определенно IHttpModule и реализовать BeginRequest и EndRequest событий.

все "сырые" данные присутствуют между HttpRequest и HttpResponse, это просто не в одном формате RAW. Вот части, необходимые для создания дампов в стиле Fiddler (примерно так же близко к raw 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 Использование шаблона декоратора в качестве обертки вокруг потока:

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

следующий метод расширения на 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;
            }
        }
    }
}

вы можете использовать переменную сервера ALL_RAW, чтобы получить исходные заголовки HTTP, отправленные с запросом, затем вы можете получить InputStream как обычно:

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

проверить: http://msdn.microsoft.com/en-us/library/ms524602%28VS.90%29.aspx

Ну, я работаю над проектом и сделал, может быть, не слишком глубоко, журнал, используя запрос params:

смотрим:

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(){...}

по какой причине вам нужно сохранить его в управляемом коде?

стоит отметить, что вы можете включить ошибка ведения журнала трассировки в IIS7, если вам не нравится повторно изобретать колесо. Это журналы заголовки запроса и ответа, а также многое другое.

Failed Trace Logging

Я пошел с подходом Макками. Вот модуль, который я написал, который поможет вам начать и, надеюсь, сэкономит вам некоторое время. Вам нужно будет подключить регистратор, очевидно, с чем-то, что работает для вас:

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

ОК, поэтому похоже, что ответ "Нет, вы не можете получить необработанные данные, вам нужно восстановить запрос/ответ из свойств анализируемых объектов". Ну что ж, я сделал реконструкцию.

использовать 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));
        }
    }

если для случайного использования, чтобы обойти жесткий угол, как насчет чего-то грубого, как ниже?

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

Я согласен с другими, используйте IHttpModule. Взгляните на ответ на этот вопрос, который делает почти то же самое, что вы просите. Он регистрирует запрос и ответ, но без заголовков.

как отслеживать запросы ScriptService WebService?

Это может быть лучше сделать за пределами вашего приложения. Вы можете настроить обратный прокси-сервер, чтобы делать такие вещи (и многое другое). Обратный прокси-сервер-это в основном веб-сервер, который находится в вашей серверной комнате и стоит между вашим веб-сервером и клиентом. См.http://en.wikipedia.org/wiki/Reverse_proxy

согласен с FigmentEngine,IHttpModule Кажется, это путь.

посмотреть в httpworkerrequest,readentitybody и GetPreloadedEntityBody.

для получения httpworkerrequest вы должны сделать это:

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

здесь inApp - это объект httpapplication.

вы можете сделать это в DelegatingHandler без использования OutputFilter упоминается в других ответах в .NET 4.5 с помощью Stream.CopyToAsync()

HttpRequest и HttpResponse pre MVC раньше имел GetInputStream() и GetOutputStream() это может быть использовано для этой цели. Не заглядывайте в эту часть в MVC, поэтому я не уверен, что они доступны, но может быть идея :)

Я знаю, что это не управляемый код, но я собираюсь предложить фильтр ISAPI. Прошло несколько лет с тех пор, как я имел "удовольствие" поддерживать свой собственный ISAPI, но из того, что я помню, вы можете получить доступ ко всем этим вещам, как до, так и после ASP.Net сделал это дело.

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

Если HTTPModule недостаточно хорош для того, что вам нужно, то я просто не думаю, что есть какой-либо управляемый способ сделать это в необходимом количестве деталей. Хотя это будет больно делать.

Comments

    Ничего не найдено.