我使用此博客文章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 块中看到的响应消息显示为正常200
,其中ObjectContent
包含最终将无法序列化的对象。这确实是有道理的——因为尚未发生对响应主体的序列化。
如何更改此代码(不强制读取ObjectContent
)以确保我看到的响应消息是客户端在内容格式化后收到的实际消息?我应该看一个不同的扩展点吗?