Middleware Pipeline

ASP Net Core MVC is built on top of a concept called Middleware. Middleware pipeline is a chain of modular components that provide essential building block of how your application handles HTTP request. These components are generating a response for an incoming request directly or with help of a framework such as MVC.

Features like Routing, sessions, CORS, Authentication, Caching etc are implemented as middleware.

Example of asp net core middleware pipeline


Each middleware in the pipeline is responsible for performing its own tasks independently. Anytime in the pipeline, any middleware can stop processing of Http request and return the response. A common example of this is using CORS (Cross origin resource locator) middleware to verify the request origin is authorized to use the Http service. You can add as many as middlewares into the middleware pipeline. They will be executed in the order they are defined in the pipeline.

Program and Startup classes

The program class and Startup class works together to make the middleware possible in asp.net core.

Startup

It defined a Configure() method that is can be used for Registering Middleware components. This class replaces the old Global.asax and Global.asax.cs files in Asp.net web api and Asp.Net core applications.

Program

It defines a main() method that is used as entry point to te applciation. The Program.cs is exists mainly for the decoupling of Asp.Net from System.web and IIS. It make the asp.net core applciation and Asp.net web api applications “Self-hostable”.

Following diagram explains the overview of ASP.Net core middleware pipeline

Configuring a middleware

Middlewares are configured using following three helper methods in Asp.net core.

1. Run

Is usually used for creating an in-line middleware. Inline middleware can be added directly into the “Configure” method n Startup.cs file of .Net core mvc web application. The code added in the app.Run are the inline middleware created using Run. These middleware usually does not allow chaining request to other middleware. The processing of Http request will stop with this middleware.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace MiddlewarepipelineDemo
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Message from inline middleware!");
            });
        }
    }
}

2. Use

used for running a middleware and chaining to other midleware int he pipeline.
The next parameter provided by “Use” helper method will contain the next middleware that is waiting to be triggered in the pipeline. The next.Invoke() will invoke the next middleware in pipeline.

An code that is written before next.Invoke() will run beofre triggering next middleware and the code after ,code>next.Invoke() will run after the other middleware porcessed request. A common usecase is logging, using middleware chaining we can track log the stastics after each middleware is finished processing.

Sample code for the middleware using “Use”

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace MiddlewarepipelineDemo
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.Use(async (context, next) =>
            {
                //Logic that runs before chaining to next middleware
                await context.Response.WriteAsync("Message from middleware created with Use: before chaining");

                await next.Invoke(); //Invoke next middleware in the pipeline

                //Logic that runs after request returned from other middleware
                await context.Response.WriteAsync("Message from middleware created with Use: after chaining");
            });

            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Message from inline middleware!");
            });
        }
    }
}

3. map

This helper method will conditionally send the request to other middleware. This helper method will observe the incoming Http requet URL and map and trigger specific middleware.

In the below example, when the MAP identify the incoming URL is the root URL e.g “http://localhost/”, the MiddlewareA is triggered. When the incoming URL is ends with middleware-b e.g “http://localhost/middleware-b”, MiddlewareB is triggered.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace MiddlewarepipelineDemo
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.Map("", MiddlewareA);
            app.Map("/middleware-b", MiddlewareB);
        }

        public static void MiddlewareA(IApplicationBuilder app)
        {
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Message from Middleware A");
            });
        }

        public static void MiddlewareB(IApplicationBuilder app)
        {
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Message from Middleware B");
            });
        }
    }
}