Hi guys,
I have an ASP.NET Core 6 Web API that I am calling 3rd party web services. I have exception middleware in order to log exceptions and return 500 responses to hide the details from the user. When this 3rd party web service returns 400 bad request, I also want to return it to the user but something in the request-response middleware makes the response 500 - Internal Server Error. I debugged and realized that after processing this line below I got Internal Server Error.
Here is the request response middleware;
Here is the exception middleware,
In conclusion, even though I get 400 error in the exception middleware, I am getting 500 Internal server error response due to the line I mentioned.
I have an ASP.NET Core 6 Web API that I am calling 3rd party web services. I have exception middleware in order to log exceptions and return 500 responses to hide the details from the user. When this 3rd party web service returns 400 bad request, I also want to return it to the user but something in the request-response middleware makes the response 500 - Internal Server Error. I debugged and realized that after processing this line below I got Internal Server Error.
// Call the next middleware in the pipeline
await _next(httpContext);
Here is the request response middleware;
C#:
public class RequestResponseMiddleware
{
private readonly RequestDelegate _next;
private readonly IConfiguration _configuration;
private readonly bool _isRequestResponseLoggingEnabled;
public RequestResponseMiddleware(RequestDelegate next, IConfiguration configuration)
{
_next = next;
_configuration = configuration;
_isRequestResponseLoggingEnabled = configuration.GetValue<bool>("EnableRequestResponseLogging", true);
}
public async Task InvokeAsync(HttpContext httpContext)
{
// Middleware is enabled only when the EnableRequestResponseLogging config value is set.
if (_isRequestResponseLoggingEnabled)
{
await using var sqlConnection =
new SqlConnection(_configuration.GetSection("ConnectionStrings:OyunPalasDbContext").Value);
await using var cmd =
new SqlCommand(
"INSERT INTO [dbo].[API_Log] ([Host],[Headers],[StatusCode],[TimeUtc],[RequestBody],[RequestedMethod],[UserHostAddress],[Useragent],[AbsoluteUri],[RequestType]) VALUES (@Host,@Headers,@StatusCode,getdate(),@RequestBody,@RequestedMethod,@UserHostAddress,@Useragent,@AbsoluteUri,@RequestType)",
sqlConnection);
sqlConnection.Open();
cmd.Parameters.AddWithValue("@Host", httpContext.Request.Host.ToString());
cmd.Parameters.AddWithValue("@StatusCode", "");
cmd.Parameters.AddWithValue("@Headers", FormatHeaders(httpContext.Request.Headers));
cmd.Parameters.AddWithValue("@RequestBody", await ReadBodyFromRequest(httpContext.Request));
cmd.Parameters.AddWithValue("@RequestedMethod", httpContext.Request.Method);
cmd.Parameters.AddWithValue("@UserHostAddress", httpContext.Request.Host.ToString());
cmd.Parameters.AddWithValue("@Useragent", httpContext.Request.Headers["User-Agent"].ToString());
cmd.Parameters.AddWithValue("@AbsoluteUri", httpContext.Request.Path.ToString());
cmd.Parameters.AddWithValue("@RequestType", "Request");
cmd.ExecuteNonQuery();
var originalResponseBody = httpContext.Response.Body;
using var newResponseBody = new MemoryStream();
httpContext.Response.Body = newResponseBody;
// Call the next middleware in the pipeline
await _next(httpContext);
newResponseBody.Seek(0, SeekOrigin.Begin);
var responseBodyText = await new StreamReader(httpContext.Response.Body).ReadToEndAsync();
await using var sqlConnectionResponse =
new SqlConnection(_configuration.GetSection("ConnectionStrings:OyunPalasDbContext").Value);
await using var cmdResponse =
new SqlCommand(
"INSERT INTO [dbo].[API_Log] ([Host],[Headers],[StatusCode],[TimeUtc],[RequestBody],[RequestedMethod],[UserHostAddress],[Useragent],[AbsoluteUri],[RequestType]) VALUES (@Host,@Headers,@StatusCode,getdate(),@RequestBody,@RequestedMethod,@UserHostAddress,@Useragent,@AbsoluteUri,@RequestType)",
sqlConnectionResponse);
sqlConnectionResponse.Open();
cmdResponse.Parameters.AddWithValue("@Host", httpContext.Request.Host.ToString());
cmdResponse.Parameters.AddWithValue("@Headers", FormatHeaders(httpContext.Response.Headers));
cmdResponse.Parameters.AddWithValue("@StatusCode", httpContext.Response.StatusCode.ToString());
cmdResponse.Parameters.AddWithValue("@RequestBody", responseBodyText);
cmdResponse.Parameters.AddWithValue("@RequestedMethod", httpContext.Request.Method);
cmdResponse.Parameters.AddWithValue("@UserHostAddress", httpContext.Request.Host.ToString());
cmdResponse.Parameters.AddWithValue("@Useragent", "");
cmdResponse.Parameters.AddWithValue("@AbsoluteUri", "");
cmdResponse.Parameters.AddWithValue("@RequestType", "Response");
cmdResponse.ExecuteNonQuery();
newResponseBody.Seek(0, SeekOrigin.Begin);
await newResponseBody.CopyToAsync(originalResponseBody);
}
else
{
await _next(httpContext);
}
}
private static async Task<string> ReadBodyFromRequest(HttpRequest request)
{
// Ensure the request's body can be read multiple times (for the next middlewares in the pipeline).
request.EnableBuffering();
using var streamReader = new StreamReader(request.Body, leaveOpen: true);
var requestBody = await streamReader.ReadToEndAsync();
// Reset the request's body stream position for next middleware in the pipeline.
request.Body.Position = 0;
return requestBody;
}
private static string FormatHeaders(IHeaderDictionary headers) => string.Join(", ", headers.Select(kvp => $"{{{kvp.Key}: {string.Join(", ", kvp.Value)}}}"));
}
Here is the exception middleware,
C#:
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
private static IConfiguration _configuration;
private static ILogger<ExceptionMiddleware> _logger;
public ExceptionMiddleware(RequestDelegate next, IConfiguration configuration, ILogger<ExceptionMiddleware> logger)
{
_next = next;
_configuration = configuration;
_logger = logger;
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
await HandleExceptionAsync(httpContext, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
try
{
_logger.LogError("Web API Exception Handler Error: " + exception);
await using var sqlConnection =
new SqlConnection(_configuration.GetSection("ConnectionStrings:OyunPalasDbContext").Value);
await using var cmd =
new SqlCommand(
"INSERT INTO [dbo].[APIError] ([Message],[RequestMethod],[RequestUri],[TimeUtc]) VALUES (@Message, @RequestMethod, @RequestUri, @TimeUtc)",
sqlConnection);
sqlConnection.Open();
cmd.Parameters.AddWithValue("@Message", exception.Message);
cmd.Parameters.AddWithValue("@TimeUtc", DateTime.Now);
cmd.Parameters.AddWithValue("@RequestUri", context.Request.Path.ToString());
cmd.Parameters.AddWithValue("@RequestMethod", context.Request.Method.ToString());
if (exception.InnerException != null)
{
cmd.Parameters.AddWithValue("@Message", exception.Message + " " + exception.InnerException);
_logger.LogError("Web API Exception Handler Inner Error: " + exception.InnerException);
}
cmd.ExecuteNonQuery();
}
catch (Exception e)
{
_logger.LogError("Web API Exception Handler insert error: " + e.Message);
}
if (exception is BadHttpRequestException)
{
//400 exception.
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
await context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = exception.Message
}.ToString());
}
else
{
//return 500 error
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
await context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = "Internal Server Error."
}.ToString());
}
}
}