Pergunta

Eu estou escrevendo um serviço web (utilizando ASP.NET MVC) e para fins de apoio que gostaríamos de ser capaz de registrar as solicitações e respostas em tão perto quanto possível da matéria, o formato on-the-wire ( isto é, incluindo o método de HTTP, caminho, todos os cabeçalhos, e o corpo) numa base de dados.

O que eu não tenho certeza é como se apossar de dados da forma menos 'mutilado'. I pode voltar a constituir o que eu acredito que os olhares pedido como inspecionando todas as propriedades do objeto HttpRequest e construir uma corda com eles (e da mesma forma para a resposta), mas eu realmente gostaria de se apossar dos dados de pedido / resposta reais que é enviado no fio.

Estou feliz em utilizar qualquer mecanismo de interceptação, tais como filtros, módulos, etc., e a solução pode ser específico para IIS7. No entanto, eu prefiro mantê-lo em apenas código gerenciado.

Quaisquer recomendações?

Editar: rel I note que HttpRequest tem um SaveAs método que pode salvar a solicitação para o disco, mas isso reconstrói o pedido do estado interno usando uma carga de métodos auxiliares internos que não podem ser acessados ??publicamente (completamente por que isso não permite salvar em um fluxo Eu não sei fornecido pelo usuário). Então, ele está começando a parecer que eu vou ter que fazer o meu melhor para reconstruir o texto de pedido / resposta dos objetos ... gemido.

Editar 2: Por favor note que eu disse que o toda pedido , incluindo método, caminho, cabeçalhos etc. As respostas atuais só olhar para os fluxos do corpo que não incluem esta informações.

Editar 3: Does ninguém ler as perguntas por aqui? Cinco respostas até agora e ainda não um, até mesmo sugere uma maneira de obter toda a matéria do pedido on-the-wire. Sim, eu sei que posso capturar os fluxos de saída e os cabeçalhos e o URL e todas essas coisas a partir do objeto de solicitação. Eu já disse que na pergunta, veja:

Eu posso voltar a constituir o que eu acredito que os olhares pedido como inspecionando todas as propriedades do objeto HttpRequest e construir uma corda com eles (e da mesma forma para a resposta), mas eu realmente gostaria de se apossar do pedido real dados de resposta / que é enviado no fio.

Se você souber o completo dados brutos (incluindo cabeçalhos, url, método HTTP, etc.) simplesmente não podem ser recuperados, em seguida, que seria útil saber. Da mesma forma, se você sabe como obtê-lo todos no formato bruto (sim, eu ainda significa incluindo cabeçalhos, url, método HTTP, etc.) sem ter de reconstruí-lo, que é o que eu pedi, então, que seria muito útil. Mas me dizendo que eu possa reconstruí-lo a partir dos objetos HttpRequest / HttpResponse não é útil. Eu sei disso. Eu já disse isso.


Por favor, note: Antes que alguém começa dizendo que isso é uma má idéia, ou vai limitar a escalabilidade, etc., nós também estaremos estrangulamento implementação, entrega sequencial e anti-repetição mecanismos em um ambiente distribuído, de modo de banco de dados logging é necessária qualquer maneira. Eu não estou olhando para uma discussão sobre se esta é uma boa idéia, eu estou à procura de como isso pode ser feito.

Foi útil?

Solução 7

OK, então parece que a resposta é "não, você não pode obter os dados brutos, você tem que reconstruir o pedido / resposta das propriedades dos objectos processadas que são". Oh bem, eu fiz a coisa reconstrução.

Outras dicas

Definitivamente usar um IHttpModule e implementar os eventos BeginRequest e EndRequest.

Todos os dados "brutos" está presente entre HttpRequest e HttpResponse, ele simplesmente não está em um único formato bruto. Aqui estão as peças necessárias para construir depósitos de Fiddler de estilo (quase tão perto de HTTP cru quanto ele ganha):

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 a resposta:

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

Note que você não pode ler o fluxo de resposta então você tem que adicionar um filtro para o fluxo de saída e capturar uma cópia.

Em seu BeginRequest, você precisará adicionar um filtro de resposta:

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

filter loja onde você pode obtê-lo no manipulador EndRequest. Sugiro na HttpContext.Items. Não pode, em seguida, obter os dados de resposta completa em filter.ReadStream().

Em seguida, implemente OutputFilterStream usando o padrão Decorator como um wrapper em torno de um stream:

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

O seguinte método de extensão em HttpRequest irá criar uma cadeia que pode ser colado em violinista e reproduzidos.

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

Você pode usar a variável de servidor ALL_RAW para obter os cabeçalhos HTTP originais enviados com o pedido, então você pode obter o InputStream como de costume:

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

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

Bem, eu estou trabalhando em um projeto e fez, talvez não muito profundo, um registo utilizando os parâmetros de solicitação:

Dê uma olhada:

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

Você pode decorar sua classe controladores para registrá-lo inteiramente:

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

ou faça login apenas alguns métodos de ação individual

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

Você tem algum motivo você precisa para mantê-lo em código gerenciado?

É importante mencionar que você pode ativar Falha Traço logging no IIS7 se você não faz como re-inventar a roda. Isso registra cabeçalhos, o pedido e corpo da resposta, bem como muitas outras coisas.

Falha registro de rastreamento

Eu fui com a abordagem de McKAMEY. Aqui está um módulo I escreveu que irá ajudar a começar e espero que poupar algum tempo. Você precisa ligar o Logger, obviamente, com algo que funciona para você:

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

Se para uso ocasional, para obter em torno de um canto apertado, como sobre algo bruto como abaixo?

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

Você pode fazer isso em um DelegatingHandler sem usar o OutputFilter mencionado em outras respostas em .NET 4.5 usando o Stream.CopyToAsync() função.

Eu não tenho certeza sobre os detalhes, mas não acionar todas as coisas ruins que acontecem quando você tenta ler diretamente o fluxo de resposta.

Exemplo:

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

Eu sei que não é o código gerenciado, mas eu vou sugerir um filtro ISAPI. Tem sido um par de anos desde que eu tive o "prazer" de manter meu próprio ISAPI mas pelo que eu me lembro de você pode ter acesso a todo este material, tanto antes como depois ASP.Net tem feito isso de coisa.

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

Se um HTTPModule não é bom o suficiente para o que você precisa, então eu só não acho que há alguma maneira conseguiu de fazer isso na quantidade necessária de detalhes. Vai ser uma dor para fazer ainda.

Eu concordo com os outros, usar um IHttpModule. Dê uma olhada na resposta a esta pergunta, o que faz quase a mesma coisa que você está pedindo. Ele registra o pedido e resposta, mas sem cabeçalhos.

Como rastrear solicitações ScriptService WebService?

Pode ser melhor fazer isso do lado de fora de sua aplicação. Você pode configurar um proxy reverso para fazer coisas como esta (e muito mais). Um proxy reverso é basicamente um servidor web que se senta em sua sala de servidores, e fica entre o seu servidor web (s) eo cliente. Consulte http://en.wikipedia.org/wiki/Reverse_proxy

Concordo com FigmentEngine, IHttpModule parece ser o caminho a percorrer.

Olhe em httpworkerrequest, readentitybody e GetPreloadedEntityBody.

Para obter o httpworkerrequest que você precisa fazer isso:

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

onde inApp é o objeto HttpApplication.

HttpRequest e HttpResponse pré MVC costumava ter um GetInputStream() e GetOutputStream() que poderia ser usado para essa finalidade. Já não olhar para aqueles parte MVC assim que eu não estou certo que eles são availavle mas pode ser uma idéia:)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top