-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRequestResponseLoggingMiddleware.cs
117 lines (94 loc) · 4.39 KB
/
RequestResponseLoggingMiddleware.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
namespace iSpindelMvc
{
public class RequestResponseLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly string _logPath;
public RequestResponseLoggingMiddleware(RequestDelegate next, string logPath)
{
_next = next;
_logPath = logPath;
}
public async Task Invoke(HttpContext context)
{
var sb = new StringBuilder(2000);
sb.AppendLine("Remote Address: " + context.Connection.RemoteIpAddress);
sb.AppendLine(context.Request.Method + " " + context.Request.Path);
try
{
foreach (var key in context.Request.Headers.Keys)
sb.AppendLine(key + ": " + context.Request.Headers[key]);
}
catch (Exception)
{
// ignore error
}
//First, get the incoming request
var request = await FormatRequest(context.Request);
sb.AppendLine(request);
dynamic uniqueid = Guid.NewGuid().ToString();
var logPath = string.IsNullOrEmpty(_logPath) ? "\\Logs" : _logPath;
var logfile = string.Format(logPath + "\\Request_{0}_{1}.txt", DateTime.Now.ToString("yyyyMMdd_hhmmss"), uniqueid);
await using (var f = new StreamWriter(logfile, true))
{
f.Write(sb);
}
//Copy a pointer to the original response body stream
var originalBodyStream = context.Response.Body;
//Create a new memory stream...
using (var responseBody = new MemoryStream())
{
//...and use that for the temporary response body
context.Response.Body = responseBody;
//Continue down the Middleware pipeline, eventually returning to this class
await _next(context);
//Format the response from the server
var response = await FormatResponse(context.Response);
//TODO: Save log to chosen datastore
await using (var f = new StreamWriter(logfile, true))
{
f.Write(response);
}
//Copy the contents of the new memory stream (which contains the response) to the original stream, which is then returned to the client.
await responseBody.CopyToAsync(originalBodyStream);
}
}
private async Task<string> FormatRequest(HttpRequest request)
{
var body = request.Body;
//This line allows us to set the reader for the request back at the beginning of its stream.
//request.EnableRewind();
request.EnableBuffering();
//We now need to read the request stream. First, we create a new byte[] with the same length as the request stream...
var buffer = new byte[Convert.ToInt32(request.ContentLength)];
//...Then we copy the entire request stream into the new buffer.
await request.Body.ReadAsync(buffer, 0, buffer.Length);
//We convert the byte[] into a string using UTF8 encoding...
var bodyAsText = Encoding.UTF8.GetString(buffer);
////..and finally, assign the read body back to the request body, which is allowed because of EnableRewind()
//request.Body = body;
request.Body.Position = 0;
return $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}";
}
private async Task<string> FormatResponse(HttpResponse response)
{
//We need to read the response stream from the beginning...
response.Body.Seek(0, SeekOrigin.Begin);
//...and copy it into a string
string text = await new StreamReader(response.Body).ReadToEndAsync();
//We need to reset the reader for the response so that the client can read it.
response.Body.Seek(0, SeekOrigin.Begin);
//Return the string for the response, including the status code (e.g. 200, 404, 401, etc.)
return $"{response.StatusCode}: {text}";
}
}
}