Deprecated: Function create_function() is deprecated in D:\home\site\wwwroot\wp-content\plugins\wp-spamshield\wp-spamshield.php on line 2033

This is an evolution of my previous post in which I described a method for inspecting the body of a request in a middleware method and passing it out so it could be logged later if required.

The below code reads in the body of a request and then if the response indicates failure (I’m also only interested in POST requests) the body is pushed as an additional property to the Serilog log.

ExceptionHandlingMiddleware.cs

public class ExceptionHandlingMiddleware
{
	private readonly RequestDelegate _next;

	public ExceptionHandlingMiddleware(RequestDelegate next)
	{
		_next = next;
	}

	public async Task Invoke(HttpContext httpContext)
	{
		string requestContent = new StreamReader(httpContext.Request.Body).ReadToEnd();
		string requestMethod = httpContext.Request.Method;
		httpContext.Request.Body.Position = 0;

		Stream responseBody = httpContext.Response.Body;

		try
		{
			using (MemoryStream memoryStream = new MemoryStream())
			{
				httpContext.Response.Body = memoryStream;
				
				await _next(httpContext);

				int responseStatus = httpContext.Response.StatusCode;

				if (!IsSuccessStatusCode(httpContext.Response.StatusCode) && requestMethod == "POST")
				{
					// Get response body if required
					//memoryStream.Position = 0;
					//string responseContent = new StreamReader(memoryStream).ReadToEnd();

					using (LogContext.PushProperty("RequestBody", requestContent))
					{
						Log.Warning("Invalid request ({@ResponseStatusCode}): {@RequestPath}", httpContext.Response.StatusCode, httpContext.Request.Path.Value);
					}
				}

				memoryStream.Position = 0;
				await memoryStream.CopyToAsync(responseBody);
			}
		}
		finally
		{
			httpContext.Response.Body = responseBody;
		}
	}

	public bool IsSuccessStatusCode(int statusCode)
	{
		if ((statusCode >= 200) && (statusCode <= 299))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
}

// Extension method used to add the middleware to the HTTP request pipeline.
public static class ExceptionHandlingMiddlewareExtensions
{
	public static IApplicationBuilder UseExceptionHandlingMiddleware(this IApplicationBuilder builder)
	{
		return builder.UseMiddleware<ExceptionHandlingMiddleware>();
	}
}

To use the above middleware it needs to be enabled in the Configure method of your Startup.cs file. By enabling buffering on the request the request body is upgraded to a FileBufferingStream which means that it goes from being a forward only stream that doesn’t support seeking or reading the stream a second time to one that does.

Startup.cs

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
	app.Use(async (context, next) => {
		context.Request.EnableBuffering();
		await next();
	});

	app.UseMiddleware<ExceptionHandlingMiddleware>();
	
	// Your other code...
}

Leave a Reply

Your email address will not be published. Required fields are marked *