1+ using Microsoft . AspNetCore . Http ;
2+ using Microsoft . Extensions . Logging ;
3+ using Microsoft . IO ;
4+ using System ;
5+ using System . Collections . Generic ;
6+ using System . IO ;
7+ using System . Linq ;
8+ using System . Threading . Tasks ;
9+
10+ namespace Xero . Demo . Api . Xero . Demo . Infrastructure . Extensions
11+ {
12+ public class RequestResponseLoggingMiddleware
13+ {
14+ private readonly RequestDelegate _next ;
15+ private readonly ILogger _logger ;
16+ private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager ;
17+
18+ public RequestResponseLoggingMiddleware ( RequestDelegate next ,
19+ ILoggerFactory loggerFactory )
20+ {
21+ _next = next ;
22+ _logger = loggerFactory
23+ . CreateLogger < RequestResponseLoggingMiddleware > ( ) ;
24+ _recyclableMemoryStreamManager = new RecyclableMemoryStreamManager ( ) ;
25+ }
26+
27+ public async Task Invoke ( HttpContext context )
28+ {
29+ await LogRequest ( context ) ;
30+ await LogResponse ( context ) ;
31+ }
32+
33+ private async Task LogRequest ( HttpContext context )
34+ {
35+ context . Request . EnableBuffering ( ) ;
36+ await using var requestStream = _recyclableMemoryStreamManager . GetStream ( ) ;
37+ await context . Request . Body . CopyToAsync ( requestStream ) ;
38+ _logger . LogInformation ( $ "Http Request Information:{ Environment . NewLine } " +
39+ $ "Schema:{ context . Request . Scheme } " +
40+ $ "Host: { context . Request . Host } " +
41+ $ "Path: { context . Request . Path } " +
42+ $ "QueryString: { context . Request . QueryString } " +
43+ $ "Request Body: { ReadStreamInChunks ( requestStream ) } ") ;
44+ context . Request . Body . Position = 0 ;
45+ }
46+
47+ private static string ReadStreamInChunks ( Stream stream )
48+ {
49+ const int readChunkBufferLength = 4096 ;
50+ stream . Seek ( 0 , SeekOrigin . Begin ) ;
51+ using var textWriter = new StringWriter ( ) ;
52+ using var reader = new StreamReader ( stream ) ;
53+ var readChunk = new char [ readChunkBufferLength ] ;
54+ int readChunkLength ;
55+ do
56+ {
57+ readChunkLength = reader . ReadBlock ( readChunk ,
58+ 0 ,
59+ readChunkBufferLength ) ;
60+ textWriter . Write ( readChunk , 0 , readChunkLength ) ;
61+ } while ( readChunkLength > 0 ) ;
62+ return textWriter . ToString ( ) ;
63+ }
64+
65+ private async Task LogResponse ( HttpContext context )
66+ {
67+ var originalBodyStream = context . Response . Body ;
68+
69+ await using var responseBody = _recyclableMemoryStreamManager . GetStream ( ) ;
70+ context . Response . Body = responseBody ;
71+
72+ await _next ( context ) ;
73+
74+ context . Response . Body . Seek ( 0 , SeekOrigin . Begin ) ;
75+ var text = await new StreamReader ( context . Response . Body ) . ReadToEndAsync ( ) ;
76+ context . Response . Body . Seek ( 0 , SeekOrigin . Begin ) ;
77+
78+ _logger . LogInformation ( $ "Http Response Information:{ Environment . NewLine } " +
79+ $ "Schema:{ context . Request . Scheme } " +
80+ $ "Host: { context . Request . Host } " +
81+ $ "Path: { context . Request . Path } " +
82+ $ "QueryString: { context . Request . QueryString } " +
83+ $ "Response Body: { text } ") ;
84+
85+ await responseBody . CopyToAsync ( originalBodyStream ) ;
86+ }
87+ }
88+ }
0 commit comments