【问题标题】:ASP.NET Web API Logging Inbound Request ContentASP.NET Web API 记录入站请求内容
【发布时间】:2013-03-08 16:14:53
【问题描述】:

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

谢谢

更新 A

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

标题:

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

如果我将记录器更改为以下代码,添加等待、异步并返回结果,它似乎现在正在工作。似乎我在异步代码中不理解的东西,或者真正的时间问题或其他东西。

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


}

【问题讨论】:

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


    【解决方案1】:

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

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

    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 主机。如果您想在 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;
        }
    }
    

    【讨论】:

    • 菲利普的评论让我很震惊。我确实使用了 ReadAsStringAsync 并且我的模型将为空。这是我在 ITraceWriter 实现中使用的基本代码: request.Content.ReadAsStringAsync().ContinueWith(s => { string requestText = s.Result; Logger.Log(requestText); });
    • 我无法重现您提到的问题。例如(不是最好的方法),我在 Mike Wasson 的示例中 SimpleTracer 的 WriteTrace 方法中有以下代码: if (rec.Request != null){ Console.WriteLine(rec.Category + ", " + rec.Request.Content.ReadAsStringAsync().Result); }
    • 感谢您尝试复制。试图弄清楚有什么不同。我正在为本地开发人员使用 MVC 4 并在 IIS Express 中运行。也许 IIS Express 是不同的。我正在尝试不同的东西,并会回复。
    • 我尝试遵循这个:weblogs.asp.net/pglavich/archive/2012/02/26/…,就像 Filip 的示例一样使用 DelegatingHandler。我不知道这是时间问题还是什么,但是从测试工具中对方法的一些调用似乎可以进行多次调用,而有些似乎最终以空模型结束。另外,附注:尝试直接遵循 Filip 的非常简单的示例,他使用并等待,这意味着该方法具有异步功能,因此它迫使我返回 response.Result,而不仅仅是响应。我可能只需要创建一个更简单的示例进行测试。
    • 我用更多细节和示例更新了这个问题。如果您有机会并且可以尝试使用示例代码进行复制或提供任何进一步的见解,我将不胜感激。
    猜你喜欢
    • 1970-01-01
    • 2016-09-25
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多