26

I have asp.net core application. The implementation of configure method redirects the user to "Error" page when there is an exception ( in non Development environment)

However it only works if the exception occurs inside controller. If exception occurs outside of controller, for example in my custom middleware, then the user does not get redirected to error page.

How do i redirect user to "Error" page if there is an exception in the middleware.

     public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        app.UseApplicationInsightsRequestTelemetry();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseApplicationInsightsExceptionTelemetry();

        app.UseStaticFiles();
        app.UseSession();
        app.UseMyMiddleware();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }

Update 1
I updated code above with the following two lines that were missing in initial post.

        app.UseSession();
        app.UseMyMiddleware();

Also I found why app.UseExceptionHandler was not able to redirect to Error page.
When there is an exception in my middleware code, app.UseExceptionHandler("\Home\Error") is redirecting to \Home\Error as expected; but since that is a new request, my middleware was executing again and throwing exception again.
So to solve the issue i changed my middleware to execute only if context.Request.Path != "/Home/Error"

I am not sure if this is the correct way to solve this issue but its working.

public class MyMiddleWare
{
    private readonly RequestDelegate _next;
    private readonly IDomainService _domainService;

    public MyMiddleWare(RequestDelegate next, IDomainService domain)
    {
        _next = next;
        _domainService = domain;
    }

    public async Task Invoke(HttpContext context)
    {
        if (context.Request.Path != "/Home/Error")
        {
            if (context.User.Identity.IsAuthenticated && !context.Session.HasKey(SessionKeys.USERINFO))
            {           
                // this method may throw exception if domain service is down
                var userInfo = await _domainService.GetUserInformation(context.User.Name).ConfigureAwait(false);                    

                context.Session.SetUserInfo(userInfo);
            }
        }

        await _next(context);
    }
}

public static class MyMiddleWareExtensions
{
    public static IApplicationBuilder UseMyMiddleWare(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<MyMiddleWare>();
    }
 }
LP13
  • 20,711
  • 38
  • 136
  • 286
  • Where in the example is your custom middleware? I **think** `UseExeptionHandler` should be able to handle it, but your middleware needs to be registered **after** it. – Tseng Nov 15 '16 at 19:03

2 Answers2

29

You can use to handle exceptions UseExceptionHandler(), put this code in your Startup.cs.

UseExceptionHandler can be used to handle exceptions globally. You can get all the details of exception object like Stack Trace, Inner exception and others. And then you can show them on screen. Here

Here You can read more about this diagnostic middleware and find how using IExceptionFilter and by creating your own custom exception handler.

   app.UseExceptionHandler(
                options =>
                {
                    options.Run(
                        async context =>
                        {
                            context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
                            context.Response.ContentType = "text/html";
                            var ex = context.Features.Get<IExceptionHandlerFeature>();
                            if (ex != null)
                            {
                                var err = $"<h1>Error: {ex.Error.Message}</h1>{ex.Error.StackTrace}";
                                await context.Response.WriteAsync(err).ConfigureAwait(false);
                            }
                        });
                }
            );

You have to also delete default setting like UseDeveloperExceptionPage(), if you use it, it always show default error page.

   if (env.IsDevelopment())
        {
            //This line should be deleted
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }
M. Wiśnicki
  • 5,749
  • 3
  • 20
  • 27
  • i have gone through this article before. However i think the problem is whenever you set response.StatusCode to 500 the browser does not show the `html` that server sends, instead browser shows its own error page for code 500. In fiddler i see the response body has error message that server sends but browser does not show that error message. – LP13 Nov 15 '16 at 17:11
  • 2
    Just delete from startup.cs app.UseDeveloperExceptionPage(); – M. Wiśnicki Nov 15 '16 at 17:20
  • btw i have also created custom exception filter attribute that derived from `ExceptionFilterAttribute`. This takes care of all the exceptions inside controller. Im looking for exception handling in middleware. Even if i handle globally using `app.UseExceptionHandler` or create my own middleware to handle exception it does not redirect user to error view. – LP13 Nov 15 '16 at 17:22
  • 2
    @LP13 You need delete app.UseDeveloperExceptionPage(); and it will be work. – M. Wiśnicki Nov 15 '16 at 17:46
  • 1
    i dont think thats the issue as i said in my post its happening in **non Development** environment – LP13 Nov 15 '16 at 19:30
  • 1
    is it possible to use UseExceptionHandler with default error page? I want to capture error details, but keep existing behaviour and pages. – Semen Shekhovtsov Dec 08 '16 at 11:47
7

You should write your own middleware to handle custom exception handling. And make sure you add it towards the beginning (first if possible) of your middleware stack because exceptions that happen in middleware that is "earlier" in a stack will not be handled.

Example:

public class CustomExceptionMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task Invoke(HttpContext context)
    {
        try 
        {
            await _next.Invoke(context);
        } 
        catch (Exception e) 
        {
            // Handle exception
        }
    }
}
Andrius
  • 1,677
  • 2
  • 11
  • 14
  • 2
    `Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware` does exactly the same thing what you have suggested. This middleware recdirects to the supplied path incase of exception. You can see the code https://github.com/aspnet/Diagnostics/blob/dev/src/Microsoft.AspNetCore.Diagnostics/ExceptionHandler/ExceptionHandlerMiddleware.cs – LP13 Nov 15 '16 at 16:34