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

10

我正在尝试注销Web API请求内容,即JSON字符串。我实现了一个ITraceWriter类(示例)并进行了配置,以便在管道中调用Web API。但是,如果我读取request.Content或将其复制到流中以读取它,则该内容对于方法不可用,导致模型为空值。这篇文章稍微谈到了这个问题。有没有人有记录入站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

如果我更改日志记录器的代码,添加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个回答

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

我被Filip的评论搞糊涂了。我确实使用了ReadAsStringAsync,但我的模型为空。这是我在ITraceWriter实现中使用的基本代码:request.Content.ReadAsStringAsync().ContinueWith(s => { string requestText = s.Result; Logger.Log(requestText); }); - Bryan
我无法重现您提到的问题。例如(不是最好的方法),我在Mike Wasson示例中的SimpleTracer的WriteTrace方法中有以下代码:if(rec.Request!= null){Console.WriteLine(rec.Category +“,”+ rec.Request.Content.ReadAsStringAsync()。Result); } - Kiran
我尝试遵循这个链接:http://weblogs.asp.net/pglavich/archive/2012/02/26/asp-net-web-api-request-response-usage-logging.aspx,它像Filip的例子一样使用了DelegatingHandler。我不知道是时间问题还是什么原因,但是从测试工具中调用某个方法时,有些调用似乎可以正常工作,而有些则似乎会得到一个空模型。另外,顺带一提:试图直接遵循Filip的非常简单的例子,他使用了await,这意味着该方法具有异步性,因此它强制我返回response.Result,而不仅仅是response。我可能需要创建一个更简单的示例来进行测试。 - Bryan
之前你没有等待访问结果。我现在已经更新了我的帖子,使用async-await版本。你可以尝试一下,看看是否正常工作。 - Kiran
只是想说谢谢!我也遇到了同样的挫败感。可惜我在苦苦奋斗了两天后才发现这个。 - Talon
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接