เขียน middleware บน .NET Core 2.0

โดยปกติ เวลาสั่ง dotnet run ตัว web api มันจะแสดงแค่ว่า ตอนนี้กำลัง listen อยู่ที่ port ไหน ถ้ามี request ที่ตรงกับ route เข้ามาก็เงียบ ๆ จะมีบ่น ๆ ก็แค่ตอนเจอ error เลยลองตั้งโจทย์ว่า อยากจะให้ web api request มันคอย log ทุก request ที่เข้ามาบน console เหมือนกับ access log จะทำยังไงดี middleware น่าจะเป็นคำตอบที่น่าสนใจ

วิธีการใช้ middleware บน .NET Core ทำได้โดยการเรียกใช้ middleware ใน Configure() ในไฟล์ Startup.cs

middleware ที่ว่ามันสามารถทำเป็น anonymous middleware หรือเป็น named middleware ก็ได้ เช่น ถ้าจะทำเป็น anonymous middleware ก็ทำเป็น lambda expression ได้ประมาณนี้

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    
    app.Use((context, next) =>
        {
            Console.WriteLine(context.Request.Path);
            return next();
        });

    app.UseMvc();
}

แต่ถ้าเราต้องการทำ named middleware เช่น สมมติว่าชื่อ AccessLogger และสามารถเรียกใช้งานได้ผ่าน UseAccessLogger() ใน Configure() ด้วยวิธีประมาณนี้

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

    app.UseMvc();
}

แปลว่า เราจะต้องสร้าง middleware ขึ้นมาใช้เอง ทีนี้ ลองมาสร้าง UseAccessLogger() และ middleware ที่ชื่อ AccessLogger ดู

เริ่มจาก สร้าง folder ชื่อ Middlewares เอาไว้เก็บ middleware ที่เราสร้างขึ้นมาทั้งหมด เสร็จแล้วสร้างไฟล์ 2 ไฟล์ ไฟล์แรก ชื่อ AccessLoggerMiddleware.cs มีรายละเอียดแบบนี้

using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using System;

namespace MiddlewareDotNetCore.Middlewares {
  public class AccessLoggerMiddleware {
    private readonly RequestDelegate _next;

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

    public Task Invoke (HttpContext context) {
      Console.WriteLine(context.Request.Path);
      return this._next (context);
    }
  }
}

ข้างใน middleware หลัก ๆ คือ เก็บ next เอาไว้ใช้สำหรับ เรียกใช้ (invoke) context เผื่อมี middleware ถัดไป ส่วนที่สำคัญคือ ใน method Invoke เราสามารถทำอะไรก็ตามที่อยากให้ middleware ทำ พอทำเสร็จแล้ว ก็ทำการ invoke middleware ถัดไป ผ่าน _next ที่เราเก็บไว้ก่อนหน้านี้ใน constructor

ส่วนอีกไฟล์ชื่อ AccessLoggerMiddlewareExtensions.cs ข้างในหน้าตาแบบนี้

using Microsoft.AspNetCore.Builder;

namespace MiddlewareDotNetCore.Middlewares
{
    public static class AccessLoggerMiddlewareExtensions
    {
        public static IApplicationBuilder UseAccessLogger(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<AccessLoggerMiddleware>();
        }
    }
}

ไฟล์นี้จะทำหน้าที่สร้าง method Use… ที่ให้เราเอาไปใช้ใน middleware ผ่านทาง IApplicationBuilder

ที่เหลือก็แค่เรียกใช้งานใน Configure() เป็นอันจบ

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MiddlewareDotNetCore.Middlewares;

namespace MiddlewareDotNetCore {
    public class Startup {
        public Startup (IConfiguration configuration) {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices (IServiceCollection services) {
            services.AddMvc ();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure (IApplicationBuilder app, IHostingEnvironment env) {
            if (env.IsDevelopment ()) {
                app.UseDeveloperExceptionPage ();
            }

            app.UseAccessLogger();

            app.UseMvc ();
        }
    }
}

โค้ดตัวอย่าง

ดูได้ที่ https://github.com/chonla/MiddlewareDotNetCore

Leave a Reply