3️⃣ با یک کلاس Middleware که اینترفیس IMiddleware را پیادهسازی میکند 🛡
رویکرد قبلی معایب خود را دارد: توسعهدهنده باید یک کلاس middleware بسازد که از تمام قوانین ذکر شده در بالا پیروی کند، در غیر این صورت middleware کار نخواهد کرد. اما یک راه امنتر برای ایجاد middleware وجود دارد: پیادهسازی اینترفیس IMiddleware:
public class ExecutionTimeMiddleware : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var watch = Stopwatch.StartNew();
await next(context);
watch.Stop();
Console.WriteLine($"Request executed in {watch.ElapsedMilliseconds}ms");
}
}
این رویکرد بسیار امنتر است زیرا کامپایلر به شما میگوید که کلاس middleware چگونه باید باشد.
برای این رویکرد شما باید به صورت دستی ExecutionTimeMiddleware را در کانتینر DI ثبت کنید:
builder.Services.AddScoped<ExecutionTimeMiddleware>();
برای افزودن این middleware به پایپلاین، متد UseMiddleware را روی کلاس WebApplication فراخوانی کنید:
app.Use(async (context, next) =>
{
// Middleware از مثال قبلی
});
app.UseMiddleware<LoggingMiddleware>();
app.UseMiddleware<ExecutionTimeMiddleware>();
در نتیجه اجرای این middleware، موارد زیر هنگام اجرای یک endpoint به آدرس /api/books در کنسول لاگ خواهد شد:
Request is starting...
Request: GET /api/books
Request executed in 68ms
Response: 200
Request has finished
میدلرور ها و تزریق وابستگی (Dependency Injection) 💉
میدلور هایی که بر اساس قرارداد ساخته میشوند، به طور پیشفرض طول عمر Singleton دارند و تمام وابستگیهای تزریق شده در سازنده نیز باید singleton باشند. همانطور که میدانیم، middlewareها به ازای هر درخواست اجرا میشوند و شما میتوانید وابستگیهای scoped را در متد InvokeAsync بعد از HttpContext تزریق کنید. در اینجا ما یک ILoggingService را که به عنوان سرویس scoped در DI ثبت شده است، تزریق میکنیم:
builder.Services.AddScoped<ILoggingService, ConsoleLoggingService>();
در مرحله بعد:
public class LoggingMiddleware
{
private readonly RequestDelegate _next;
public LoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context, ILoggingService loggingService)
{
loggingService.LogRequest(context.Request.Method, context.Request.Path);
await _next(context);
loggingService.LogResponse(context.Response.StatusCode);
}
}