在没有读取响应的情况下捕获DelegatingHandler中的ObjectContent序列化错误

时间:2012-08-31 09:58:31

标签: c# asp.net-mvc-4 asp.net-web-api

我使用this blog post中的详细信息构建了一个记录DelegatingHandler的请求。

我不想阅读请求或响应机构(这项工作在可能的高容量Web服务上看起来很愚蠢);我想要做的就是为每个请求分配一个唯一的ID,记录URI和标题;将请求ID写入另一个头中的响应,然后记录响应(成功/失败)和发生的任何异常。

protected override System.Threading.Tasks.Task<HttpResponseMessage> 
  SendAsync(HttpRequestMessage request, 
  System.Threading.CancellationToken cancellationToken)
{
  DateTime receivedUTC = DateTime.UtcNow;

  //I use the Request properties to persist a Request's ID
  var requestID = request.GetRequestUID();
  if (requestID == null)
    requestID = request.SetRequestUID();

  //grab the base response task
  var baseResult = base.SendAsync(request, cancellationToken);

  return baseResult.ContinueWith(innerTask =>
  {
    var tcs = new TaskCompletionSource<HttpResponseMessage>();

    //note I've got rid of SynchronizationContext code out of this
    //to keep it shorter

    //construct the log packet
    LogData toLog = new LogData()
        {
          ReceivedUTC = receivedUTC,
          Request = request,
          RequestID = requestID,
        };

    //get the response
    try
    {
      tcs.SetResult(innerTask.Result);
      //NOTE - If this request actually fails response serialization,
      //       Then the above result will actually look fine, because
      //       The error hasn't occurred yet!
      toLog.Response = innerTask.Result;
      //this adds a header
      AddRequestIDToResponse(toLog.Response, requestID);
    }
    catch (Exception ex)
    {
      tcs.TrySetException(ex);
      toLog.Ex = ex;
    }

    //fire the logging call asynchronously (code elided, just some DB work)
    Task.Factory.StartNew(() => Log(toLog));
    return tcs.Task;
  }).Unwrap();
}

除了从动作方法返回的ObjectContent被序列化为响应时发生异常时,这几乎对所有请求都完全正常。

如果发生这种情况,上面的代码已经触发了,它在上面的try / catch块中看到的响应消息显示为普通200ObjectContent包含最终会失败的对象序列化。这确实有意义 - 因为尚未发生对响应主体的序列化。

如何更改此代码(不强制读取ObjectContent)以确保我看到的响应消息是 实际 消息客户端收到内容格式化后?我应该看一个不同的可扩展性点吗?

1 个答案:

答案 0 :(得分:3)

作为一个试探性的答案,直到有人(希望)出现并证明我错了;在没有复制和粘贴大量代码的情况下,Web API管道本身似乎无法实现这一点。

内容序列化在Web API管道的最顶层触发 - 在HttpControllerHandler中,HttpContent被复制到基础HttpResponse的{​​{1}}在一个名为OutputStream的内部静态方法中。类本身没有足够的可扩展点,无法在正确的位置注入任务,以便能够在Web API管道中记录实际的响应。

所以我面对的是:

  • 从源头完整地提升HttpControllerHandler.ConvertResponse 代码并将其导入我的项目(以及它使用的所有内部, 这不是一个好主意,然后相应地定制它。

  • 只是提前强制序列化并捕获异常 - 我可以记录 例外但不是回应。有一个小问题 尽管如此 - 将请求ID写入响应是非常的 很重要,当发生这些格式化异常时,ID不会 写作是因为HttpControllerHandler取代了整个 响应它的错误响应!

  • 从Web API向上一级记录请求。这
    并不理想,因为我只想记录 Web API 请求;和
    日志代码也依赖于当前请求的
    HttpConfiguration对象以获取特定于请求的DI
    用于解析数据库连接的容器(它是一个多租户Web 服务)。

最终唯一真正的选择是最后一个 - 在IIS或Asp.Net级别完成工作。因此,尽管Web API中存在所有这些可扩展性点 - 您实际上无法在其中实现“完整”的日志记录解决方案 - 这真的令人失望。