Middleware và pipeline
Nội dung bài · 5 mục
- 1.Khái niệm
- 2.Ví dụ
- 3.Thử ngay
- 4.Lỗi hay gặp
- 5.Tóm tắt
Để đo mỗi request mất bao lâu, bạn không muốn thêm code đo vào từng action. Việc chung cho mọi request như vậy nên làm ở một chỗ, trước khi request tới controller. Chỗ đó là pipeline.
Khái niệm
🚰 Pipeline: chuỗi các bước mà mọi request đi qua trước khi tới controller, rồi response đi ngược lại qua đúng các bước đó.
🧱 Middleware: một bước trong pipeline, xử lý request, gọi bước tiếp theo, rồi xử lý response.
Ví dụ
using System.Diagnostics;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.Use(async (context, next) =>
{
var watch = Stopwatch.StartNew();
await next();
Console.WriteLine(
$"{context.Request.Path} " +
$"{context.Response.StatusCode} " +
$"{watch.ElapsedMilliseconds}ms");
});
app.MapControllers();
app.Run();app.Use(...)thêm một middleware vào pipeline. Tham số là lambda nhận hai tham số(context, next), thân nhiều câu lệnh đặt trong{ }.- Lambda cũng đánh dấu
asyncđược như method. contextchứa request và response hiện tại.- Code trước
await next()chạy khi request đi vào. Code sau nó chạy khi response đi ra. Stopwatchđo thời gian, cầnusing System.Diagnostics.- Middleware chạy theo đúng thứ tự được thêm trong
Program.cs.
Thử ngay
Thay phần middleware ở ví dụ bằng hai middleware:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.Use(async (context, next) =>
{
Console.WriteLine("A vào");
await next();
Console.WriteLine("A ra");
});
app.Use(async (context, next) =>
{
Console.WriteLine("B vào");
await next();
Console.WriteLine("B ra");
});
app.MapControllers();
app.Run();Chạy server, gọi curl -i http://localhost:5000/api/products rồi xem cửa sổ
đang chạy server.
Đoán trước khi chạy: bốn dòng in ra theo thứ tự nào?
Xem kết quả
A vào
B vào
B ra
A raRequest đi xuôi A rồi B, tới controller, rồi response đi ngược B rồi A. Middleware thêm trước thì bọc ngoài middleware thêm sau.
Lỗi hay gặp
Quên await next(). Request dừng ở middleware này, controller không bao
giờ chạy. Client nhận response rỗng.
// SAI — không gọi next, request không đi tiếp
var app = WebApplication.Create(args);
app.Use(async (context, next) =>
{
Console.WriteLine("Có request");
});// ĐÚNG
var app = WebApplication.Create(args);
app.Use(async (context, next) =>
{
Console.WriteLine("Có request");
await next();
});Sửa response sau khi đã gửi. Sau await next(), response thường đã bắt
đầu gửi về client. Đổi header lúc đó sẽ báo lỗi.
// SAI — header đã gửi đi, không đổi được nữa
var app = WebApplication.Create(args);
app.Use(async (context, next) =>
{
await next();
context.Response.Headers["X-Shop"] = "An";
});Muốn thêm header thì đặt code đó trước await next().
// ĐÚNG — đổi header khi response chưa gửi
var app = WebApplication.Create(args);
app.Use(async (context, next) =>
{
context.Response.Headers["X-Shop"] = "An";
await next();
});Tóm tắt
- Pipeline là chuỗi middleware mà mọi request đi qua.
- Middleware xử lý request trước
await next(), xử lý response sau đó. - Thứ tự thêm middleware trong
Program.cslà thứ tự chạy. - Việc chung cho mọi request như đo thời gian, ghi log nên làm ở middleware.
Tự kiểm tra
0/3 câuBa middleware X, Y, Z được thêm theo thứ tự đó. Response đi ra qua chúng theo thứ tự nào?
Việc nào hợp để làm bằng middleware?
Một middleware không gọi await next(). Chuyện gì xảy ra với request?