Select Page

ASP.NET Core Exception Middleware

Handling errors is one of the most important aspect of any application. ASP.NET Core Exception Middleware provides an option to implement global exception handling logic.

ASP.NET Core has provided try, catch & finally block for exceptions handling. Instead of implementing these blocks in all the methods it is better to implement a global exception handler.

Also most of the time developers are so engrossed in the implementation of actual code logic that they tend to ignore or forget error handling code. This results in many runtime exceptions some of which get captured during testing and rest get reported on production. So global error handling logic will allow the application to gracefully log the error and report the user-friendly error message to users.

If you need details on how to implement logging in ASP.NET Core then you can check this article on ASP.NET Core Logging

ASP.NET Core Exception Middleware

ASP.NET Core provides a built-in middleware to implement global error handler but we can even implement our own custom exception middleware to handle exceptions globally. For further details on middleware, you can read this article on ASP.NET Core Middleware

Implementing ASP.NET Core Exception Middleware

Handling errors globally with built-in middleware

Built-in exception middleware can be configured in the configure method of a startup.cs file. As shown in the below code exception middleware and environment variable are used to redirect to /home/error page when an error occurs in the production environment & in the development environment it will display developer exception page which contains details for further analysis.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }
    //Remaining code was removed
}

I have simulated error condition to capture screenshots for developer exception page & custom error page for production Below is the screenshot of Developer exception page which obviously has more data for debugging & analysis.

ASP.NET Core Developer Exception Page

Shown below is the custom error page for production environment.

ASP.NET Core Use Exception Handler Page

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

Handling errors globally with custom exception middleware

Shown below is the code of custom exception middleware which in case of error will log the exception details and redirect the user to a global error page.

public class ExceptionMiddleware
{
    private readonly RequestDelegate _next;

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

    public async Task InvokeAsync(HttpContext httpContext)
    {
        try
        {
            await _next(httpContext);
        }
        catch (Exception ex)
        {
            //Add logging code to log exception details
            httpContext.Response.Redirect("/Home/Error");
        }
    }
}

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

This custom exception middleware will be used in the ASP.NET Core middleware pipeline as shown below in code from configure method in a startup.cs file

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseStaticFiles();

    if (!env.IsDevelopment())
    {
        app.UseExceptionMiddleware();
    }
    //Remaining code was removed
}

Summary

ASP.NET Core Exception middleware can be used to implement a global exception handling code. It is always recommended to implement a global exception handler as we can control as to what to log during exception times. This will keep the code clean & readable.

References: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-3.1

You can also check my other Article on ASP.NET Core Caching: https://procodeguide.com/programming/aspnet-core-caching/

ASP.NET Core Middleware

Middleware got introduced in ASP.NET with .NET Core. ASP.NET Core Middleware allows controlling the web application request pipeline. The middleware approach got introduced to configure the ASP.NET request pipeline.

Introduction

ASP.NET Core Middleware is a piece of code that gets executed on every request. ASP.NET Core comes with built-in middleware e.g. routing, session, MVC, etc and one can write their own custom middleware as well. Middleware forms an application pipeline to handle requests & responses and a pipeline consists of a series of middleware that gets executed in the configured order.

Request pipeline can be a combination of built-in Middleware, third party middleware, or your own custom middleware like logging middleware, exception middleware, etc.

Middleware can be directly added using request delegates that handle each HTTP request. Request delegates can be configured using Run, Use & Map methods directly in the startup.cs file in Configure Method. Alternatively, middleware can also be configured using a separate reusable class.

Based on the request type, each Middleware can decide whether to perform work on request (before & after the next component in a pipeline), do nothing, and forward the request to the next middleware or short circuit the pipeline by generating a response. When middleware short circuits pipeline its called terminal middleware.

ASP.NET Core Middleware

Implement ASP.NET Core Middleware

ASP.NET Core Middleware can be implemented using request delegates – Run, Use, Map in the startup.cs file and also by implementing custom middleware class. Both methods have been covered below.

Create a middleware with IApplicationBuilder – Run, Use and Map

Configure the HTTP pipeline using Run, Use, and Map request delegates. Request delegates can be registered as in-line anonymous methods through startup.cs in Configure Method. ASP.NET Core Request Delegates – Run, Use & Map can be used to alter request pipeline.

Run – Generates a response and terminates the pipeline i.e. no other middleware will run after this. Typically should be placed at the end of the pipeline. When a request is not passed to next delegate its called short-circuiting the request pipeline (i.e. terminal middleware)

public class Startup { public void Configure(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync(“Hello, Some Response Text!!”); }); } }

Use – Connects multiple middlewares in a pipeline. Performs tasks before and after the next delegate. Parameter next will take to next middleware in the pipeline. Conditionally you can even short-circuit the pipeline by not calling the next parameter.

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            // Code Logic that does not short cicuits pipeline
            await next.Invoke();
            // Code Logic
        });
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Some Response From next delegate in pipeline!!");
        });
    }
}

Don’t call next.Invoke() after the response has been sent to the client as any changes to the response after the response has started will result in an exception.

Map – Maps are used to reroute the request to different middleware components based on incoming requests. Map and MapWhen reroute the request pipeline based on matches of the specified request path.

public class Startup
{
    private static void HandleContact(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Contact us at [email protected]");
        });
    }
    
    private static void HandleAbout(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hi from rsmrocks");
        });
    }
    
    private static void HandleWelcome(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Welcome to our Blog");
        });
    }
    
    public void Configure(IApplicationBuilder app)
    {
        app.Map("/contact", HandleContact);
        app.Map("/about", HandleAbout);
        app.MapWhen(context => context.Request.Query.ContainsKey("welcome"), HandleWelcome);
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from delegate without Map!");
        });
    }
}

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

Create a middleware using custom middleware

Request Delegates inside startup.cs will be difficult to maintain when request pipeline complexity increases. We generally want to keep code clean and also allow middleware to handle single responsibility so middleware is implemented as standalone reusable classes. Middleware class is a class with an invoke method that contains code logic and also has a constructor that accepts a RequestDelegate.

public class MyFirstMiddleware
{
    private readonly RequestDelegate _next;
    public MyFirstMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    
    public async Task Invoke(HttpContext httpContext)
    {
        // Code Logic before next middleware
        await _next(httpContext);
        // Code Logic after next middleware
    }
}

We can directly use the above middleware class in a startup.cs but to keep it clean we can add below extension class which uses IApplicationBuilder and then use this extension in configure method in startup.cs

public static class MyFirstMiddlewareExtensions
{
    public static IApplicationBuilder UseMyFirstMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware();
    }
}

Now our configure method in startup class will look like this

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

Middleware Order

ASP.NET Core request processing pipeline consists of a series of middleware that handles the request/response one by one i.e. they are called one by one by .NET core framework. The sequence in which these middlewares are configured is called Middleware Order.

The order in which ASP.NET Core Middleware Components are added in the Configure method of the Startup class method defines the order in which middleware components are invoked for handling request as well as response.

Summary

  • ASP.NET Core Middleware is a piece of code that gets executed on every request
  • Request pipeline can be a combination of built-in Middleware, third party middleware or your own custom middleware like logging middleware, exception middleware, etc.
  • Middleware can be directly added using request delegates like Run, Use & Map in the startup.cs file in Configure Method. Alternatively, middleware can also be configured using a separate reusable class.

You can also check my another Article explaining how to analyze ASP.NET application issues – https://procodeguide.com/programming/analyze-aspnet-application-issues/

References: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.0#use-run-and-map

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.2