C# 捕获DelegatingHandler中的ObjectContent序列化错误,而不读取响应
我使用中的详细信息构建了一个请求日志C# 捕获DelegatingHandler中的ObjectContent序列化错误,而不读取响应,c#,asp.net-mvc-4,asp.net-web-api,C#,Asp.net Mvc 4,Asp.net Web Api,我使用中的详细信息构建了一个请求日志DelegatingHandler 我不想阅读请求或响应机构(在一个潜在的高容量web服务上,将此工作加倍似乎很愚蠢);我只想为每个请求分配一个唯一的ID,记录URI和头;将请求ID写入另一个标头中的响应,然后记录响应(成功/失败)和发生的任何异常 protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage r
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();
}
protected override System.Threading.Tasks.Task
SendAsync(HttpRequestMessage请求,
System.Threading.CancellationToken CancellationToken)
{
DateTime receivedUTC=DateTime.UtcNow;
//我使用请求属性来持久化请求的ID
var requestID=request.GetRequestUID();
if(requestID==null)
requestID=request.SetRequestUID();
//抓取基本响应任务
var baseResult=base.sendaync(请求、取消令牌);
返回baseResult.ContinueWith(innerTask=>
{
var tcs=new TaskCompletionSource();
//注意,我已经去掉了SynchronizationContext代码
//缩短
//构造日志包
LogData toLog=新的LogData()
{
ReceivedUTC=ReceivedUTC,
请求=请求,
RequestID=RequestID,
};
//得到回应
尝试
{
SetResult(innerTask.Result);
//注意-如果此请求实际上未能通过响应序列化,
//那么上面的结果实际上看起来不错,因为
//错误还没有发生!
toLog.Response=innerTask.Result;
//这将添加一个标题
AddRequestIDToResponse(toLog.Response,requestID);
}
捕获(例外情况除外)
{
tSystexception(ex);
toLog.Ex=Ex;
}
//异步触发日志调用(代码省略,只是一些数据库工作)
Task.Factory.StartNew(()=>Log(toLog));
返回tcs.Task;
}).Unwrap();
}
这一切对于几乎所有的请求都绝对有效,除非从操作方法返回的ObjectContent
序列化到响应时发生异常
如果发生这种情况,则上述代码已经触发,并且它在上面的try/catch块中看到的响应消息显示为正常的200
,其中ObjectContent
包含最终将无法序列化的对象。这是有道理的,因为对响应体的序列化还没有发生
如何更改此代码(无需强制读取
ObjectContent
),以确保在内容格式化后,我看到的响应消息是客户端收到的实际消息?我应该考虑另一个扩展点吗?作为一个试探性的答案,直到有人(希望)证明我错了;如果不复制和粘贴大量代码,这在WebAPI管道本身中似乎几乎是不可能的
内容序列化在Web API管道的最顶端触发-在HttpControllerHandler
中,其中HttpContent
通过名为HttpControllerHandler.ConvertResponse
的内部静态方法复制到底层HttpResponse
的OutputStream
。类本身没有足够的扩展点,无法在正确的位置注入任务,从而能够在Web API管道中记录实际响应
因此,我面临着:
- 从源代码整体提升HttpControllerHandler 编码并将其导入到我的项目中(以及它使用的所有内部构件, 这不是一个好主意),然后相应地定制它
- 只要尽早强制序列化并捕获异常,我就可以记录
异常,但不是响应。
不过,将请求ID写入响应非常困难
重要信息,当出现这些格式异常时,ID不会 因为
替换了整个HttpControllerHandler
用它的错误响应进行响应 - 从Web API向上记录下一级的请求。这
不是很理想,因为我只想记录Web API请求;和
日志代码还依赖于当前请求的
HttpConfiguration对象以获取特定于请求的DI
用于解析数据库连接(它是一个多租户web)的容器 服务)
最终,唯一真正的选择是最后一个——在IIS或Asp.Net级别执行工作。因此,尽管Web API中存在所有这些可扩展性点,但实际上您无法在其中实现“完整”的日志解决方案,这确实令人失望。作为一个试探性的答案,直到有人(希望)出现并证明我错了;如果不复制和粘贴大量代码,这在WebAPI管道本身中似乎几乎是不可能的 内容序列化在Web API管道的最顶端触发-在
HttpControllerHandler
中,其中HttpContent
通过名为HttpControllerHandler.ConvertResponse
的内部静态方法复制到底层HttpResponse
的OutputStream
。类本身没有足够的扩展点,无法在正确的位置注入任务,从而能够在Web API管道中记录实际响应
因此,我面临着:
- 从源代码整体提升HttpControllerHandler 编码并将其导入到我的项目中(以及它使用的所有内部构件, 这不是一个好主意),然后相应地定制它
- 只要尽早强制序列化并捕获异常,我就可以记录 例外情况,但不包括re