API da Web do ASP.NET Registrando o conteúdo da solicitação de entrada

9

Estou tentando fazer o logout do Web Request Request Content, ou seja, a string json. Implementei uma classe ITraceWriter ( example ) e configurou-o para que a API da Web o chame no pipeline. Mas se eu ler o request.Content ou copiar para um fluxo para lê-lo não está disponível para o método, resultando em um modelo nulo. Esta postagem fala sobre essa questão um pouco. Alguém tem experiência em desconectar conteúdo de solicitação de API da Web de entrada e saber qual é a melhor abordagem?

Obrigado

Atualizar A

Eu criei um projeto de API da Web de amostra simples para excluir qualquer coisa do meu projeto e ainda vejo que o modelo será nulo devido ao registro em log. Eu simplesmente testo algumas vezes seguidas postando via Fidder e vejo meu modelo em null. Com pontos de interrupção no lugar, pode funcionar, e é por isso que acho que há um problema de sincronização / tempo. Alguma idéia de como fazer isso funcionar?

Cabeçalho:

User-Agent: Fiddler
Host: localhost:56824
Content-Type: application/json
Content-Length: 22

Corpo:

{
"A":1,"B":"test"
}

Este é o código:

Controlador:

public class ValuesController : ApiController
{
    [HttpPost]
    public void Post(ValuesModel model)
    {
        if (model == null)
        {
            Debug.WriteLine("model was null!");
        }
        else
        {
            Debug.WriteLine("model was NOT null!");
        }
    }
}

Modelo:

public class ValuesModel
{
    public int A { get; set; }
    public string B { get; set; }
}

Logger:

public class APITraceLogger : DelegatingHandler
    {
        protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            if (request.Content != null)
            {
                // This can cause model to be null
                request.Content.ReadAsStringAsync().ContinueWith(s =>
                {
                    string requestText = s.Result;
                    Debug.WriteLine(requestText);
                });

                // and so can this
                //request.Content.ReadAsByteArrayAsync()
                //    .ContinueWith((task) =>
                //    {
                //        string requestText = System.Text.UTF8Encoding.UTF8.GetString(task.Result);
                //        Debug.WriteLine(requestText);
                //    });
            }
            // Execute the request, this does not block
            var response = base.SendAsync(request, cancellationToken);

            // TODO:
            // Once the response is processed asynchronously, log the response data
            // to the database


            return response;
        }


    }

Ligando logger na classe WebApiConfig:

config.MessageHandlers.Add(new APITraceLogger());

Atualização B

Parece que agora está funcionando se eu alterar o registrador para o código a seguir, adicionando o await, async e retornando o resultado. Parece algo que eu não estou entendendo no código assíncrono ou realmente um problema de tempo ou algo assim.

public class APITraceLogger : DelegatingHandler
{
    protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        if (request.Content != null)
        {

            // This does seem to work - is it because it is synchronous?  Is this a potential problem?
            var requestText = await request.Content.ReadAsStringAsync();
            Debug.WriteLine(requestText);
        }
        // Execute the request, this does not block
        var response = base.SendAsync(request, cancellationToken);

        // TODO:
        // Once the response is processed asynchronously, log the response data
        // to the database


        return response.Result;
    }


}
    
por Bryan 19.03.2013 в 20:34
fonte

1 resposta

5

Como Filip menciona, os métodos ReadAsStringAsync ou ReadAsByteArrayAsync postam em buffer o conteúdo da solicitação internamente. Isso significa que, mesmo que o tipo de fluxo da sua solicitação recebida seja um fluxo sem buffer, você pode fazer um ReadAsStringAsync / ReadAsByteArrayAsync em um manipulador de mensagens, por exemplo, e também esperar que a ligação do modelo funcione bem.

Por padrão, o fluxo de uma solicitação é armazenado em buffer nos casos de host da web e selfhost. Mas se você quiser verificar se o uso de ReadAsStringAsync / ReadAsByteArrayAsync e o modelo biding funcionam bem, mesmo no modo não-buffer, você pode fazer o seguinte para forçar o modo não-buffer:

public class CustomBufferPolicySelector : WebHostBufferPolicySelector
{
    public override bool UseBufferedInputStream(object hostContext)
    {
        //NOTE: by default, the request stream is always in buffered mode.
        //return base.UseBufferedInputStream(hostContext);

        return false;
    }
}

config.Services.Replace(typeof(IHostBufferPolicySelector), new CustomBufferPolicySelector());

Apenas FYI ... o seletor de políticas acima funciona apenas para o Web Host atualmente. Se você gostaria de fazer um teste semelhante no SelfHost, faça o seguinte:

//NOTE: by default, the transfer mode is TransferMode.Buffered
config.TransferMode = System.ServiceModel.TransferMode.StreamedRequest;

Após a atualização B da postagem acima:

Você pode modificar seu manipulador como abaixo:

public class LoggingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request.Content != null)
        {
            string requestContent = await request.Content.ReadAsStringAsync();
        }

        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

        if (response.Content != null)
        {
            string responseContent = await response.Content.ReadAsStringAsync();
        }

        return response;
    }
}
    
por Kiran Challa 19.03.2013 / 21:31
fonte