[ASPNET Core 2] – Middleware

Here is a strange request: Write hello world using ASP.NET.

So easy, dotnet new mvc then modify Views/Home/Index.cshtml to return a single line of <p>hello world</p>. Must be it, right?

There is another way 😀

1. What is middleware

Imagine your asp.net application is a water pipe. Your request is the water itself. The input water (request) is dirty, contaminated and unhealthy. You expected the output water (response) is clean, no smell, drinkable. There must be some kind of filters on the pipe to do that

Middleware is that kind of filter. It assembled into your application pipeline to handle requests and responses.

middleware

middleware can decide to not pass a request to the next middleware, which is called

  short-circuit
  

2. Types of middleware

There are 3 type of middleware, classified on how you implement them

Type Can short-circuit Use for
Use Yes Short-circuit a request
Logic for serving response
Run No End of the pipeline
Map
MapWhen
No Branching the pipeline based on request path
MapWhen branching based on conditions
Support Nesting (multi-level branching)

Compared with the ancient ASP.NET MVC5

ASP.NET MVC5 ASP.NET Core 2
Concepts HTTP Handlers

HTTP Modules
middleware
Choose? HTTP Handlers => Based on filename extension

HTTP Modules => Hook into life cycle using events
Using specific order and types

Run => end of pipeline

Use => short-circuit and handle logic

Map => branching based on path
Easy to use? Require a deep understanding of ASP.NET life-cycle

Tricky to use since multiple modules can hook to the same event
Require a specific order

Single line of request pipeline, easy to understand and use/debug

3. Default middleware

When creating new asp.net core project, the .net cli will add some middleware for you to use

  1. Exception Handler: handle any exception from deeper middleware
  2. Static Files: return static files in wwwroots
  3. Mvc: Redirect request to corresponding controller’s actions

here 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 delegate

just add more code in Startup.cs, method Configure

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 separated class

this one is more complex, but flexible

first, the actual code for 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 extension to make it ready to consume

// Expose through IApplicationBuilder
public static class RequestCultureMiddlewareExtensions
{
    public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestCultureMiddleware>();
    }
}

and finally, consume in 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}");
    });
}

A note on Dependency Injection

Scoped lifetime service must be inject into Invoke or InvokeAsync method

Inject the scoped lifetime service via constructor will forces the service to behave like a singleton

there is 3 type of lifetime for a service in asp.net, which is transient, scoped and singleton

  • Transient lifetime services are created each time they’re requested. This lifetime works best for lightweight, stateless services.

  • Scoped lifetime services are created once per request.

  • Singleton lifetime services are created the first time they’re requested (or when ConfigureServices is run if you specify an instance there) and then every subsequent request will use the same instance.

Advertisements

One thought on “[ASPNET Core 2] – Middleware

  1. Pingback: [ASPNET Core 2] – Middleware | Tuan Tran's Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.