ASP.NET Web API记录入站请求内容

时间:2013-03-19 19:34:17

标签: asp.net-mvc asp.net-web-api

我正在尝试注销Web API请求内容 - 即json字符串。我实现了一个ITraceWriter类(example)并对其进行了配置,以便Web API在管道中调用它。但是,如果我读取请求。内容或复制到流中读取它不适用于导致空模型的方法。 This post稍微讨论了这个问题。任何人都有登出入站Web API请求内容的经验,并知道最佳方法是什么?

由于

更新A

我创建了一个简单的示例Web API项目来排除项目​​中的任何内容,但我仍然看到由于日志记录,模型将为null。我只是通过Fidder发布测试连续几次,看看我的模型是否为null。有了断点,它可能会起作用,这就是为什么我认为存在同步/计时问题。有关如何使其发挥作用的任何想法?

部首:

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

体:

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

以下是代码:

控制器:

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

型号:

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

记录器:

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


    }

在WebApiConfig类中连接记录器:

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

更新B

如果我将记录器更改为以下代码,添加await,async并返回结果,它似乎正在工作。看起来像我在异步代码中没有理解的东西,或者真正的时间问题或其他什么。

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


}

1 个答案:

答案 0 :(得分:5)

正如Filip在该帖子中提到的,ReadAsStringAsync或ReadAsByteArrayAsync方法在内部缓冲请求内容。这意味着即使您的传入请求的流类型是非缓冲流,您也可以安全地在消息处理程序中执行ReadAsStringAsync / ReadAsByteArrayAsync,并且还期望模型绑定正常工作。

默认情况下,请求的流在webhost和selfhost情况下都会缓冲。但是,如果您想检查是否使用ReadAsStringAsync / ReadAsByteArrayAsync并且模型biding即使在非缓冲模式下也能正常工作,您可以执行以下操作来强制执行非缓冲模式:

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

仅供参考...上述政策选择器目前仅适用于Web Host。如果您想在SelfHost中进行类似的测试,请执行以下操作:

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

上述帖子的更新B后:

你可以修改你的处理程序,如下所示:

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