[ASPNET Core 2] – Middleware
For English version: [ASPNET Core 2] – Middleware
Đôi khi bạn có một yêu cầu éo le: Viết Hello world bằng ASP.NET
Dễ ẹt, dotnet new mvc
, rồi sửa Views/Home/Index.cshtml
cho nó trả về 1 dòng
<p>hello world</p>
Thế là xong, phải ko? Có cách khác :D
1. Middleware là giề
Tưởng tượng rằng ứng dụng asp.net của bạn là 1 đường ống nước. Data chính là nước. Nước ở đầu ống (request) thì bẩn như kênh nhiêu lộc. Bạn mong muốn rằng nước ở cuối ống (response) phải sạch như nước khoáng Lavie. Chắc phải có lọc gì đó ở giữa ống nhể? Middleware chính là loại lọc đó. Nó gắn vào ứng dụng để xử lý requests và responses
middleware có thể quyết định là nó có tiếp tục truyền cái request nó đã xử lý cho 1 middleware tiếp theo hay ko. Trong trường hợp nó ngắt luôn ko truyền, thì ta gọi đó là
short-circuit
2. Các loại middleware
Có 3 loại middleware, phân loại bằng cách bạn implement nó như nào
Loại | Có thể short-circuit | Dùng để |
---|---|---|
Use | Có | Short-circuit một request Logic để tạo response |
Run | Không | Kết thúc pipeline |
Map MapWhen |
Không | Phân nhánh pipeline dựa trên request path MapWhen phân nhánh dựa trên điều kiện Hỗ trợ Nesting (multi-level branching) |
So sánh với đồ cổ ASP.NET MVC5
ASP.NET MVC5 | ASP.NET Core 2 | |
---|---|---|
Khái niệm | HTTP Handlers HTTP Modules |
middleware |
Lựa chọn? | HTTP Handlers => Dựa trên filename extension HTTP Modules => Móc vào life cycle bằng cách dùng events |
Định nghĩa theo 1 thứ tự đặc biệt và các từ khóa Run => kết thúc pipeline Use => short-circuit và xử lý logic Map => phân nhánh dựa trên path |
Dễ xài ko? | Đòi hỏi hiểu sâu về ASP.NET life-cycle Khó xài vì nhiều modules có thể móc vào cùng 1 event |
Yêu cầu 1 thứ tự nhất định Chỉ có 1 dòng pipeline, dễ hiểu/xài/debug |
3. Default middleware
Khi mới tạo 1 project asp.net core mới, .net cli sẽ thêm vào 1 số middleware cho bạn
- Exception Handler: handle exception từ các middleware ở dưới
- Static Files: trả về files trong wwwroots
- Mvc: Hướng request tới các action trong controller
source code nè:
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?}");
});
}
Đây danh sách các built-in middlewares
4. Viết một middleware
4.1. Dùng delegate
thêm code vào 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. Xài class riêng
phức tạp hơn, nhưng bù lại linh hoạt hơn đầu tiên, code cho 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);
}
}
sau đó, code cho extension để xài được middleware đó trong method Configure
// Expose through IApplicationBuilder
public static class RequestCultureMiddlewareExtensions
{
public static IApplicationBuilder UseRequestCulture(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestCultureMiddleware>();
}
}
cuối cùng, xài trong 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}");
});
}
ghi chú khi xài Dependency Injection
Scoped lifetime service
phải được inject vào Invoke hoặc InvokeAsync method Inject cáiscoped lifetime service
thông qua constructor sẽ ép cái service đó thành singleton
có 3 loại lifetime cho một service trong asp.net, là transient
, scoped
và singleton
- Transient lifetime services được tạo ra mỗi lần nó được gọi. Loại này phù hợp cho các service lightweight, stateless.
- Scoped lifetime services được tạo cho mỗi request.
- Singleton lifetime services được tạo cho lần requested đầu tiên (hoặc khi ConfigureServices khởi chạy nếu bạn có tạo 1 instance của service trong đó) rồi các request sau đó sẽ xài lại instance này.