Introduction
Middleware is very important to understand if you are working in ASP.NET CORE Web Application because it impacts our request pipeline and helps us to achieve customized features like authenticating the request before it hits the action method, handling exceptions, routing, etc.… The role of Middleware is similar to HttpModules and HttpHandlers in Legacy .Net Framework.
Normally when we create a new ASP.Net core project then some default middlewares are used in Configure method which is required to run the application like Routing, Development Exception Page, and Authorization middlewares.
Let’s see the default middleware which is used when we create an ASP.Net Core Web API project using .Net Core Version 3.1
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
RequestDelegate
Middlewares in ASP.NET Core handles the request pipeline with the help of RequestDelegate and it’s used in the Run, Use and Map method. RequestDelegate is a delegate that accepts HttpContext as a parameter and returns a Task and below is the signature of the same.
RequestDelegate Signature
public delegate Task RequestDelegate(HttpContext context);
Understanding Run method
public static void Run(this IApplicationBuilder app, RequestDelegate handler)
Run is an extension method of IApplicationBuilder that accepts a delegate parameter of the type RequestDelegate.
Let’s check a simple middleware using the Run method which returns “Hello World” in the response.
public void Configure(IApplicationBuilder app1, IWebHostEnvironment env)
{
app1.Run(async (context1) =>
{
await context1.Response.WriteAsync("Hello World!");
});
}
In the above example, you can see we have added a middleware using the Run method which returns “Hello World!” in the response. Run is the terminal middleware delegate to the request pipeline which means we cannot call another middleware after using the Run method.
Understanding Use method
Use is another extension method of IApplicationBuilder in which we can call another middleware with the help of the next function. If we don’t use call the next parameter then the request will short-circuited and will not call the next middleware mentioned in the pipeline.
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Before Invoking from First app.Use()\n");
await next();
await context.Response.WriteAsync("After Invoking from First app.Use()\n");
});
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Before Invoke from Second app.Use()\n");
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
As we can see from the response after calling the second app.Use it doesn’t call the app.Run because of the second app.Use doesn’t call the next parameter to call the next middleware instead it short circuits the request pipeline.
Signature of Use Extension method
public static IApplicationBuilder Use(this IApplicationBuilder app, Func<HttpContext, Func<Task>, Task> middleware)
The middleware parameter handles the request and calls the next function.
Understanding Map method
The map is another extension method of IApplicationBuilder where middleware is called based on the matching of the route path.
app.Map("/route1", appMap =>
{
appMap.Run(async context =>
{
await context.Response.WriteAsync("Hello World from Route1");
});
});
app.Map("/route2", appMap => {
appMap.Run(async context =>
{
await context.Response.WriteAsync("Hello World from Route2");
});
});
As we can see above when we are hitting the route “/route1” it’s calling only the middleware mentioned with “/route1” and likewise it’s doing the same for “/route2” where it’s calling the middleware mentioned within “/route2”.
The map has various overloads available where it calls middleware based on the request starts with the given path.
Signature of Map Extension method
public static IApplicationBuilder Map(this IApplicationBuilder app, PathString pathMatch, Action<IApplicationBuilder> configuration)
Ordering in Middleware
Ordering in Middleware is important as the request will hit the middleware first which is mentioned first in Configure method. For example, ExceptionHandler middleware should always be mentioned first so that exceptions can be handled if any exceptions occurred in the preceding middleware. The following diagram mentioned in Microsoft Docs Link shows the complete request pipeline for the ASP.NET Core MVC app
Static Files Middleware is mentioned before Routing middleware because any request which wants to access static files can be served directly without being called Routing middleware.
Sample Middleware to Authenticate Request
Let’s say we want to check all the incoming requests for the “token” value in the request header therefore we will allow the request to hit the Controller endpoint only if the request contains a header named “token”. We will create a middleware to achieve the same.
public class ValidateRequestMiddleware
{
private readonly RequestDelegate _next;
public ValidateRequestMiddleware(RequestDelegate next)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
}
public Task Invoke(HttpContext httpContext)
{
if (!httpContext.Request.Headers.ContainsKey("token"))
{
httpContext.Response.StatusCode = 401;
return httpContext.Response.WriteAsync("UnAuthorized");
}
else
{
return _next(httpContext);
}
}
}
Now we can configure this middleware in Configure method as below
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseMiddleware<ValidateRequestMiddleware>();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Note the Order in which we mentioned the middleware is very important and if we call app.UseMiddleware<ValidateRequestMiddleware>(); after app.UseEndpoints then Controller endpoint can be accessed irrespective of token value is present in the request header.
With Token
Without Token
Built-in Middlewares
ASP.NET Core comes with various middleware which can be used for various purposes. If we are creating any project type like Web API, MVC or Razor, etc. using Microsoft Visual Studio then we can see some built-in middlewares are mentioned in Configure method based on project type. Some of the most common middlewares are Routing, Authorization, Exception Handler, etc...
Middleware |
Description |
DeveloperExceptionPage |
Generates a page with error information used in the DEV environment. |
CORS |
Configure Cross-Origin Resource Sharing |
Authentication |
Configure Authentication |
Authorization |
Configure Authorization |
Static Files |
Helps serve static files and Directory browsing |
MVC |
Process request with MVC or Razor Pages |
EndpointRouting |
Define and constrains request routes. |
There are much more middleware and the above table are some of the widely used middleware.
Conclusion
Middleware is important in ASP.NET Core as it helps us define how the application will respond to the HTTP request. It helps us to check exception details, route the requests to endpoints, log, helps to short circuit the request based on the condition. Order of the middleware is important for any application because it executes based on the order it's mentioned in the Configure method.
Share This Post
Support Me