Exception handling in Dotnet Core
Exception handling in Dotnet Core
This code defines a custom exception handling middleware in
an ASP.NET Core application. The ExceptionMiddleware class is designed to
catch, log, and handle exceptions globally, ensuring that detailed error
information is captured and appropriate responses are returned to the client.
Key Features:
- Logging:
The middleware logs key details when an exception occurs, including
request method, URL, client IP address, and timestamp, which helps with
debugging and monitoring.
- Custom
Error Responses: Depending on the type of exception, it returns a
specific HTTP status code and a structured error message to the client. It
handles various exceptions like unauthorized access, bad requests, not
found resources, validation issues, and rate-limiting errors.
- Environment-Sensitive
Detail: In a development environment, the middleware includes more
detailed exception information for easier debugging, while in production,
it provides a more user-friendly error message to protect sensitive
details.
- Custom
Exception Types: It defines two custom exception types, ValidationException
and RateLimitExceededException, for more specific handling of validation
and rate-limiting errors.
Usage:
- This
middleware should be registered in the ASP.NET Core pipeline to handle any
unhandled exceptions across the application.
- It
ensures that all exceptions are logged and a consistent, structured error
response is returned to the client.
Overall, the middleware improves error handling, logging,
and response consistency, while offering the flexibility to handle different
types of errors in a meaningful way.
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Hosting; // Add this namespace for IHostEnvironment
using System.Text.Json;
using ECommerceWebAPI.Models;
namespace ECommerceWebAPI.Globals
{
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionMiddleware> _logger;
private readonly IHostEnvironment _environment; // Declare IHostEnvironment
// Inject IHostEnvironment in the constructor
public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger, IHostEnvironment environment)
{
_next = next;
_logger = logger;
_environment = environment; // Assign it to the class field
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
// Log the exception with details including request information
LogException(httpContext, ex);
// Handle the exception and return a custom response
await HandleExceptionAsync(httpContext, ex);
}
}
private void LogException(HttpContext context, Exception exception)
{
// Log details like the exception message, stack trace, request method, URL, and other useful info
_logger.LogError(
exception,
"Exception occurred while processing request {Method} {Url} from {IpAddress} at {Time}. User: {User}.",
context.Request.Method,
context.Request.Path,
context.Connection.RemoteIpAddress?.ToString(),
DateTime.UtcNow,
context.User.Identity?.Name);
}
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
// Prepare a base error response structure
var errorResponse = new ErrorResponse
{
StatusCode = context.Response.StatusCode,
ErrorCode = "UNKNOWN_ERROR", // Default error code
Message = "An unexpected error occurred.",
UserMessage = "Something went wrong. Please try again later.",
Timestamp = DateTime.UtcNow
};
// Handle specific exceptions and customize responses accordingly
switch (exception)
{
case UnauthorizedAccessException _:
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
errorResponse.ErrorCode = "UNAUTHORIZED";
errorResponse.Message = "You are not authorized to perform this action.";
errorResponse.UserMessage = "Access denied. Please check your credentials.";
break;
case KeyNotFoundException _:
case ArgumentException _:
context.Response.StatusCode = StatusCodes.Status400BadRequest;
errorResponse.ErrorCode = "BAD_REQUEST";
errorResponse.Message = "The request is invalid or contains bad data.";
errorResponse.UserMessage = "Please check your input and try again.";
break;
case NotFoundException _:
context.Response.StatusCode = StatusCodes.Status404NotFound;
errorResponse.ErrorCode = "NOT_FOUND";
errorResponse.Message = "The resource you are looking for was not found.";
errorResponse.UserMessage = "We couldn't find the requested resource.";
break;
case InvalidOperationException _:
context.Response.StatusCode = StatusCodes.Status409Conflict;
errorResponse.ErrorCode = "CONFLICT";
errorResponse.Message = "There was a conflict with the request.";
errorResponse.UserMessage = "There was an issue processing your request. Please try again.";
break;
case ValidationException _:
context.Response.StatusCode = StatusCodes.Status422UnprocessableEntity;
errorResponse.ErrorCode = "VALIDATION_ERROR";
errorResponse.Message = "The request body is invalid.";
errorResponse.UserMessage = "Check the input data and try again.";
break;
case RateLimitExceededException _:
context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
errorResponse.ErrorCode = "RATE_LIMIT_EXCEEDED";
errorResponse.Message = "You have exceeded the maximum number of requests.";
errorResponse.UserMessage = "Please try again later.";
break;
default:
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
errorResponse.ErrorCode = "INTERNAL_SERVER_ERROR";
errorResponse.Message = exception.Message;
errorResponse.UserMessage = "An internal error occurred. Please try again later.";
break;
}
// In development, include more detailed information for debugging purposes.
if (_environment.IsDevelopment()) // Use the injected IHostEnvironment here
{
errorResponse.Message = exception.ToString(); // Include full exception details in dev.
}
var responseJson = JsonSerializer.Serialize(errorResponse);
return context.Response.WriteAsync(responseJson);
}
}
// Custom exceptions
public class ValidationException : Exception { }
public class RateLimitExceededException : Exception { }
}
Comments
Post a Comment