Middleware — nima, pipeline, tartib, custom middleware yaratish
Muammodan boshlaylik
Har bir API so'rovi uchun kerak:
- So'rov qancha vaqt olganini log qilish
- Har bir so'rovga noyob ID berish va uni javob sarlavhasiga qo'shish
- Xizmat ko'rsatish rejimida barcha so'rovlarga
503qaytarish
Har bir controller va har bir action ga bu kodni yozish — yuzlab takrorlanish va bittasi albatta unutiladi. Kerak: barcha so'rovlar o'tadigan bitta joy.
Middleware nima?
Middleware — HTTP so'rovi va javobi o'tadigan zanjirdagi komponent. Har biri:
- So'rov bilan biror narsa qiladi
- Keyingi middleware ni chaqiradi (yoki zanjirni to'xtatib, o'zi javob beradi)
- Javob qaytayotganda yana biror narsa qilishi mumkin
So'rov ──▶ [ Log ] ──▶ [ Auth ] ──▶ [ Controller ]
│ │ │
Javob ◀──────┴───────────┴───────────────┘
37-darsdagi app.UseHttpsRedirection(), app.UseAuthentication() — o'rnatilgan middleware lar.
Hayotiy o'xshatma
Aeroportdagi yo'lovchi yo'li: chipta tekshiruvi → bagaj topshirish → xavfsizlik nazorati → pasport nazorati → samolyot. Har bir punkt o'z ishini qiladi va keyingisiga o'tkazadi. Xavfsizlik taqiqlangan narsa topsa — yo'lovchi samolyotga yetib bormaydi (zanjir to'xtatildi). Qaytishda esa yo'l teskari tartibda.
Inline middleware — app.Use
app.Use(async (context, next) =>
{
var vaqt = Stopwatch.StartNew();
await next(context); // keyingi middleware va controller
vaqt.Stop();
Console.WriteLine(quot;{context.Request.Method} {context.Request.Path} → {context.Response.StatusCode} ({vaqt.ElapsedMilliseconds} ms)");
});
next(context)dan oldingi kod — so'rov ketayotgandanext(context)dan keyingi kod — javob qaytayotgandacontext—HttpContext:Request,Response,User,Items
Zanjirni to'xtatish
app.Use(async (context, next) =>
{
if (builder.Configuration.GetValue<bool>("XizmatRejimi"))
{
context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
await context.Response.WriteAsync("Texnik ishlar olib borilmoqda");
return; // next chaqirilmaydi — controller ga yetib bormaydi
}
await next(context);
});
app.Run — oxirgi middleware
app.Run(async context => await context.Response.WriteAsync("Hech narsa topilmadi"));
Run — next siz, zanjirning oxiri.
Klass sifatida middleware
Kattaroq va qayta ishlatiladigan mantiq uchun:
public class SorovIdMiddleware(RequestDelegate next, ILogger<SorovIdMiddleware> logger)
{
private const string Sarlavha = "X-Request-Id";
public async Task InvokeAsync(HttpContext context)
{
var id = context.Request.Headers[Sarlavha].FirstOrDefault() ?? Guid.NewGuid().ToString("N");
context.TraceIdentifier = id;
context.Response.OnStarting(() =>
{
context.Response.Headers[Sarlavha] = id;
return Task.CompletedTask;
});
using (logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = id }))
{
await next(context);
}
}
}
// Program.cs
app.UseMiddleware<SorovIdMiddleware>();
- Konstruktor:
RequestDelegate next+ singleton bog'liqliklar (middleware bir marta yaratiladi) InvokeAsync(HttpContext context, ...)— har so'rovda; scoped servislar (masalan,DbContext) shu metod parametrlari orqali
public async Task InvokeAsync(HttpContext context, DokonDbContext db) // scoped — shu yerda
Extension metod bilan
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseSorovId(this IApplicationBuilder app) =>
app.UseMiddleware<SorovIdMiddleware>();
}
app.UseSorovId();
Tartib — eng muhim qoida
app.UseExceptionHandler("/error"); // 1 — keyingi hammasining xatolarini ushlaydi
app.UseSorovId(); // 2 — log larda ID bo'lishi uchun erta
app.UseHttpsRedirection();
app.UseCors();
app.UseAuthentication(); // Authorization dan OLDIN
app.UseAuthorization();
app.MapControllers();
[!WARNING] Middleware lar yozilgan tartibda bajariladi. Keng tarqalgan xatolar:
UseAuthorizationUseAuthenticationdan oldin (hamma 401),UseCorscontroller lardan keyin (CORS sarlavhalari qo'shilmaydi), xato ushlovchi pipeline oxirida (undan oldingi xatolar ushlanmaydi). Shubha bo'lsa — rasmiy hujjatdagi tartib jadvaliga qarang.
[!INFO]
await next(context)dan keyin javob allaqachon yuborilgan bo'lishi mumkin. Shu paytdacontext.Response.Headersni o'zgartirish —InvalidOperationException: Headers are read-only, response has already started. Sarlavha qo'shish uchun —Response.OnStarting(...)(yuqoridagi misolda).
Middleware yoki filtr?
| Middleware | Filtr (action filter) | |
|---|---|---|
| Qayerda | Barcha so'rovlar (statik fayllar ham) | Faqat controller/action lar |
| Nimani biladi | HttpContext |
Qaysi controller, action, model, argumentlar |
| Qachon | Log, so'rov ID, xatolar, CORS, rate limit | Action ga xos: validatsiya, audit, natijani o'zgartirish |
Tez-tez uchraydigan xatolar
next ni chaqirishni unutish:
app.Use(async (context, next) =>
{
Console.WriteLine("Log");
// ❌ await next(context) yo'q — hamma so'rovlar shu yerda bo'sh javob bilan tugaydi
});
Javob boshlangandan keyin sarlavha qo'shish — OnStarting.
Middleware konstruktorida scoped servis — captive dependency (38-dars); InvokeAsync parametriga o'tkazing.
Xulosa
Middleware — barcha so'rovlar o'tadigan zanjir bo'g'ini: so'rovda ishlaydi, next ni chaqiradi yoki zanjirni to'xtatib o'zi javob beradi, javob qaytayotganda yana ishlaydi. Inline — app.Use(async (ctx, next) => ...), oxirgisi — app.Run.
Klass middleware — RequestDelegate next + InvokeAsync(HttpContext); konstruktorda singleton, InvokeAsync parametrlarida scoped bog'liqliklar; app.UseMiddleware<T>(). Tartib hal qiluvchi: xatolar → ... → Authentication → Authorization → endpoint lar. Sarlavhalar — Response.OnStarting.
Keyingi dars
40-dars: Configuration — appsettings.json, environment, User Secrets, IOptions