使用 OWIN 中间件或委托 MessgaeHandler 记录 api requests/responses?
Use OWIN middleware or a delegating MessgaeHandler to log api requests/responses?
在我的旧非 OWIN API 中,我使用 MessageHanlder 来记录所有 HttpRequest 和 HttpResponses。这是消息处理程序:
public class MessageHandler : DelegatingHandler
{
private static readonly ILog RequestApiLogger = LogManager.GetLogger("RequestApiPacketLogger");
private static readonly ILog ResponseApiLogger = LogManager.GetLogger("ResponseApiPacketLogger");
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var correlationId = Guid.NewGuid();
RequestApiLogger.LogHttpRequest(request, correlationId);
return await base.SendAsync(request, cancellationToken).ContinueWith(
task =>
{
var response = task.Result;
response.Headers.Add("http-tracking-id", correlationId.ToString("D"));
ResponseApiLogger.LogHttpResponse(response, correlationId);
return response;
}, cancellationToken);
}
}
但是,在我的新项目中,我可以编写自定义 OWIN 中间件来使用 OwinContext 做类似的事情,如下所示:
//use an alias for the OWIN AppFunc
using AppFunc = Func<IDictionary<string, object>, Task>;
public class LoggingMiddleware
{
private readonly AppFunc _next;
public LoggingMiddleware(AppFunc next)
{
_next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
IOwinContext context = new OwinContext(environment);
// Get the identity
var identity = (context.Request.User != null && context.Request.User.Identity.IsAuthenticated)
? context.Request.User.Identity.Name
: "(anonymous)";
// Buffer the request (body is a string, we can use this to log the request later
var requestBody = new StreamReader(context.Request.Body).ReadToEnd();
var requestData = Encoding.UTF8.GetBytes(requestBody);
context.Request.Body = new MemoryStream(requestData);
var apiPacket = new ApiPacket
{
CallerIdentity = identity,
Request = requestBody,
RequestLength = context.Request.Body.Length
};
// Buffer the response
var responseBuffer = new MemoryStream();
var responseStream = context.Response.Body;
context.Response.Body = responseBuffer;
// add the "http-tracking-id" response header so the user can correlate back to this entry
var responseHeaders = (IDictionary<string, string[]>)environment["owin.ResponseHeaders"];
responseHeaders["http-tracking-id"] = new[] { apiPacket.TrackingId.ToString("d") };
await _next.Invoke(environment);
responseBuffer.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(responseBuffer);
apiPacket.Response = await reader.ReadToEndAsync();
apiPacket.ResponseLength = context.Response.ContentLength ?? 0;
WriteRequestHeaders(context.Request, apiPacket);
WriteResponseHeaders(context.Response, apiPacket);
// You need to do this so that the response we buffered is flushed out to the client application.
responseBuffer.Seek(0, SeekOrigin.Begin);
await responseBuffer.CopyToAsync(responseStream);
//TODO: persist the ApiPacket in the database
}
private static void WriteRequestHeaders(IOwinRequest request, ApiPacket packet)
{
packet.Verb = request.Method;
packet.RequestUri = request.Uri;
packet.RequestHeaders = "{\r\n" + string.Join(Environment.NewLine, request.Headers.Select(kv => "\t" + kv.Key + "=" + string.Join(",", kv.Value))) + "\r\n}";
}
private static void WriteResponseHeaders(IOwinResponse response, ApiPacket packet)
{
packet.StatusCode = response.StatusCode;
packet.ReasonPhrase = response.ReasonPhrase;
packet.ResponseHeaders = "{\r\n" + string.Join(Environment.NewLine, response.Headers.Select(kv => "\t" + kv.Key + "=" + string.Join(",", kv.Value))) + "\r\n}";
}
}
我正在使用 log4net 将信息写入 SQL2012 数据库。两种方式都实现了我的目标。但是,我正在寻找使用一种方法而不是另一种方法的理由。我应该使用自定义 OWIN 中间件还是 MessageHandler,为什么?提前致谢。
由于您已经有了 MessageHandler
实现,我建议您使用它,直到您有其他理由。
但是,在我的脑海中,将日志记录移至 OwinMiddleware
的一个正当理由是,如果您有其他 OwinMiddleware
组件需要(或将从中受益)该日志记录功能(假设您正在使用 WebApi
,其中 MessageHandlers
将 运行 在请求管道中的所有 OwinMiddleware
之后。
看来我将使用 OWIN 中间件。我发现在 MessageHandler 内部 Principal.IIdentity 尚未解决。例如,如果我在消息处理程序、API 控制器的构造函数和 API 方法中放置断点,这就是我看到的(按顺序)。
使用消息处理程序
- 在 MessageHandler > Principal.IIdentity 尚未解决。
- 在 API 控制器的构造函数中 > Principal.IIDentity 尚未解决。
- 在API控制器的GET方法中,Principal.IIdentity终于被解决了。
因此,我无法在 MessageHandler 中提取并记录授权用户的 ID。
然而,当使用 OWIN 中间件时,Principal.IIdentity 在那里解析,所以我可以在那时将 userId 写入我的日志 table。这就是我决定使用中间件的原因。
也许有人可以清楚地说明何时在 API 项目中设置 IIDentity。
在我的旧非 OWIN API 中,我使用 MessageHanlder 来记录所有 HttpRequest 和 HttpResponses。这是消息处理程序:
public class MessageHandler : DelegatingHandler
{
private static readonly ILog RequestApiLogger = LogManager.GetLogger("RequestApiPacketLogger");
private static readonly ILog ResponseApiLogger = LogManager.GetLogger("ResponseApiPacketLogger");
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var correlationId = Guid.NewGuid();
RequestApiLogger.LogHttpRequest(request, correlationId);
return await base.SendAsync(request, cancellationToken).ContinueWith(
task =>
{
var response = task.Result;
response.Headers.Add("http-tracking-id", correlationId.ToString("D"));
ResponseApiLogger.LogHttpResponse(response, correlationId);
return response;
}, cancellationToken);
}
}
但是,在我的新项目中,我可以编写自定义 OWIN 中间件来使用 OwinContext 做类似的事情,如下所示:
//use an alias for the OWIN AppFunc
using AppFunc = Func<IDictionary<string, object>, Task>;
public class LoggingMiddleware
{
private readonly AppFunc _next;
public LoggingMiddleware(AppFunc next)
{
_next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
IOwinContext context = new OwinContext(environment);
// Get the identity
var identity = (context.Request.User != null && context.Request.User.Identity.IsAuthenticated)
? context.Request.User.Identity.Name
: "(anonymous)";
// Buffer the request (body is a string, we can use this to log the request later
var requestBody = new StreamReader(context.Request.Body).ReadToEnd();
var requestData = Encoding.UTF8.GetBytes(requestBody);
context.Request.Body = new MemoryStream(requestData);
var apiPacket = new ApiPacket
{
CallerIdentity = identity,
Request = requestBody,
RequestLength = context.Request.Body.Length
};
// Buffer the response
var responseBuffer = new MemoryStream();
var responseStream = context.Response.Body;
context.Response.Body = responseBuffer;
// add the "http-tracking-id" response header so the user can correlate back to this entry
var responseHeaders = (IDictionary<string, string[]>)environment["owin.ResponseHeaders"];
responseHeaders["http-tracking-id"] = new[] { apiPacket.TrackingId.ToString("d") };
await _next.Invoke(environment);
responseBuffer.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(responseBuffer);
apiPacket.Response = await reader.ReadToEndAsync();
apiPacket.ResponseLength = context.Response.ContentLength ?? 0;
WriteRequestHeaders(context.Request, apiPacket);
WriteResponseHeaders(context.Response, apiPacket);
// You need to do this so that the response we buffered is flushed out to the client application.
responseBuffer.Seek(0, SeekOrigin.Begin);
await responseBuffer.CopyToAsync(responseStream);
//TODO: persist the ApiPacket in the database
}
private static void WriteRequestHeaders(IOwinRequest request, ApiPacket packet)
{
packet.Verb = request.Method;
packet.RequestUri = request.Uri;
packet.RequestHeaders = "{\r\n" + string.Join(Environment.NewLine, request.Headers.Select(kv => "\t" + kv.Key + "=" + string.Join(",", kv.Value))) + "\r\n}";
}
private static void WriteResponseHeaders(IOwinResponse response, ApiPacket packet)
{
packet.StatusCode = response.StatusCode;
packet.ReasonPhrase = response.ReasonPhrase;
packet.ResponseHeaders = "{\r\n" + string.Join(Environment.NewLine, response.Headers.Select(kv => "\t" + kv.Key + "=" + string.Join(",", kv.Value))) + "\r\n}";
}
}
我正在使用 log4net 将信息写入 SQL2012 数据库。两种方式都实现了我的目标。但是,我正在寻找使用一种方法而不是另一种方法的理由。我应该使用自定义 OWIN 中间件还是 MessageHandler,为什么?提前致谢。
由于您已经有了 MessageHandler
实现,我建议您使用它,直到您有其他理由。
但是,在我的脑海中,将日志记录移至 OwinMiddleware
的一个正当理由是,如果您有其他 OwinMiddleware
组件需要(或将从中受益)该日志记录功能(假设您正在使用 WebApi
,其中 MessageHandlers
将 运行 在请求管道中的所有 OwinMiddleware
之后。
看来我将使用 OWIN 中间件。我发现在 MessageHandler 内部 Principal.IIdentity 尚未解决。例如,如果我在消息处理程序、API 控制器的构造函数和 API 方法中放置断点,这就是我看到的(按顺序)。
使用消息处理程序
- 在 MessageHandler > Principal.IIdentity 尚未解决。
- 在 API 控制器的构造函数中 > Principal.IIDentity 尚未解决。
- 在API控制器的GET方法中,Principal.IIdentity终于被解决了。
因此,我无法在 MessageHandler 中提取并记录授权用户的 ID。
然而,当使用 OWIN 中间件时,Principal.IIdentity 在那里解析,所以我可以在那时将 userId 写入我的日志 table。这就是我决定使用中间件的原因。
也许有人可以清楚地说明何时在 API 项目中设置 IIDentity。