Pregunta

Estoy escribiendo un servicio web (usando ASP.NET MVC) y para fines de soporte, nos gustaría poder registrar las solicitudes y la respuesta lo más cerca posible al formato sin formato en línea ( es decir, incluir el método HTTP, la ruta, todos los encabezados y el cuerpo) en una base de datos.

De lo que no estoy seguro es de cómo obtener estos datos de la manera menos 'destrozada'. Puedo reconstituir el aspecto que creo que tiene la solicitud al inspeccionar todas las propiedades del objeto HttpRequest y construir una cadena a partir de ellos (y de manera similar para la respuesta), pero realmente me gustaría obtener de los datos reales de solicitud / respuesta que se envían por cable.

Me complace utilizar cualquier mecanismo de intercepción, como filtros, módulos, etc., y la solución puede ser específica para IIS7. Sin embargo, preferiría mantenerlo solo en código administrado.

¿Alguna recomendación?

Editar: Observo que HttpRequest tiene un SaveAs método que puede guardar la solicitud en el disco pero esto reconstruye la solicitud desde el estado interno utilizando una carga de métodos internos que no pueden se accede públicamente (no sé por qué esto no permite guardar en una secuencia proporcionada por el usuario). Así que parece que tendré que hacer todo lo posible para reconstruir el texto de solicitud / respuesta de los objetos ... gemido.

Edición 2: Tenga en cuenta que dije la solicitud completa incluyendo método, ruta, encabezados, etc. Las respuestas actuales solo miran las secuencias del cuerpo que no incluyen esto información.

Edición 3: ¿Nadie lee las preguntas por aquí? Cinco respuestas hasta el momento y, sin embargo, ninguna sugiere siquiera una forma de obtener toda la solicitud cruda en el cable. Sí, sé que puedo capturar las secuencias de salida y los encabezados y la URL y todas esas cosas del objeto de solicitud. Ya dije eso en la pregunta, ver:

  

Puedo reconstituir el aspecto que creo que tiene la solicitud al inspeccionar todas las propiedades del objeto HttpRequest y construir una cadena a partir de ellas (y de manera similar para la respuesta), pero realmente me gustaría obtener la solicitud real / datos de respuesta que se envían por cable.

Si sabe que los datos sin procesar completos (incluidos encabezados, url, método http, etc.) simplemente no se pueden recuperar, entonces sería útil saberlo. Del mismo modo, si sabe cómo obtenerlo todo en formato sin formato (sí, me refiero a incluir encabezados, url, método http, etc.) sin tener que reconstruirlo, que es lo que pedí, entonces sería muy útil. Pero decirme que puedo reconstruirlo a partir de los objetos HttpRequest / HttpResponse no es útil. Yo sé eso. Ya lo dije.


Tenga en cuenta: antes de que alguien comience a decir que es una mala idea o que limitará la escalabilidad, etc., también implementaremos mecanismos de aceleración, entrega secuencial y antirrepetición en un entorno distribuido, por lo que la base de datos el registro se requiere de todos modos. No estoy buscando una discusión sobre si esta es una buena idea, estoy buscando cómo se puede hacer.

¿Fue útil?

Solución 7

OK, entonces parece que la respuesta es "no, no puede obtener los datos sin procesar, debe reconstruir la solicitud / respuesta a partir de las propiedades de los objetos analizados". Oh, bueno, he hecho lo de la reconstrucción.

Otros consejos

Definitivamente use un IHttpModule e implemente los eventos BeginRequest y EndRequest .

Todas las "primas" los datos están presentes entre HttpRequest y HttpResponse , simplemente no están en un solo formato sin formato. Estas son las partes necesarias para crear volcados de estilo Fiddler (lo más cerca posible de HTTP sin procesar):

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

Para la respuesta:

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

Tenga en cuenta que no puede leer la secuencia de respuesta , por lo que debe agregar un filtro a la secuencia de salida y capturar una copia.

En su BeginRequest , deberá agregar un filtro de respuesta:

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

Almacenar filter donde puede acceder a él en el controlador EndRequest . Sugiero en HttpContext.Items . Luego puede obtener los datos de respuesta completa en filter.ReadStream () .

Luego implemente OutputFilterStream usando el patrón Decorator como envoltorio alrededor de una secuencia:

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

El siguiente método de extensión en HttpRequest creará una cadena que puede pegarse en el violinista y reproducirse.

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

Puede usar la variable de servidor ALL_RAW para obtener los encabezados HTTP originales enviados con la solicitud, luego puede obtener InputStream como de costumbre:

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

check out: http://msdn.microsoft .com / es-es / biblioteca / ms524602% 28VS.90% 29.aspx

Bueno, estoy trabajando en un proyecto e hice, tal vez no demasiado profundo, un registro utilizando los parámetros de solicitud:

Echa un vistazo:

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

Puede decorar su clase de controladores para registrarla por completo:

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

o registre solo algunos métodos de acción individuales

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

¿Alguna razón para mantenerlo en el código administrado?

Vale la pena mencionar que puede habilitar Registro de seguimiento fallido en IIS7 si no le gusta reinventar la rueda. Esto registra los encabezados, el cuerpo de solicitud y respuesta, así como muchas otras cosas.

Registro de seguimiento fallido

Fui con el enfoque de McKAMEY. Aquí hay un módulo que escribí que lo ayudará a comenzar y, con suerte, le ahorrará algo de tiempo. Obviamente, deberá conectar el registrador con algo que funcione para usted:

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

use un 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));
        }
    }

si es para uso ocasional, para moverse en una esquina cerrada, ¿qué tal algo crudo como a continuación?

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

Puede lograr esto en un DelegatingHandler sin usar el OutputFilter mencionado en otras respuestas en .NET 4.5 usando el Stream.CopyToAsync () función.

No estoy seguro de los detalles, pero no desencadena todas las cosas malas que suceden cuando intentas leer directamente el flujo de respuesta.

Ejemplo:

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

Sé que no es código administrado, pero voy a sugerir un filtro ISAPI. Han pasado un par de años desde que tuve el "placer". de mantener mi propio ISAPI, pero por lo que recuerdo, puede obtener acceso a todo esto, tanto antes como después de que ASP.Net lo haya hecho.

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

Si un HTTPModule no es lo suficientemente bueno para lo que necesita, entonces no creo que haya una forma administrada de hacerlo con la cantidad de detalles requerida. Sin embargo, será un dolor.

Estoy de acuerdo con los demás, use un IHttpModule. Eche un vistazo a la respuesta a esta pregunta, que hace casi lo mismo que está preguntando. Registra la solicitud y la respuesta, pero sin encabezados.

¿Cómo rastrear las solicitudes de ScriptService WebService?

Puede ser mejor hacer esto fuera de su aplicación. Puede configurar un proxy inverso para hacer cosas como esta (y mucho más). Un proxy inverso es básicamente un servidor web que se encuentra en la sala de servidores y se interpone entre su (s) servidor (es) web y el cliente. Ver http://en.wikipedia.org/wiki/Reverse_proxy

De acuerdo con FigmentEngine, IHttpModule parece ser el camino a seguir.

Examine httpworkerrequest , readentitybody y GetPreloadedEntityBody .

Para obtener la httpworkerrequest necesita hacer esto:

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

donde inApp es el objeto httpapplication.

HttpRequest y HttpResponse pre MVC solía tener un GetInputStream () y GetOutputStream () que podrían ser utilizado para ese fin. No he examinado esas partes en MVC, así que no estoy seguro de que estén disponibles, pero podría ser una idea :)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top