Ryan
Ryan

Reputation: 682

Global error handler for ASP.NET Core application

I am trying to configure a global error handler for my web app.

Configuration In the Startup.cs Configure method:

if (env.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseMiddleware<ErrorHandlerMiddleware>();
}

Middleware

public class ErrorHandlerMiddleware
{
    // RequestDelegate denotes a HTTP Request completion.
    private readonly RequestDelegate _next;
    public ErrorHandlerMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception error)
        {
            var response = context.Response;
            response.ContentType = "application/json";
            // Creates an ErrorReponse Model out of the error message using the Fail method
            var responseModel = ErrorResponse<string>.Fail(error.Message);
            switch (error)
            {
                case ExceptionService e:
                    // custom application error
                    response.StatusCode = (int)HttpStatusCode.BadRequest;
                    break;
                case KeyNotFoundException e:
                    // not found error
                    response.StatusCode = (int)HttpStatusCode.NotFound;
                    break;
                default:
                    // unhandled error
                    response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    break;
            }
            var result = JsonSerializer.Serialize(responseModel);

            // Create a new ITempDataDictionary object
            var tempData = context.RequestServices.GetRequiredService<ITempDataProvider>().LoadTempData(context);

            // Add key-value pairs to the temp data dictionary
            //tempData["ErrorMessage"] = "An unexpected error occurred."; 
            
            tempData["Error_Message"] = error.Message.ToString();
            tempData["Error_StackTrace"] = error.StackTrace.ToString();
            tempData["Error_Source"] = error.Source.ToString();
            tempData["Error_TargetSite"] = error.TargetSite.ToString();

            if (error.InnerException != null)
                tempData["Error_InnerException"] = error.InnerException.ToString();

            // Save the temp data dictionary to the HttpContext
            context.RequestServices.GetRequiredService<ITempDataProvider>().SaveTempData(context, tempData);
            //context.Response.Redirect(result);
            context.Response.Redirect($"/Error?code={response.StatusCode}");
        }
    }

}

Error Controller

public class ErrorController : Controller
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly IEmailService _emailService;
    private readonly AppConfiguration _appConfig;
    private readonly AppMailSettings _mailSettings;

    public ErrorController(IUnitOfWork unitOfWork,
                            UserManager<ApplicationUser> userManager, 
                            IEmailService emailService,
                            IOptions<AppConfiguration> appConfig,
                            IOptions<AppMailSettings> mailSettings)
    {
        _unitOfWork = unitOfWork;
        _userManager = userManager;
        _emailService = emailService;
        _appConfig = appConfig.Value;
        _mailSettings = mailSettings.Value;
    }

    [AllowAnonymous]
    [Route("Error")]
    public async Task<IActionResult> Error(string code)
    {
        var Error_Message = TempData["Error_Message"];
        var Error_StackTrace = TempData["Error_StackTrace"];
        var Error_Source = TempData["Error_Source"] ;
        var Error_TargetSite = TempData["Error_TargetSite"];

        ErrorViewModel model = new ErrorViewModel();

        model.ErrorMessage = "Sorry, an application error occured. Our support team has been notified.";

        model.ErrorDateTime = DateTime.Now.ToString();
        model.StackTrace = Error_StackTrace.ToString();
        model.InnerException = TempData["Error_Message"].ToString();
        model.Source = Error_Source.ToString();
        model.TargetSite = Error_TargetSite.ToString();

        var user = await _userManager.GetUserAsync(HttpContext.User);
        var baseUrl = "http://localhost:44350";

        if (user != null)
        {
            model.UserId = user.Id;
            model.UserName = user.UserName;
            model.Role = user.UserType;
            
            var mailRequest = new MailRequest();
            //mailRequest.ToAddresses = new List<string> { "[email protected]", _mailSettings.ErrorEmailAddress };
            mailRequest.ToAddresses = new List<string> { "[email protected]", _mailSettings.ErrorEmailAddress };
            mailRequest.Subject = "WFC Plus Error Report";
            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"User: {model.UserName}");
            // other details in string builder
            sb.AppendLine($"Is Development: {model.IsDevelopment}");
            mailRequest.Body = sb.ToString();
            await _emailService.SendEmail(mailRequest);

        }
        return View(model);
    }
}

Problem Invoke method of ErrorHandlerMiddleware is getting hit on startup so I know that the environment setting is correct, but exceptions that I am triggering in testing are not getting handled by the middleware.

I am not sure how to proceed.

Upvotes: 0

Views: 71

Answers (0)

Related Questions