[ASPNET Core 2] – Middleware
For English version: [ASPNET Core 2] – Middleware
Sometimes you get an awkward request: write Hello world in ASP.NET
Dead easy: dotnet new mvc, then edit Views/Home/Index.cshtml so it returns one line
<p>hello world</p>
Done, right? There is another way :D
1. What is middleware
Imagine your asp.net app is a water pipe. Data is the water. The water at the start of the pipe (the request) is as dirty as the Nhiêu Lộc canal. You want the water at the end of the pipe (the response) to be as clean as Lavie mineral water. There must be some filter in the middle of the pipe, right? Middleware is exactly that filter. It plugs into the app to handle requests and responses

middleware can decide whether to pass the request it handled on to the next middleware or not. When it cuts things off and passes nothing on, we call that a
short-circuit
2. The kinds of middleware
There are 3 kinds of middleware, classified by how you implement it
| Kind | Can short-circuit | Used for |
|---|---|---|
| Use | Yes | Short-circuiting a request Logic that produces a response |
| Run | No | Ending the pipeline |
| Map MapWhen |
No | Branching the pipeline based on the request path MapWhen branches based on a condition Supports Nesting (multi-level branching) |
Compared with the ancient ASP.NET MVC5
| ASP.NET MVC5 | ASP.NET Core 2 | |
|---|---|---|
| The concept | HTTP Handlers HTTP Modules |
middleware |
| How is it chosen? | HTTP Handlers => based on the filename extension HTTP Modules => hooked into the life cycle using events |
Defined in a specific order with specific keywords Run => ends the pipeline Use => short-circuits and handles logic Map => branches based on the path |
| Easy to use? | Requires a deep understanding of the ASP.NET life-cycle Hard to use because many modules can hook into the same event |
Requires a specific order Only 1 pipeline, easy to understand/use/debug |
3. Default middleware
When you create a new asp.net core project, the .net cli adds a few middlewares for you
- Exception Handler: handles exceptions from the middlewares below it
- Static Files: returns files in wwwroots
- Mvc: routes requests to the actions in the controllers
here’s the source code:
public static void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error/500");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseSession();
if (!env.IsDevelopment())
{
app.UseMiddleware<ErrorHandlingMiddleware>();
}
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Here is the list of built-in middlewares
4. Writing a middleware
4.1. Using a delegate
add code to Startup.cs, the Configure method
app.Use((context, next) =>
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
// Call the next delegate/middleware in the pipeline
return next();
});
4.2. Using your own class
more complicated, but more flexible in return. First, the code for the middleware
public class RequestCultureMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next) { _next =next; }
public Task InvokeAsync(HttpContext context)
{
var cultureQuery = context.Request.Query["culture"];
if (!string.IsNullOrWhiteSpace(cultureQuery))
{
var culture = new CultureInfo(cultureQuery);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
// Call the next delegate/middleware in the pipeline
return this._next(context);
}
}
then the code for the extension so you can use that middleware in the Configure method
// Expose through IApplicationBuilder
public static class RequestCultureMiddlewareExtensions
{
public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestCultureMiddleware>();
}
}
finally, use it in the Configure method
// Use in Startup.cs => Configure method
public void Configure(IApplicationBuilder app)
{
app.UseRequestCulture();
app.Run(async (context) =>
{
await context.Response.WriteAsync($"Hello{CultureInfo.CurrentCulture.DisplayName}");
});
}
notes when using Dependency Injection
A
Scoped lifetime servicemust be injected into the Invoke or InvokeAsync method. Injecting ascoped lifetime servicethrough the constructor forces that service into being a singleton
there are 3 lifetimes for a service in asp.net: transient, scoped and singleton
- Transient lifetime services are created every time they are called. This kind fits lightweight, stateless services.
- Scoped lifetime services are created once per request.
- Singleton lifetime services are created on the first request (or when ConfigureServices runs, if you create an instance of the service there) and every request after that reuses this instance.