Ich schreibe ein Stück benutzerdefinierte OWIN-Middleware, um alle HTTP-Anfragen und ihre Antworten zu protokollieren. Ich möchte diese mit einer Tracking-ID "korrelieren". Hier ist der Code:OWIN Middleware, um Anfragen und Antworten zu korrelieren und zu protokollieren?
public class PacketTrackingMiddleware
{
private readonly AppFunc _next;
public PacketTrackingMiddleware(AppFunc next)
{
_next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
IOwinContext context = new OwinContext(environment);
var request = context.Request;
var response = context.Response;
//capture details about the caller identity
var identity = (request.User != null && request.User.Identity.IsAuthenticated)
? request.User.Identity.Name
: "(anonymous)";
var apiPacket = new ApiPacket
{
CallerIdentity = identity
};
//buffer the request stream in order to intercept downstream reads
var requestBuffer = new MemoryStream();
request.Body = requestBuffer;
//buffer the response stream in order to intercept downstream writes
var responseStream = response.Body;
var responseBuffer = new MemoryStream();
response.Body = responseBuffer;
//add the "Http-Tracking-Id" response header
context.Response.OnSendingHeaders(state =>
{
var ctx = state as IOwinContext;
if (ctx == null) return;
var resp = ctx.Response;
//adding the tracking id response header so that the user
//of the API can correlate the call back to this entry
resp.Headers.Add("http-tracking-id", new[] { apiPacket.TrackingId.ToString("d") });
}, context);
//invoke the next middleware in the pipeline
await _next.Invoke(environment);
//rewind the request and response buffers to record their content
WriteRequestHeaders(request, apiPacket);
requestBuffer.Seek(0, SeekOrigin.Begin);
var requestReader = new StreamReader(requestBuffer);
apiPacket.Request = await requestReader.ReadToEndAsync();
WriteResponseHeaders(response, apiPacket);
responseBuffer.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(responseBuffer);
apiPacket.Response = await reader.ReadToEndAsync();
//write the apiPacket to the database
//await database.InsterRecordAsync(apiPacket);
System.Diagnostics.Debug.WriteLine("TrackingId: " + apiPacket.TrackingId);
//make sure the response we buffered is flushed to the client
responseBuffer.Seek(0, SeekOrigin.Begin);
await responseBuffer.CopyToAsync(responseStream);
}
private static void WriteRequestHeaders(IOwinRequest request, ApiPacket packet)
{
packet.Verb = request.Method;
packet.RequestUri = request.Uri;
packet.RequestHeaders = request.Headers;
}
private static void WriteResponseHeaders(IOwinResponse response, ApiPacket packet)
{
packet.StatusCode = response.StatusCode;
packet.ReasonPhrase = response.ReasonPhrase;
packet.ResponseHeaders = response.Headers;
}
}
ich Probleme, bin mit Zugabe des http-Tracking-ID Antwort-Header (diese Zeilen hier).
context.Response.OnSendingHeaders(state =>
{
var ctx = state as IOwinContext;
if (ctx == null) return;
var resp = ctx.Response;
resp.Headers.Add("http-tracking-id", new[] { apiPacket.TrackingId.ToString("d") });
}, context);
Wenn der Header hinzugefügt ich manchmal diesen Fehler:
HttpException was unhandled by user code.
Additional information: Server cannot append header after HTTP headers have been sent.
Edit 1: ich dies testen, indem einfach die api ausgeführt wird, die Chrom meiner http://localhost:64051/ Adresse öffnet . Wenn ich zum Beispiel nach einer tatsächlichen API (http://localhost:64051/api/Accounts/21067) suche, erhalte ich den Fehler nicht. Soll ich gerade eine 404 zurücksenden, wenn ich zum Root der Site navigiere?
Von was ich gelesen habe, kann dies passieren, wenn etwas außerhalb der OWIN-Pipeline die Header bereits gesendet hat. Vielleicht eine ältere ASPX-Seite, die die Antwort löscht und beendet. – Sneal