Mundarija (29)
- Bu darsda
- 1. Nega bu kerak?
- 1.1 Express'da qanday edi
- 1.2 Guard so'rov yo'lining qayerida
- 2. Birinchi guard
- 2.1 CanActivate
- 2.2 ExecutionContext
- 3. false yoki xato tashlash
- 3.1 Ikki yo'l
- 3.2 Model: guard zanjiri
- 3.3 Async guard
- 3.4 Guard keyingi qatlamlarga ma'lumot qoldiradi
- 4. Metama'lumot bilan guard
- 4.1 Naqsh
- 4.2 Reflector.createDecorator tur tuzog'i
- 5. Guard'lar tartibi
- 5.1 To'rt daraja
- 5.2 Global guard va DI
- 5.3 Guard xatosini kim ko'radi
- 6. Hujumchi nigohi
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): to'ldiring
- 2-mashq (o'rta): metama'lumotli guard modeli
- 3-mashq (qiyin): getAllAndOverride va getAllAndMerge
- 9. Loyiha qadami: JsonTanaGuard ni tekshirish
- 10. Real ishda
- Xulosa
- Manbalar
Guard'lar NestJS'da: CanActivate, ExecutionContext va false yoki xato tashlash
Qisqacha: Guard (himoyachi) — "bu so'rov handler'ga o'tishi mumkinmi?" degan savolga javob beradigan klass. U
CanActivateningcanActivate(kontekst)metodini bajaradi:true— o'tadi,false— Nest 403 Forbidden qaytaradi, xato tashlasa — o'sha xato (masalan, 401 yoki 415).ExecutionContextguard'ga so'rovni (switchToHttp().getRequest()), handler'ni (getHandler()) va controller klassini (getClass()) beradi — shular orqaliReflectorhandler'dagi metama'lumotni o'qiydi. Guard'lar middleware'dan keyin, interceptor va pipe'lardan oldin ishlaydi: global → controller → metod.
Bu darsda
- Guard yozasiz va uni metod, controller va global darajada ulaysiz.
ExecutionContextdan so'rov, handler va klassni olasiz.falseqaytarish va xato tashlash farqini haqiqiy javoblarda ko'rasiz.SetMetadata+Reflectorbilan "shu marshrutga qoida kerak" naqshini qo'llaysiz vaReflector.createDecoratortur tuzog'ini ko'rasiz.- Guard'lar tartibini va guard qachon middleware'dan yaxshiroq ekanini bilasiz.
Oldin bilishingiz kerak: Middleware Nest'da, Reflector, metadata va DiscoveryService, Request lifecycle, Status kodlarni to'g'ri tanlash.
Javobni serializatsiya qilish darsining «Loyiha qadami» da JsonTanaGuard ni qo'shdik — qisqa tushuntirish bilan. Bu darsda guard'larni to'liq ko'ramiz va o'sha guard'ning har qatorini tushunamiz.
1. Nega bu kerak?
O'tgan darsda middleware'larni ko'rdik. Middleware ham so'rovni to'xtata oladi — next() ni chaqirmasa bo'ldi. Nega yana bir qatlam?
Middleware qaysi handler ishlashini bilmaydi. U req va res ni ko'radi, xolos. "Bu marshrut faqat admin uchun", "bu marshrutga JSON tana kerak" — bunday qoidalar handler'ga bog'langan. Express'da buni marshrut darajasidagi middleware hal qilardi: .post(jsonKerak, handler). Nest'da marshrut darajasidagi middleware yo'q (Controller'lar darsidagi jadval). Uning o'rni — guard.
Guard nimani biladi:
- so'rovni (middleware kabi);
- qaysi handler va qaysi controller chaqirilishini;
- demak, ular ustidagi dekoratorlar metama'lumotini (
@Rollar("admin"),@JsonTana()).
Va u pipe'lardan oldin ishlaydi. Shuning uchun ruxsat tekshiruvi tananing tekshiruvidan oldin bo'ladi: ruxsatsiz foydalanuvchi validatsiya xatolarini ko'rmaydi (bu ham ma'lumot oshkor qiladi — "qaysi maydonlar bor").
O'xshatish: konsert zalining kirish nazoratchisi. Bino qorovuli (middleware) hammani ko'radi, lekin qaysi zalga ketayotganingizni bilmaydi. Zal eshigidagi nazoratchi (guard) aniq shu zal uchun chiptani tekshiradi: VIP zalga — VIP chipta, oddiy zalga — oddiy.
1.1 Express'da qanday edi
25-qismda "shu marshrutga JSON tana kerak" qoidasi marshrutning o'zida yozilardi: router.post("/", jsonKerak, yarat). 28-qismdagi auth ham shunday: kirishKerak middleware'i marshrut zanjiriga qo'yilardi. Qoida va marshrut bitta qatorda — ko'rinib turadi. Lekin har yangi marshrutda uni yana yozish kerak, unutilsa — marshrut himoyasiz.
| Express | Nest guard | |
|---|---|---|
| Qoida qayerda | marshrut zanjirida (jsonKerak) |
dekorator (@JsonTana()) + bitta global guard |
| Qaysi handler ekanini biladimi | yo'q, faqat req |
ha — getHandler() |
| Unutilsa | marshrut himoyasiz | global guard "sukut — yopiq" qila oladi |
| Narx | yo'q, sodda | bitta qo'shimcha qatlam va Reflector |
1.2 Guard so'rov yo'lining qayerida
Request lifecycle darsidagi tartibni har qatlam nimani biladi degan savol bilan qayta ko'raylik:
| Qatlam | Nimani biladi | Rad etsa |
|---|---|---|
| middleware | req, res |
o'zi javob yozadi yoki next(xato) |
| guard | req + handler va controller (metama'lumot) |
false — 403, yoki o'z xatosi |
| interceptor | guard'dagi hamma narsa + handler natijasi | xato tashlaydi yoki natijani o'zgartiradi |
| pipe | bitta parametr va uning metama'lumoti | 400/422 |
| handler | tekshirilgan parametrlar | — |
| xato filtri | tashlangan xato | javobni yozadi |
Guard — handler'ni biladigan birinchi qatlam. Shuning uchun "bu marshrutga kim kira oladi" degan savolning tabiiy joyi — aynan u. Interceptor ham handler'ni biladi, lekin u guard'dan keyin ishlaydi: ruxsatsiz so'rov unga yetib kelmasligi kerak. Pipe esa faqat bitta parametrni ko'radi — ruxsat haqida qaror qilish uchun unda ma'lumot yetarli emas.
2. Birinchi guard
2.1 CanActivate
@Injectable()
class YolgonGuard implements CanActivate {
canActivate(): boolean { return false; }
}
@Get('rad')
@UseGuards(YolgonGuard)
rad() { return 'yetmaydi'; }CanActivate— bitta metodli interfeys:canActivate(context). Qaytaradi:boolean,Promise<boolean>yokiObservable<boolean>.@UseGuards(...)— guard'ni metod yoki controller'ga ulaydi. Klass beriladi — Nest uni DI bilan yaratadi.
Demo loyihadagi javob (biz ishga tushirdik: NestJS 12.2.0, Node 24.21.0):
GET /t/rad {} → 403 {"message":"Forbidden resource","error":"Forbidden","statusCode":403}Tarjimasi: "Taqiqlangan resurs". false qaytarilganda Nest o'zi ForbiddenException tashlaydi. Handler chaqirilmadi — "yetmaydi" javobi kelmadi. Javob tanasi Nest'ning sukut formatida (message, error, statusCode); vazifalar-api da uni Problem Details filtri o'z formatiga aylantiradi.
2.2 ExecutionContext
Guard'ning argumenti — ExecutionContext. Demo guard uning asosiy metodlarini chaqirib, logga yozdi:
@Injectable()
class KontekstGuard implements CanActivate {
canActivate(k: ExecutionContext): boolean {
const req = k.switchToHttp().getRequest<Request>();
console.log(` [kontekst] turi=${k.getType()} ` +
`klass=${k.getClass().name} handler=${k.getHandler().name} ` +
`${req.method} ${req.url}`);
return true;
}
} [kontekst] turi=http klass=C handler=ochiq GET /t/ochiq
GET /t/ochiq {} → 200 ochiq| Metod | Nima beradi | Misol |
|---|---|---|
getType() |
kontekst turi | "http" (yana "rpc", "ws") |
switchToHttp().getRequest() |
Express req |
req.method, req.get(...) |
switchToHttp().getResponse() |
Express res |
sarlavha qo'yish |
getHandler() |
chaqiriladigan metod (funksiya) | ochiq |
getClass() |
controller klassi | C |
Nega switchToHttp()? Nest faqat HTTP freymvorki emas: o'sha guard WebSocket yoki mikroservis xabarida ham ishlashi mumkin (Mikroservis asoslari). ExecutionContext — umumiy qobiq; switchToHttp() — "HTTP ko'rinishiga o'tkaz". ArgumentsHost ham shu g'oya — xato filtrlarida ko'ramiz.
Chiqishdagi birinchi qator — guard'ning logi, ikkinchisi — javob. Tartibga qarang: guard handler'dan oldin yozdi va qaysi metod (ochiq) chaqirilishini allaqachon bildi. Middleware buni bila olmasdi.
getHandler() va getClass() — eng muhimlari: dekoratorlar metama'lumoti aynan shu ikki obyektda saqlanadi (Reflector).
3. false yoki xato tashlash
3.1 Ikki yo'l
Rad etishning ikki usuli bor va ular turli javob beradi. Demo guard: @KalitKerak() belgisi bor handler'da X-Api-Kalit: maxfiy sarlavhasini talab qiladi.
const KALIT = 'demo:kalit-kerak';
const KalitKerak = () => SetMetadata(KALIT, true);
@Injectable()
class KalitGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(k: ExecutionContext): boolean {
const kerak = this.reflector.get<boolean | undefined>(
KALIT, k.getHandler(),
);
if (kerak !== true) return true;
const req = k.switchToHttp().getRequest<Request>();
if (req.get('x-api-kalit') === 'maxfiy') return true;
throw new UnauthorizedException('X-Api-Kalit kerak');
}
}
@Controller('t')
@UseGuards(KontekstGuard, KalitGuard)
class C {
@Get('ochiq') ochiq() { return 'ochiq'; }
@Post('yopiq') @KalitKerak() yopiq() { return 'yopiq'; }
}POST /t/yopiq {} → 401 {"message":"X-Api-Kalit kerak","error":"Unauthorized","statusCode":401}
POST /t/yopiq {"x-api-kalit":"maxfiy"} → 201 yopiq- Xato tashlash — status va xabar sizning qo'lingizda: 401 "kalit kerak". Mijoz nima qilishini biladi.
false— doim 403 "Forbidden resource". Sabab aytilmaydi.
Status kodlarni to'g'ri tanlash darsidagi farqni eslang: 401 — "kimligingizni bilmaymiz" (autentifikatsiya yo'q yoki noto'g'ri), 403 — "bilamiz, lekin ruxsat yo'q". Kalitsiz so'rovga 403 noto'g'ri — u 401. Shuning uchun amalda guard'lar ko'pincha xato tashlaydi, false esa faqat "ruxsat yo'q" degan aniq holat uchun.
3.2 Model: guard zanjiri
Bir nechta guard qanday ishlashini kichik JS modelida ko'raylik:
class HttpXato extends Error {
constructor(status, xabar) { super(xabar); this.status = status; }
}
function guardlar(royxat, kontekst) {
for (const g of royxat) {
const javob = g(kontekst);
if (javob === false) {
throw new HttpXato(403, "Forbidden resource");
}
}
return "handler chaqiriladi";
}
const kirish = (k) => {
if (!k.foydalanuvchi) throw new HttpXato(401, "kirish kerak");
return true;
};
const admin = (k) => k.foydalanuvchi?.rol === "admin";
for (const k of [{}, { foydalanuvchi: { rol: "user" } },
{ foydalanuvchi: { rol: "admin" } }]) {
try {
console.log(guardlar([kirish, admin], k));
} catch (x) {
console.log(x.status, x.message);
}
}Konsolda:
401 kirish kerak
403 Forbidden resource
handler chaqiriladiUch holat — uch javob. Birinchi guard tushsa, ikkinchisi umuman chaqirilmaydi (shuning uchun kirish tekshiruvi — birinchi). Model soddalashtirilgan: haqiqiy Nest Promise va Observable ni ham kutadi va xatoni filtrga uzatadi. Tartib muhimligiga e'tibor bering: guard'lar o'rnini almashtirsangiz, kirmagan foydalanuvchi 401 o'rniga 403 oladi.
Tekshirib ko'ring: Guard foydalanuvchi bloklanganmi tekshiradi va bloklangan bo'lsa
return falseqiladi. Mijoz ilovasi 403 ni "parol noto'g'ri" deb ko'rsatyapti. Nima o'zgartirish kerak?
Javob
false o'rniga aniq xato tashlash: masalan, throw new ForbiddenException("Hisob bloklangan") — status 403 qoladi, lekin xabar aniq. Yoki o'z xato klassingiz bilan Problem Details kod maydoni (hisob-bloklangan) — mijoz ilovasi kodga qarab to'g'ri matn ko'rsatadi. false hech qachon sababni aytmaydi.
3.3 Async guard
Ko'pincha guard javobni darhol bilmaydi: API kalitini omborda qidirish, sessiyani Redis'dan o'qish kerak. Shuning uchun canActivate Promise<boolean> ham qaytara oladi — Nest uni kutadi va keyingi qatlamga faqat javob kelgach o'tadi. Kichik model (brauzerda ishlaydi):
const kalitlar = new Map([["k-ali", { egasi: "Ali" }]]);
const kut = (ms) => new Promise((r) => setTimeout(r, ms));
async function kalitGuard(sarlavhalar) {
const kalit = sarlavhalar["x-api-kalit"];
if (kalit === undefined) {
throw Object.assign(new Error("X-Api-Kalit kerak"),
{ status: 401 });
}
await kut(10); // ombordan qidirish o'rnida
return kalitlar.has(kalit);
}
for (const s of [{}, { "x-api-kalit": "k-vali" },
{ "x-api-kalit": "k-ali" }]) {
try {
const ok = await kalitGuard(s);
console.log(ok
? "200 handler chaqirildi"
: "403 Forbidden resource");
} catch (x) {
console.log(x.status, x.message);
}
}Konsolda:
401 X-Api-Kalit kerak
403 Forbidden resource
200 handler chaqirildiNima bo'ldi? Uchta so'rov — uchta yo'l. Kalit yo'q — xato, 401: "o'zingizni tanishtiring". Kalit bor, lekin noma'lum — false, Nest o'zi 403 qiladi. Kalit to'g'ri — guard true va so'rov davom etadi. Eng muhimi — await: javob kelmaguncha handler chaqirilmaydi.
Tekshirib ko'ring: Dasturchi async guard'da
awaitni unutdi:return this.ombor.bormi(kalit);— u yerdabormiPromise qaytaradi vacanActivatehamasyncemas. Kalit noto'g'ri bo'lsa nima bo'ladi?
Javob
Hech narsa buzilmaydi: canActivate Promise qaytardi, Nest uni kutadi va false ni oladi — 403. await siz ham ishlaydi, chunki Promise qaytarish ruxsat etilgan. Xavfli holat boshqa: if (this.ombor.bormi(kalit)) return true; — bu yerda Promise obyekti doim truthy, shart doim bajariladi va hamma kiradi. Promise'ni if ichida await siz tekshirmang.
Bitta ogohlantirish: async guard har so'rovda ishlaydi. Ichida sekin so'rov bo'lsa (masalan, 50 ms), har marshrut shuncha sekinlashadi. Shuning uchun guard'dagi qidiruvlar tez manbadan (kesh, indeksli jadval) bo'lsin.
3.4 Guard keyingi qatlamlarga ma'lumot qoldiradi
Auth guard kimligingizni aniqlagach, bu ma'lumot handler'ga ham kerak. Odatiy yo'l — guard uni so'rov obyektiga yozadi: req.foydalanuvchi = topilgan. Keyin interceptor, pipe va handler shu maydonni o'qiydi. Handler'da @Req() o'rniga qulay @JoriyFoydalanuvchi() dekoratorini yozish — Custom dekoratorlar darsida.
Faqat bir qoida: guard req ga server aniqlagan narsani yozadi (sessiya, tekshirilgan token). Mijoz yuborgan tana yoki sarlavhani "foydalanuvchi" deb yozish — mijozga ishonish.
4. Metama'lumot bilan guard
4.1 Naqsh
Yuqoridagi KalitGuard — Nest guard'larining eng ko'p uchraydigan naqshi:
flowchart TB
D["@KalitKerak()<br/>SetMetadata(KALIT, true)"] --> M["handler metama'lumoti"]
R["so'rov"] --> G["KalitGuard.canActivate"]
G --> L["reflector.get(KALIT, getHandler())"]
M --> L
L --> Q{"belgi bormi?"}
Q -- "yo'q" --> T["true — o'tadi"]
Q -- "bor" --> C{"kalit to'g'rimi?"}
C -- "ha" --> T
C -- "yo'q" --> X["401 xato"]Ikki qism: dekorator marshrutga belgi qo'yadi (ilova ishga tushganda, bir marta), guard har so'rovda belgini o'qib qaror qiladi. Belgisiz marshrut — tekshirilmaydi. Guard global bo'lsa ham, ishni faqat belgili joyda qiladi.
Controller va metod belgilarini birlashtirish kerak bo'lsa, Reflector darsidagi getAllAndOverride (metod belgisi controller'nikini bosadi) va getAllAndMerge (ikkalasi qo'shiladi) ishlatiladi: this.reflector.getAllAndOverride(ROLLAR, [k.getHandler(), k.getClass()]).
4.2 Reflector.createDecorator tur tuzog'i
NestJS'da metama'lumot dekoratorining yangiroq yo'li bor — Reflector.createDecorator<T>(). U kalit satrini o'zi yaratadi va turni saqlaydi. Reflector darsida ko'rgan edingiz. Lekin uning get() turi bitta kamchilikka ega: u T qaytaradi deydi, undefined siz. Belgisiz handler'da esa ish vaqtida undefined keladi.
Kanon JsonTanaGuard ni dastlab shunday yozgan. Biz o'sha variantni kanon nusxasida qayta yozib, ESLint'ni yurgizdik (biz ishga tushirdik; typescript-eslint strictTypeChecked sozlamasi):
export const JsonTana = Reflector.createDecorator<true>();
// guard ichida:
const kerak = this.reflector.get(JsonTana, kontekst.getHandler());
if (kerak) {
jsonTanaKerak(kontekst.switchToHttp().getRequest<Request>());
}src\umumiy\json-tana.guard.ts
15:9 error Unnecessary conditional, value is always truthy @typescript-eslint/no-unnecessary-condition
✖ 1 problem (1 error, 0 warnings)(Fayl yo'lining boshini — loyiha papkasigacha — olib tashladik.) Tarjimasi: "keraksiz shart: qiymat doim truthy". Linter kerak ning turini true deb biladi — demak if (kerak) doim bajariladi, shart ortiqcha. Aslida esa GET handler'ida belgi yo'q va kerak — undefined. Linter noto'g'ri tur asosida to'g'ri kodni xato deydi.
Bu xavfli ham: linter maslahatiga ergashib if ni olib tashlasangiz, guard hamma marshrutda 415 tekshiradi — GET so'rovlari ham "Content-Type: application/json bo'lishi kerak" oladi. Kanon shuning uchun SetMetadata + get<boolean | undefined>(...) ga qaytdi — tur haqiqatni aytadi.
5. Guard'lar tartibi
5.1 To'rt daraja
Guard global, controller va metod darajasida ulanadi. Kanon tajribasidagi lifecycle'da hammasi yoqilgan (kurs tajribasi, NestJS 12.2.0, Vitest ichida):
1. middleware: global (app.use)
2. middleware: modul (MiddlewareConsumer)
3. guard: global (APP_GUARD)
4. guard: global (useGlobalGuards)
5. guard: controller
6. guard: route
7. interceptor (oldin): global (APP_INTERCEPTOR)Tartib: global → controller → metod, bir daraja ichida — yozilgan tartibda (@UseGuards(A, B) — avval A). Va hammasi middleware'dan keyin, interceptor'lardan oldin.
Guard rad etganda nima bo'ladi — o'sha tajribaning to'rtinchi holati:
4) guard rad etdi POST /t → 418 {"filtr":"controller"}
1. middleware: global (app.use)
2. middleware: modul (MiddlewareConsumer)
3. guard: global (APP_GUARD)
4. guard: global (useGlobalGuards)
5. guard: controller
6. guard: route (rad)
7. filter: controller ← Forbidden resource
8. javob ketdi: 418Metod guard'i rad etdi (false) — interceptor'lar, pipe'lar va handler umuman ishlamadi, xato to'g'ridan filtrga ketdi (tajribada filtr 418 qaytaradi — qaysi filtr ushlaganini ko'rish uchun). Bu tartib — o'tgan darslardagi 415 muammosining yechimi: guard pipe'dan oldin, shuning uchun 415 validatsiyadan (422) oldin.
5.2 Global guard va DI
Global guard ikki yo'l bilan ulanadi: app.useGlobalGuards(new G()) va modulda { provide: APP_GUARD, useClass: G }. JsonTanaGuard constructor'ida Reflector bor — uni DI beradi. new JsonTanaGuard(...) ni qo'lda yozsangiz, Reflector ni ham o'zingiz topib berishingiz kerak. Farq va tuzoqlar — Global enhancer'lar va bajarilish tartibi darsida haqiqiy chiqish bilan.
5.3 Guard xatosini kim ko'radi
Rad etish chiqishiga yana bir bor qarang: guard'dan keyin to'g'ridan filter qatori keldi, interceptor qatorlari yo'q. Bu muhim oqibat beradi. Interceptor'da yozilgan vaqt o'lchovi yoki so'rov logi guard rad etgan so'rovlarni ko'rmaydi. "Nechta 401 bo'ldi?" degan savolga javobni interceptor'dan emas, middleware darajasidagi so'rov logidan (pino-http) yoki xato filtridan oling.
Ikkinchi oqibat: guard tashlagan xato qaysi formatda chiqishini filtr hal qiladi. vazifalar-api da bu — Problem Details filtri. Shuning uchun JsonTanaGuard ning 415 javobi ham boshqa xatolar kabi application/problem+json bo'ladi.
6. Hujumchi nigohi
- Guard'siz yangi marshrut. Guard faqat
@UseGuardsqo'yilgan joyda bo'lsa, yangi controller qo'shgan dasturchi uni unutadi — marshrut ochiq. Himoya — sukut bo'yicha yopiq. Global guard hamma marshrutni tekshiradi, ochiq marshrutlar esa@Ochiq()kabi belgi bilan ochiladi.vazifalar-apining auth qismi Nest'ga ko'chganda aynan shu naqsh ishlatiladi (Global auth guard, @Public va @Roles). - Belgini o'qishdagi xato.
reflector.get(KALIT, k.getClass())— controller'dan o'qiydi, handler'dan emas; metod belgisi e'tiborsiz qoladi. Himoya —getAllAndOverride([getHandler(), getClass()])va har belgili/belgisiz marshrut uchun test. - 403 orqali ma'lumot oshkor qilish. "Bunday vazifa yo'q" (404) va "bu sizning vazifangiz emas" (403) — hujumchi qaysi ID'lar mavjudligini bilib oladi. Himoya — begona resursga ham 404 qaytarish (28-qismdagi IDOR qoidasi).
- Guard'ga tana tekshiruvini qo'yish. Guard pipe'dan oldin —
req.bodyhali tekshirilmagan. Guard ichidareq.body.rol === "admin"ga ishonish — mijozga ishonish. Himoya — guard faqat server biladigan narsaga (sessiya, token, metama'lumot) tayanadi.
7. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
403 Forbidden resource, sabab noma'lum |
guard false qaytardi |
aniq xato tashlang |
Guard'dagi reflector — undefined |
useGlobalGuards(new G()) — DI yo'q |
APP_GUARD |
| Metod belgisi ishlamaydi | get(..., getClass()) |
getHandler() yoki getAllAndOverride |
| Lint "Unnecessary conditional" | createDecorator<T> turi |
SetMetadata + get<T | undefined> |
Guard'da req.body tekshirilmagan |
guard pipe'dan oldin | tana — pipe'da |
| Yangi marshrut himoyasiz | guard lokal ulangan | global guard + ochish belgisi |
8. Mashqlar
Mashqlar kurs/mashqlar/30/22-guardlar/ da.
1-mashq (oson): to'ldiring
- Guard bajaradigan interfeys —
- Guard
falseqaytarsa status — - Express so'rovini olish —
kontekst.().getRequest() - Chaqiriladigan metod —
kontekst.() - Metod belgisi controller'nikini bossin —
reflector.(KALIT, [h, c])
Yechim
- CanActivate —
canActivate(context): boolean, Promise yoki Observable. - 403 —
ForbiddenException("Forbidden resource"). - switchToHttp — HTTP ko'rinishi; WebSocket uchun
switchToWs, mikroservis uchunswitchToRpc. - getHandler — metama'lumot shu funksiyada.
- getAllAndOverride — birinchi topilgan qiymat;
getAllAndMerge— birlashtirilgan.
2-mashq (o'rta): metama'lumotli guard modeli
Belgilar Map da saqlanadi: kalit — handler funksiyasi, qiymat — { rollar: [...] }. rolGuard(belgilar, handler, foydalanuvchi) funksiyasini yozing: belgi yo'q — true; belgi bor, foydalanuvchi yo'q — 401 xato; rol mos kelmasa — false; mos kelsa — true. Natijani darsdagi guardlar zanjiriga o'xshab chiqaring. Ishora: Map (Map), includes.
Yechim
const belgilar = new Map();
function ochiqRoyxat() { return "ro'yxat"; }
function ochir() { return "o'chirildi"; }
belgilar.set(ochir, { rollar: ["admin"] });
function rolGuard(handler, foydalanuvchi) {
const belgi = belgilar.get(handler);
if (belgi === undefined) return true;
if (foydalanuvchi === undefined) {
throw Object.assign(new Error("kirish kerak"), { status: 401 });
}
return belgi.rollar.includes(foydalanuvchi.rol);
}
function sorov(handler, foydalanuvchi) {
try {
if (!rolGuard(handler, foydalanuvchi)) {
return "403 Forbidden resource";
}
return `200 ${handler()}`;
} catch (x) {
return `${x.status} ${x.message}`;
}
}
console.log(sorov(ochiqRoyxat, undefined));
console.log(sorov(ochir, undefined));
console.log(sorov(ochir, { rol: "user" }));
console.log(sorov(ochir, { rol: "admin" }));Konsolda:
200 ro'yxat
401 kirish kerak
403 Forbidden resource
200 o'chirildiMap kaliti — funksiyaning o'zi (havola), nomi emas. Nest'da ham metama'lumot handler funksiyasiga biriktiriladi — shuning uchun getHandler() funksiyani qaytaradi, satrni emas.
3-mashq (qiyin): getAllAndOverride va getAllAndMerge
Ikki daraja belgi: controller — ["user"], ba'zi metodlarda — o'z belgisi. override(metod, klass) — avval metod belgisi, bo'lmasa klassniki, bo'lmasa undefined. merge(metod, klass) — ikkalasini birlashtirib, takrorsiz massiv (bo'lmasa — bo'sh massiv). Uch metod bilan sinang: belgisiz, ["admin"] belgili va ["user"] belgili. Ishora: ??, spread va Set (Set).
Yechim
const metodBelgi = new Map([["b", ["admin"]], ["c", ["user"]]]);
const klassBelgi = ["user"];
function override(metod) {
return metodBelgi.get(metod) ?? klassBelgi ?? undefined;
}
function merge(metod) {
const hammasi = [...(metodBelgi.get(metod) ?? []), ...klassBelgi];
return [...new Set(hammasi)];
}
for (const m of ["a", "b", "c"]) {
const o = JSON.stringify(override(m));
console.log(m, o, JSON.stringify(merge(m)));
}Konsolda:
a ["user"] ["user"]
b ["admin"] ["admin","user"]
c ["user"] ["user"]Reflector darsidagi kanon tajribasi (VController.a va b) bilan solishtiring: b uchun getAllAndOverride — ["admin"], getAllAndMerge — ["admin","foydalanuvchi"] — xuddi shu mantiq. Qaysi birini tanlash — siyosatga bog'liq: "metod qoidasi controller'nikini almashtiradi" yoki "qo'shadi". Rollar uchun ko'pincha override: @Rollar("admin") — "faqat admin", "admin yoki user" emas.
9. Loyiha qadami: JsonTanaGuard ni tekshirish
Bu darsda vazifalar-api kodi o'zgarmaydi: guard Javobni serializatsiya qilish darsida qo'shilgan. O'tgan darsdagi middleware o'zgarishlari esa hali commit qilinmagan — ular shu bosqichning bir qismi.
Guard'ning qatorlarini darsdagi tushunchalar bilan qayta o'qing:
| Qator | Tushuncha |
|---|---|
JsonTana = () => SetMetadata(JSON_TANA, true) |
metama'lumot belgisi («Naqsh» bo'limi) |
this.reflector.get<boolean | undefined>(JSON_TANA, kontekst.getHandler()) |
handler belgisini o'qish, haqiqiy tur |
kontekst.switchToHttp().getRequest<Request>() |
Express so'rovi |
jsonTanaKerak(req) → IlovaXatosi(415) |
rad etish — xato tashlash, false emas |
return true |
belgisiz yoki to'g'ri so'rov o'tadi |
{ provide: APP_GUARD, useClass: JsonTanaGuard } |
global, DI bilan (Reflector) |
Kanon testlari guard'ni ikki tomondan tekshiradi: text/plain POST — 415 (api.test, xato.test), oddiy JSON POST va GET — o'tadi (qolgan hamma testlar). O'zingiz tekshiring:
npm run tip && npm run lint && npm testPostgres'siz yurgizganda kutilgan natija — Tests 75 passed | 6 skipped (81). Ixtiyoriy tajriba: guard faylida SetMetadata ni Reflector.createDecorator<true>() ga almashtirib, npm run lint ni ishga tushiring — «Reflector.createDecorator tur tuzog'i» bo'limidagi xatoni o'z ko'zingiz bilan ko'rasiz. Keyin o'zgarishni qaytaring (git checkout src/umumiy/json-tana.guard.ts).
Hali commit qilmang — keyingi to'rt darsda shu bosqichga interceptor, filtr va dekoratorlar qo'shiladi.
10. Real ishda
- Auth guard — Nest loyihalaridagi eng mashhur guard: sessiya yoki token tekshiruvi, keyin
@Roles(...)bilan rol guard'i. Passport'ningAuthGuard("jwt")ham — oddiy guard (Passport NestJS'da). - Boshqa guard'lar: tezlik cheklovi (
@nestjs/throttler— ThrottlerGuard), CAPTCHA, IP ruxsat ro'yxati, "funksiya bayrog'i yoqilganmi". - Tanlov qoidasi: qoida marshrutga yoki uning dekoratoriga bog'liq bo'lsa — guard; hamma so'rovga bir xil bo'lsa va 404 ga ham kerak bo'lsa — middleware. Xavfsizlik qarori ("kirishi mumkinmi") deyarli doim guard'da.
- Intervyu: "guard va middleware farqi?", "guard
falseqaytarsa nima bo'ladi?", "guard'da qanday qilib marshrut metama'lumotini o'qiysiz?".
Xulosa
- Guard —
CanActivate.canActivate(kontekst):true— o'tadi,false— 403, xato — o'sha xato (401, 415…). ExecutionContext:switchToHttp().getRequest(),getHandler(),getClass(),getType().- Naqsh: dekorator (
SetMetadata) belgi qo'yadi, guardReflectorbilan o'qiydi;createDecorator<T>ningget()turiundefinedni ko'rsatmaydi. - Tartib: middleware → guard (global → controller → metod) → interceptor → pipe → handler; rad etilsa — to'g'ridan filtrga.
- Xavfsizlik guard'lari — global va "sukut bo'yicha yopiq".
- Guard async bo'lishi mumkin (
Promise<boolean>) — Nest javobni kutadi; ichidagi qidiruv tez bo'lsin. Aniqlangan foydalanuvchini guardreqga yozadi, keyingi qatlamlar o'qiydi.
Keyingi dars: Interceptor'lar va RxJS — handler'ni ikki tomondan o'rash: javob konverti, vaqt o'lchash, timeout va vazifalar-api ning ETag sarlavhalari interceptor'da.
Manbalar
- NestJS hujjati — Guards (
CanActivate,ExecutionContext,Reflector): https://docs.nestjs.com/guards - NestJS hujjati — Execution context: https://docs.nestjs.com/fundamentals/execution-context
- typescript-eslint —
no-unnecessary-condition: https://typescript-eslint.io/rules/no-unnecessary-condition/ - Kanon (IlmHamroh, 2026-10-27): lifecycle tartibi (NestJS 12.2.0, Vitest ichida),
JsonTanaGuardvaReflector.createDecoratortopilmasi. Demo guard'lar vacreateDecoratorvarianti bilan lint — biz ishga tushirdik (2026-11-02, NestJS 12.2.0, typescript-eslintstrictTypeChecked, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!