C# Cơ bản .NET Core §1) Cài đặt, chương trình C# đầu tiên §2) Biến, kiểu dữ liệu và nhập/xuất §3) Toán tử số học và gán §4) So sánh, logic và lệnh if, switch §5) Vòng lặp for, while §6) Phương thức - Method §7) Phương thức - Delegate §8) Lớp - Class §9) Kiểu vô danh và dynamic §10) Biểu thức lambda §11) Event §12) Hàm hủy - Quá tải toán tử - thành viên tĩnh - indexer §13) Lớp lồng nhau - namespace §14) null và nullable §15) Mảng §16) Chuỗi ký tự §17) Tính kế thừa §18) Tính đa hình - abstract - interface §19) Struct và Enum §20) Ngoại lệ Exeption §21) IDisposable - using §22) File cơ bản §23) FileStream §24) Generic §25) Collection - List §26) SortedList §27) Queue / Stack §28) Linkedlist §29) Dictionary - HashSet §30) Phương thức mở rộng §31) ObservableCollection §32) LINQ §33) (Multithreading) async - bất đồng bộ §34) Type §35) Attribute Annotation §36) DI Dependency Injection §37) (Multithreading) Parallel §38) (Networking) HttpClient §39) (Networking) HttpMessageHandler §40) (Networking) HttpListener §41) (Networking) Tcp TcpListenerr/TcpClient §42) (ADO.NET) SqlConnection §43) (ADO.NET) SqlCommand §44) (EF Core) Tổng quan §45) (EF Core) Tạo Model §46) (EF Core) Fluent API §47) (EF Core) Query §48) (EF Core) Scaffold §49) (EF Core) Migration §50) (ASP.NET CORE) Hello World! §51) (ASP.NET CORE) Middleware §52) (ASP.NET CORE) Map - Request - Response §53) (ASP.NET CORE) IServiceCollection - MapWhen §54) (ASP.NET CORE) Session - ISession §55) (ASP.NET CORE) Configuration §56) (ASP.NET CORE MVC) Controller - View

Giới thiệu về MiddleWare / Pipeline, áp dụng trong ASP.NET CORE

Một Middleware là một module code nó nhận yêu cầu gửi đến Request và trả về Response. Cụ thể trong ASP.NET Core, middlewarre có thể:

  • Nhận một HTTP Request gửi đến và phát sinh ra HTTP Response để trả về
  • Nhận một HTTP Request gửi đến, thi hành một số tác vụ (có thể là sửa đổi HTTP Request), sau đó chuyển đến một middleware khác.
  • Nhận HTTP Response, sửa nó và chuyển đến một Middleware khác

Pipeline: Trong ứng dụng ASP.NET Core, các middlware kết nối lại với nhau thành một xích, middleware đầu tiên nhận HTTP Request, xử lý nó và có thể chuyển cho middleware tiếp theo hoặc trả về ngay HTTP Response. Chuỗi các middleware theo thứ tự như vậy gọi là pipeline.

Để thực hành, tạo ra ứng dụng web đơn giản với cách lệnh sau:

mkdir Middleware      # tạo thư mục lựu code
cd Middleware         # vào thư mục
dotnet new web        # tạo dự án với template mẫu, đơn giản
code .                # mở ra bằng Visual Studio Code, nhớ chọn .NET: Generate Assets for Build and Debug lần đầu mở ra

Trong ứng dụng ASP.NET CORE, có sẵn một một loạt Middleware hoặc có thể xây dựng thêm để đưa vào một pipeline, luồng xử lý HttpRequest và trả về HttpResponse. Việc thêm Middleware vào pipeline được thực hiện ở phương thức Configure của lớp Startup. Các middleware trong pipeline xử lý các HTTP Message đều có dạng - nhận đầu vào là tham số kiểu HttpContext (có chứa HttpRequest và HttpRespone), thi hành code, sau đó chuyển đến cho middleware tiếp theo.

Ví dụ sửa mã Startup.cs có sử dụng một số Middleware như sau:


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

namespace Middleware
{
    public class Startup
    {
        // đang ký các dịch vụ
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDistributedMemoryCache();       // Thêm dịch vụ dùng bộ nhớ lưu cache (session sử dụng)
            services.AddSession();                      // Thêm  dịch vụ Session
        }


        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
                // Thêm StaticFileMiddleware - nếu Request là yêu cầu truy cập file tĩnh,
                // Nó trả ngay về Response nội dung file và là điểm cuối pipeline, nếu  khác
                // nó gọi  Middleware phía sau trong Pipeline
            app.UseStaticFiles();

                // Thêm SessionMiddleware:  khôi phục, thiết lập - tạo ra session
                // gán context.Session, sau đó chuyển gọi ngay middleware
                // tiếp trong pipeline
            app.UseSession();

                // Thêm EndpointRoutingMiddleware: ánh xạ Request gọi đến Endpoint (Middleware cuối)
                // phù hợp định nghĩa bởi EndpointMiddleware
            app.UseRouting();


               // EndpointMiddleware: định nghĩa các EndPoint (là các Delegate), như là  điểm cuối của
               // pipeline - cho mỗi URL phù hợp
            app.UseEndpoints(endpoints =>
            {

                //  EndPoint(1) -  phù hợp  khi truy cập /Home với phương thức GET - nó làm Middleware cuối Pipeline
                endpoints.MapGet("/Home", async context =>
                {
                    int? count  = context.Session.GetInt32("count");
                    count = (count != null) ? count + 1 : 1;
                    context.Session.SetInt32("count", count.Value);
                    await context.Response.WriteAsync("Home page!");
                });

            });

            // Delegate (1) - nó làm Middleware cuối Pipleline
            app.Run(async context  => {
                context.Response.StatusCode = StatusCodes.Status404NotFound;
                await context.Response.WriteAsync("Page not found");
            });


        }
    }
}

Với cấu hình trên, tùy thuộc vào truy vấn mà HttpContext có thể đi qua các Middleware: StaticFileMiddleware, SessionMiddleware, EndpointRoutingMiddleware, EndPoint(1), Delegate (1), ví dụ:

  • Nếu truy cập đến một file tĩnh (file chứa trong thư mục wwwroot), thì điểm cuối của pipeline là StaticFileMiddleware (các middleware khác không được sử dụng).
  • Nếu truy cập địa chỉ /Home (http://localhost:5000/Home) thì điểm cuối của Pipeline là EndPoint(1) - tức HttpContext đi qua các Middleware: StaticFileMiddleware - SessionMiddleware - EndpointRoutingMiddleware - EndpointMiddleware (ánh xạ EndPoint(1))
  • Nếu truy cập các địa chỉ còn lại thì điểm cuối của pipeline là Delegate (1): tức HttpContext đi qua các Middleware: StaticFileMiddleware - SessionMiddleware - EndpointRoutingMiddleware - Delegate (1)

Tạo Middleware riêng trong ASP.NET

Giờ ta sẽ thực hành tạo ra một Middleware như sau: tạo Middleware có tên là CheckAcessMiddleware, đăng ký Middleware này nằm sau SessionMiddleware trong pipeline của ứng dụng trên, CheckAcessMiddleware có chức năng - kiểm tra nếu truy cập là URL /testxxx thì không gọi Middleware tiếp theo mà thiết lập ngay HttpRespone hiện thị thông báo không được truy cập.

Còn nếu Url khác /testxxx thì thêm vào HttpResponse một Header tên throughCheckAcessMiddleware với giá trị ngày tháng - để biết HttpRequest đã đi qua Middleware này sau đó gọi Middleware tiếp theo trong pipeline.

Cấu trúc Middleware trong ASP.NET

Một lớp (class) phù hợp là một Middleware trong ASP.NET nếu lớp đó có cấu trúc thỏa mãn những điều kiện sau:

  • Có một phương thức khởi tạo public (hàm tạo) với tham số thứ nhất kiểu RequestDelegate, nếu có tham số thứ 2 thì các tham số tiếp theo này phải Inject được từ DI của hệ thống
  • Phải có tổi thiểu một trong hai phương thức có tên Invoke hoặc InvokeAsync với tham số nhận là HttpContext, những phương thức này phải trả về Task. (Dùng InvokeAsync nếu muốn áp dụng kỹ thuật bất đồng bộ - nên làm)
  • Trong Invoke/InvokeAsync bạn viết code xử lý tác vụ của Middleware, sau đó quyết định chuyển đến Middleware tiếp theo bằng cách gọi RequestDeleage đã truyền đến trong hàm tạo, hoặc không chuyến đến Middlware tiếp theo thì cần đảm bảo lúc này HttpResponse trrong HttpContext đã phù hợp để trả về cho Client.

Tạo Middleware tên CheckAcessMiddleware

Middleware/CheckAcessMiddleware.cs

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

namespace Middleware
{
    public class CheckAcessMiddleware
    {
        // Lưu middlewware tiếp theo trong Pipeline
        private readonly RequestDelegate _next;
        public CheckAcessMiddleware(RequestDelegate next) {
            _next = next;
        }
        public async Task Invoke(HttpContext httpContext) {

            Console.WriteLine("Go in CheckAcessMiddleware");

            if (httpContext.Request.Path ==  "/testxxx") {
                await  Task.Run(
                    async () => {
                        string html = "<h1>CAM KHONG DUOC TRUY CAP</h1>";
                        httpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
                        await httpContext.Response.WriteAsync(html);
                    }
                );
                // Không gọi Middleware tiếp theo
            }
            else
            {
                // Thiết lập Header cho HttpResponse
                httpContext.Response.Headers.Add("throughCheckAcessMiddleware", new[] { DateTime.Now.ToString()});

                // Chuyển Middleware tiếp theo trong pipeline
                await _next(httpContext);
            }

         }
    }
}

Đưa Middleware vào pipeline

Như vậy đã có một Middleware theo đúng chuẩn để có thể đưa vào pipeline nhận HttpRequest của ASP.NET Core. Để đăng ký chỉ việc sử dụng phương thức UseMiddleware của IApplicationBuilder

Ví dụ, tại phương thức Configure của Startup

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    / ..
    app.UseStaticFiles();
    app.UseSession();
    // Đưa Middleware vào pipeline - vị trí thứ 3
    app.UseMiddleware<CheckAcessMiddleware>();
    /..

}

Tuy nhiên ta sẽ làm theo cách các Middleware mặc định được đăng ký, tức là thêm phương thức mở rộng vào IApplicationBuilder, ví dụ thêm vào nó phương thức UseCheckAccess (về phương thức mở rộng xem phương thức mở rộng )

Middleware/MyAppExtensions.cs

using Microsoft.AspNetCore.Builder;

namespace Middleware
{
    public static class MyAppExtensions
    {
         public static IApplicationBuilder UseCheckAccess(this IApplicationBuilder builder)
         {
             return builder.UseMiddleware<CheckAcessMiddleware>();
         }
    }
}

Đã có phương thức mở rộng UseCheckAccess, để đăng ký Middleware vào pipeline thực hiện

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    / ..
    app.UseStaticFiles();                       // Đưa Middleware vào pipeline - vị trí đầu tiên
    app.UseSession();                           // Đưa Middleware vào pipeline - vị trí thứ 2

    app.UseCheckAccess();                      // Đưa Middleware vào pipeline - vị trí thứ 3
    /..

}
    

Kiểm tra:

Giờ truy cập vào địa chỉ /testxxx thì Request sau khi qua Middleware Session, đến Middleware CheckAccess thì bị chặn lại, các Middleware phía sau không được gọi.

Trong trường hợp, địa chỉ truy cập khác /testxxx, thì Request sau khi đi qua CheckAccess - thì Response có thêm một Header và chuyển các Request cho Middleware tiếp theo xử lý

Header được thêm vào sau khi đi qua middleware CheckAccess

Source Code - Mã nguồn
Đăng ký theo dõi ủng hộ kênh