Mundarija (33)
- Bu darsda
- 1. Nega bu kerak?
- 2. cookies() va headers() — so'rovni o'qish
- 2.1 Async funksiyalar
- 2.2 Haqiqiy tajriba: cookie sahifani qanday o'zgartiradi
- 3. Cookie yozish — faqat action va handler'da
- 3.1 Haqiqiy xato
- 3.2 To'g'ri joy: Route Handler
- 4. headers() va IP manzil
- 4.1 Kanondagi ishlatilishi
- 4.2 Hujumchi nigohi: sarlavhaga ishonish
- 5. draftMode() — qoralamani ko'rish
- 5.1 Muammo
- 5.2 Tajriba
- 6. after() — javobdan keyin
- 6.1 G'oya
- 6.2 portfolio dagi after()
- 6.3 Qoidalar
- 7. connection() — "so'rovni kut"
- 8. Umumiy jadval
- 9. Portfolio qadami: after() va commit
- 9.1 O'zgarish
- 9.2 Tekshiruv
- 9.3 Commit
- 10. Ko'p uchraydigan xatolar
- 11. Mashqlar
- 1-mashq (oson): qayerda mumkin?
- 2-mashq (o'rta): Set-Cookie satrini yasang
- 3-mashq (qiyin): ishonchli IP
- 4-mashq: Portfolio qadami
- 12. Real ishda
- Xulosa
- Manbalar
Next.js request API'lari: cookies(), headers(), draftMode() va after()
Qisqacha:
cookies()vaheaders()— kelgan so'rovni o'qiydigan async funksiyalar (await cookies()). O'qish istalgan server kodida mumkin, lekin cookie yozish faqat Server Action va Route Handler'da: sahifa oqimi boshlangach, javob sarlavhasini o'zgartirib bo'lmaydi. Bu funksiyalar sahifani statikdan dinamikka o'tkazadi, Cache Components'da esa<Suspense>ichida bo'lishi shart.draftMode()— CMS qoralamasini ko'rish rejimi.after()— javob ketgandan keyin bajariladigan ish: log, analitika.
Bu darsda
cookies()vaheaders()bilan so'rovni o'qiysiz va ular sahifani nega dinamik qilishini build jadvalida ko'rasiz.- Cookie nega faqat Server Action yoki Route Handler'da yozilishini haqiqiy xato bilan tushuntirasiz.
draftMode()bilan statik sahifani faqat muharrir uchun "jonli" qilishni ko'rasiz.after()bilanportfolioning aloqa formasiga spam loglarini qo'shasiz — foydalanuvchini kutdirmasdan.connection()qachon kerakligini bilasiz.
Oldin bilishingiz kerak: proxy.ts, Route Handlers, Server Actions xavfsizligi, Streaming va Suspense, Cookie'lar JS'dan.
1. Nega bu kerak?
Oldingi darsda proxy so'rovni eshikda ushladi. Lekin ba'zan so'rov ma'lumoti sahifa yoki action'ning o'zida kerak:
- aloqa formasi: tezlik cheklovi uchun yuboruvchining IP manzili;
- sayt mavzusi: foydalanuvchi qorong'i rejimni tanlagan bo'lsa, sahifani darhol qorong'i chizish;
- blog: muharrir hali chiqmagan postni oldindan ko'rishi;
- log: "spam bot ushlandi" yozuvi — lekin foydalanuvchi bu yozuvni kutib turmasligi kerak.
Next buning uchun kichik funksiyalar to'plamini beradi. Ularning hammasi bitta savolga bog'liq: bu ma'lumot qachon ma'lum? Build vaqtidami (statik sahifa) yoki faqat so'rov kelganda?
Hayotiy o'xshatish: bosmaxona va kafe. Gazeta (statik sahifa) tunda bosiladi — hamma uchun bir xil, ertalab tayyor. Kafedagi buyurtma (dinamik) esa mijoz kelganda tayyorlanadi: u nima istashini oldindan bilib bo'lmaydi. cookies() va headers() — mijozdan so'rash. Ularni chaqirsangiz, sahifa "gazeta" bo'lolmay qoladi.
2. cookies() va headers() — so'rovni o'qish
2.1 Async funksiyalar
Ikkalasi next/headers dan keladi va Promise qaytaradi:
import { cookies, headers } from "next/headers";
export default async function Sahifa() {
const tema = (await cookies()).get("tema")?.value;
const til = (await headers()).get("accept-language");
return <p>{tema} · {til}</p>;
}| Funksiya | Qaytaradi | Asosiy metodlar |
|---|---|---|
cookies() |
so'rov cookie'lari | get, getAll, has; action/handler'da set, delete |
headers() |
so'rov sarlavhalari (faqat o'qish) | get, has, entries |
Next 15 gacha (14 va undan oldin) bu funksiyalar sinxron edi (cookies().get(...)). Next 15 dan async — eski kodda await siz ko'rsangiz, bu eski uslub. Sabab — params ning Promise bo'lgani bilan bir xil: Next sahifaning statik qismini so'rov ma'lumotisiz oldindan tayyorlay olishi uchun.
Cookie — brauzer har so'rovda Cookie sarlavhasida qaytarib yuboradigan kichik nom=qiymat yozuv (HTTP sarlavhalari va cookie). cookies() shu sarlavhani o'qib, qulay obyektga aylantiradi. Ichida nima bo'layotganini oddiy JS'da ko'raylik:
function cookieOqi(sarlavha) {
const natija = {};
for (const juft of sarlavha.split(";")) {
const [nom, ...qolgan] = juft.trim().split("=");
if (nom) natija[nom] = decodeURIComponent(qolgan.join("="));
}
return natija;
}
const c = cookieOqi("tema=qorongi; til=uz; savat=osh%2Cmanti");
console.log(c.tema); // qorongi
console.log(c.savat); // osh,manti
console.log(c.yoq ?? "yo'q"); // yo'q%2C — vergulning kodlangan ko'rinishi: cookie qiymatida maxsus belgilar shunday yoziladi. cookies().get("tema") ham aslida shu ishni qiladi, faqat tayyor va to'liq.
2.2 Haqiqiy tajriba: cookie sahifani qanday o'zgartiradi
Kanonda cookies() ishlatilmaydi. Shuning uchun biz portfolio (holat 32) nusxasida kichik tajriba sahifasini qo'shdik — /tajriba/cookie (biz ishga tushirdik, next 16.4.0). Birinchi urinish — eng oddiy kod:
import { cookies } from "next/headers";
export default async function CookieSahifa() {
const tema = (await cookies()).get("tema")?.value ?? "yo'q";
return (
<main id="asosiy">
<h1>Tema cookie: {tema}</h1>
</main>
);
}next build — yiqildi (stek izi qisqartirildi):
Error: Route "/tajriba/cookie": Next.js encountered uncached or runtime data during prerendering.
`fetch(...)`, `cookies()`, `headers()`, `params`, `searchParams`, or `connection()` accessed outside of `<Suspense>` prevents the route from being prerendered, blocking the page load and leading to a slower user experience.
Ways to fix this:
- [stream] Provide a placeholder with `<Suspense fallback={...}>` around the data access
- [cache] For uncached data (`fetch`, database calls): cache the access with `"use cache"` (does not apply to `connection()`)
- [block] Set `export const instant = false` to allow a blocking routeTarjimasi: "Next prerender paytida keshlanmagan yoki so'rov vaqtidagi ma'lumotga duch keldi. cookies() va boshqalar <Suspense> dan tashqarida chaqirilsa, sahifani oldindan tayyorlab bo'lmaydi — sahifa yuklanishi bloklanadi". Xato uchta yo'l taklif qiladi: stream (Suspense bilan o'rash), cache (cookie uchun mos emas — u har foydalanuvchida boshqa), block (butun sahifani dinamik qilish).
Bu — Cache Components qoidasi. Next sahifaning iloji boricha katta qismini statik qobiq qilib tayyorlamoqchi. So'rovga bog'liq qism esa alohida "orol" bo'lishi kerak. To'g'ri variant:
import { cookies } from "next/headers";
import { Suspense } from "react";
async function TemaYozuvi() {
const tema = (await cookies()).get("tema")?.value ?? "yo'q";
return <p>Tema cookie: {tema}</p>;
}
export default function CookieSahifa() {
return (
<main id="asosiy">
<h1>Cookie tajribasi</h1>
<Suspense fallback={<p>Yuklanmoqda…</p>}>
<TemaYozuvi />
</Suspense>
</main>
);
}Endi build o'tdi. Jadvalda:
├ ◐ /tajriba/cookie◐ — Partial Prerender: sarlavha va «Yuklanmoqda…» build'da tayyor, cookie qismi esa har so'rovda oqim bilan keladi. curl bilan ikki marta so'radik — cookie'siz va Cookie: tema=qorongi bilan (HTML'dan ko'chirma):
Tema cookie: <!-- -->yo'q
Tema cookie: <!-- -->qorongi<!-- --> — React matn bo'laklari orasiga qo'yadigan belgi, ' — apostrof. Ya'ni bir xil URL, ikki xil javob — cookie'ga qarab.
Tekshirib ko'ring:
portfolioda mavzu (yorug'/qorong'i)localStorageda saqlanadi (Hydration darsi). Uni cookie'ga ko'chirsak, server sahifani darhol to'g'ri rangda chiza olardi. Kanon nega buni qilmadi?
Javob
Cookie'ni o'qigan har sahifa dinamik bo'lib qoladi: hozir hamma sahifa ○ (build'da tayyor, TTFB 4–12 ms), cookie bilan esa root layout'dagi mavzu hamma joyda ◐ yoki ƒ ga aylanardi. Kanon boshqa yo'lni tanladi: <head> dagi kichik skript localStorage ni hydration'dan oldin o'qiydi, sahifalar esa statik qoladi. Narx — bitta inline skript; foyda — CDN keshida turadigan butun sayt.
3. Cookie yozish — faqat action va handler'da
3.1 Haqiqiy xato
Endi sahifaning o'zida cookie yozishga urinamiz (yana <Suspense> ichida):
async function TemaniYoz() {
(await cookies()).set("tema", "qorongi");
return <p>Yozildi</p>;
}Build o'tdi (◐ /tajriba/cookie-yoz). Lekin next start da sahifani ochganimizda curl 200 oldi, server logida esa:
⨯ Error: Cookies can only be modified in a Server Action or Route Handler. Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#optionsTarjimasi: "Cookie'larni faqat Server Action yoki Route Handler'da o'zgartirish mumkin".
Nega? Cookie brauzerga Set-Cookie javob sarlavhasi bilan yetadi. Sarlavhalar esa javobning eng boshida, matndan oldin ketadi. Sahifa oqim bilan yuborilganda (statik qobiq darhol, <Suspense> ichi keyin) — sarlavhalar allaqachon jo'natilgan. Hujjat ham shuni yozadi: HTTP oqim boshlangandan keyin cookie o'rnatishga ruxsat bermaydi.
Status 200 bo'lgani ham saboq: xato oqim o'rtasida bo'ldi, status esa avval ketgan. Bu proxy darsidagi 404 muammosining egizagi.
sequenceDiagram
participant B as Brauzer
participant S as Next server
B->>S: GET /sahifa (Cookie: tema=...)
S-->>B: 200 + sarlavhalar + statik qobiq
Note over S: Suspense ichi render bo'ladi
S-->>B: qolgan HTML (oqim)
Note over S: bu yerda Set-Cookie — kech3.2 To'g'ri joy: Route Handler
Xuddi shu cookie'ni Route Handler'da yozdik:
import { cookies } from "next/headers";
export async function POST() {
const c = await cookies();
c.set("tema", "qorongi", {
httpOnly: true,
sameSite: "lax",
path: "/",
maxAge: 60 * 60 * 24 * 365,
});
return Response.json({ ok: true });
}Javob sarlavhalari (curl -i -X POST, biz ishga tushirdik):
HTTP/1.1 200 OK
set-cookie: tema=qorongi; Path=/; Expires=Thu, 21 Oct 2027 05:25:53 GMT; Max-Age=31536000; HttpOnly; SameSite=lax
{"ok":true}Variantlar Set-Cookie atributlariga aylandi:
| Variant | Atribut | Ma'nosi |
|---|---|---|
httpOnly: true |
HttpOnly |
brauzer JS'i (document.cookie) o'qiy olmaydi |
sameSite: "lax" |
SameSite=lax |
boshqa saytdan POST'da yuborilmaydi (CSRF'ga qarshi) |
maxAge |
Max-Age, Expires |
1 yil yashaydi (soniyalarda) |
path: "/" |
Path=/ |
butun saytda amal qiladi |
Server Action'da ham xuddi shunday yoziladi — u ham alohida POST javobi. Cookie atributlarining to'liq tahlili (Secure, Domain, __Host- prefiksi) — Cookie chuqur darsida.
Tekshirib ko'ring: Tema tugmasi bosilganda cookie yozilishi kerak. Tugma client komponentda. Cookie'ni qayerda yozasiz?
Javob
Server Action'da: tugma onClick da (yoki forma orqali) temaniSaqla("qorongi") action'ini chaqiradi, action ichida (await cookies()).set(...). Action — alohida POST so'rov, uning javobida Set-Cookie bor. Client komponentning o'zi esa document.cookie bilan ham yoza oladi, lekin u holda httpOnly bo'lmaydi.
4. headers() va IP manzil
4.1 Kanondagi ishlatilishi
headers() ni siz allaqachon ishlatgansiz — Server Actions xavfsizligi darsida, tezlik cheklovi uchun:
const ip =
(await headers()).get("x-forwarded-for")?.split(",")[0]?.trim() ?? "lokal";x-forwarded-for — teskari proksi (nginx, Vercel) qo'shadigan sarlavha: "bu so'rov aslida qaysi IP'dan keldi". Bir nechta proksi bo'lsa, ro'yxat vergul bilan: birinchi element — eng chetdagi mijoz.
Bu mantiqni alohida sinab ko'ramiz:
function ipniOl(sarlavha) {
return sarlavha?.split(",")[0]?.trim() ?? "lokal";
}
console.log(ipniOl("203.0.113.5")); // 203.0.113.5
console.log(ipniOl("203.0.113.5, 10.0.0.2")); // 203.0.113.5
console.log(ipniOl(null)); // lokal?. — sarlavha yo'q bo'lsa xato bermasdan undefined, ?? — u holda "lokal" (?? va ?.). 203.0.113.x — hujjatlar uchun ajratilgan namuna manzil.
4.2 Hujumchi nigohi: sarlavhaga ishonish
x-forwarded-for — oddiy sarlavha. Uni brauzer emas, har kim yubora oladi:
curl -H "X-Forwarded-For: 1.2.3.4" https://example.com/aloqaAgar saytingiz oldida sarlavhani qayta yozadigan proksi bo'lmasa, hujumchi har so'rovda boshqa "IP" yuborib, tezlik cheklovini aylanib o'tadi. Yechim — kod emas, infratuzilma. Ishonchli proksi (Vercel, to'g'ri sozlangan nginx) bu sarlavhani o'zi qayta yozadi yoki haqiqiy manzilni ro'yxat oxiriga qo'shadi. Kod esa faqat ishonchli qismni o'qiydi (3-mashqda). portfolio ning cheklovi — birinchi qatlam, to'liq himoya emas. Teskari proksi ortida Node qanday ishlashini Reverse proxy orqasida Node darsida batafsil ko'ramiz.
Qoida: headers() dagi har qiymat — foydalanuvchi kiritgan ma'lumot kabi. Unga ishonishdan oldin kim qo'yganini biling.
5. draftMode() — qoralamani ko'rish
5.1 Muammo
CMS (Content Management System, "kontent boshqaruv tizimi") — maqolalarni kodsiz yozadigan admin panel: Sanity, Contentful, Strapi. Sayt CMS'dan postlarni olib, statik sahifa yasaydi. Muharrir yangi postni yozdi, lekin hali "nashr" bosmadi. U sahifa saytda qanday ko'rinishini ko'rmoqchi. Statik sahifa esa build'da yasalgan — unda qoralama yo'q.
Draft mode (qoralama rejimi) — shu muammoning yechimi: maxsus cookie bor bo'lsa, statik sahifa shu foydalanuvchi uchun so'rov vaqtida qayta render bo'ladi va qoralama ma'lumotini ko'rsatadi. Qolganlar uchun sahifa statik qoladi.
5.2 Tajriba
Ikki fayl qo'shdik: rejimni yoqadigan Route Handler va holatni ko'rsatadigan sahifa.
// app/api/tajriba/qoralama/route.ts
import { draftMode } from "next/headers";
export async function GET() {
(await draftMode()).enable();
return new Response("Qoralama yoqildi");
}// sahifa ichida (Suspense ichida)
const { isEnabled } = await draftMode();
return <p>Qoralama rejimi: {isEnabled ? "yoqilgan" : "o'chiq"}</p>;Build jadvalida sahifa — ○ statik (draftMode uni dinamik qilmadi). Uch so'rov (biz ishga tushirdik):
## 5) qoralama: oddiy
HTTP/1.1 200 OK
x-nextjs-cache: HIT
Cache-Control: s-maxage=31536000
Qoralama rejimi: <!-- -->o'chiq
## 6) qoralamani yoqish
HTTP/1.1 200 OK
set-cookie: __prerender_bypass=5e6eccc673b33a1e643aac4235052172; Path=/; Secure; HttpOnly; SameSite=none
Qoralama yoqildi
## 7) qoralama cookie bilan
HTTP/1.1 200 OK
Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate
x-nextjs-cache: HIT
Qoralama rejimi: <!-- -->yoqilganO'qing:
- Oddiy tashrif — keshdan (
HIT), bir yil keshlanadi, "o'chiq". enable()—__prerender_bypasscookie'sini o'rnatadi. Uning qiymati harnext buildda yangi — taxmin qilib bo'lmaydi.- Shu cookie bilan — xuddi o'sha sahifa endi
private, no-storeva "yoqilgan": statik nusxa chetlab o'tildi.
Haqiqiy loyihada CMS "Ko'rish" tugmasi /api/qoralama?secret=...&slug=... ni ochadi, handler sirni tekshirib enable() qiladi va postga redirect qiladi. Sahifa isEnabled bo'lsa CMS'dan qoralama versiyasini oladi. portfolio da CMS yo'q (postlar — Markdown fayllar), shuning uchun kanonda draftMode ishlatilmagan.
Diqqat:
Secure; SameSite=none— cookie faqat HTTPS orqali yuboriladi. Lokalhttp://localhostda brauzer uni saqlamasligi mumkin; hujjat lokal sinash uchun uchinchi tomon cookie'lariga ruxsat berishni aytadi. Bizcurlda cookie'ni qo'lda yubordik.
6. after() — javobdan keyin
6.1 G'oya
Aloqa formasiga bot yozdi. Server buni sezdi (asal idish to'lgan) va logga yozmoqchi. Lekin log yozish — foydalanuvchi uchun keraksiz kutish. Javobni darhol qaytarib, logni keyin yozsa bo'ladimi?
after(callback) (next/server dan) aynan shuni qiladi: callback javob (yoki prerender) tugagandan keyin bajariladi. Hujjat bo'yicha u Server Component, Server Action, Route Handler va proxy'da ishlaydi.
G'oyani oddiy JS modeli bilan ko'raylik. Bu Next kodi emas — o'quv modeli: keyin navbatga qo'yadi, javobBer avval javobni "yuboradi", keyin navbatni bajaradi:
const navbat = [];
const keyin = (ish) => navbat.push(ish);
function xabarYubor(forma) {
if (forma.vebSayt) {
keyin(() => console.log("log: aloqa-spam, asal-idish"));
return "yuborildi";
}
return "qabul";
}
function javobBer(forma) {
const javob = xabarYubor(forma);
console.log(`javob ketdi: ${javob}`);
for (const ish of navbat.splice(0)) ish();
}
javobBer({ vebSayt: "http://spam.example" });
javobBer({ vebSayt: "" });Konsolda:
javob ketdi: yuborildi
log: aloqa-spam, asal-idish
javob ketdi: qabulafter ichidagi ish funksiya ichida yozilgan, lekin javobdan keyin bajarildi. navbat.splice(0) — massivni bo'shatib, elementlarini qaytaradi: har javobdan keyin navbat toza.
6.2 portfolio dagi after()
Kanondagi o'zgarish (xabarYubor.ts, holat 24 → 27):
import { headers } from "next/headers";
+import { after } from "next/server";
…
- if (String(forma.get("veb_sayt") ?? "") !== "")
+ if (String(forma.get("veb_sayt") ?? "") !== "") {
+ // after() — javob ketgandan KEYIN bajariladi (N-27): log foydalanuvchini kutdirmaydi
+ after(() =>
+ console.warn(JSON.stringify({ tur: "aloqa-spam", sabab: "asal-idish" })),
+ );
return { holat: "yuborildi", ism: "" };
+ }
…
if (!ruxsatmi(ip)) {
+ after(() =>
+ console.warn(JSON.stringify({ tur: "aloqa-spam", sabab: "limit" })),
+ );Ikki joy: asal idish to'lganda va tezlik cheklovidan oshganda. Log — JSON qator: keyin loglar va instrumentation darsida aynan shu formatni yig'amiz.
Kanon o'lchovi (natijalar/27-after.txt):
asal idish POST 200, javob tugadi [1792528058635] (162 ms)
server log:
[1792528058635] {"tur":"aloqa-spam","sabab":"asal-idish"}Kvadrat qavsdagi son — millisekundlardagi vaqt belgisi (Date.now()). Javob tugagan va log yozilgan payt — bir xil millisekund: log javobni kutdirmadi, javobdan keyin darhol bajarildi.
6.3 Qoidalar
| Qoida | Hujjatdan |
|---|---|
after sahifani dinamik qilmaydi |
statik sahifada callback build vaqtida ishlaydi |
| Xato bo'lsa ham bajariladi | notFound(), redirect() yoki istisno bo'lsa ham |
Action va handler'da — cookies()/headers() callback ichida mumkin |
so'rov konteksti saqlanadi |
| Server Component'da — callback ichida mumkin emas | ma'lumotni oldindan o'qib, o'zgaruvchi bilan bering |
| Vaqt chegarasi — platformaniki | serverless'da maxDuration |
Birinchi qatorga e'tibor bering: statik sahifadagi after(() => log("ko'rildi")) har tashrifda emas, build paytida bir marta ishlaydi. Sahifa ko'rishlarini sanash uchun bu noto'g'ri joy.
Tekshirib ko'ring:
after()bilan "Rahmat" xatini foydalanuvchiga yuborish yaxshi g'oyami?
Javob
Qisman. Foydalanuvchi xat yuborilishini kutmaydi — bu yaxshi. Lekin after ishonchli navbat emas: server shu payt qayta ishga tushsa yoki serverless funksiya vaqti tugasa, xat yo'qoladi va hech kim bilmaydi. Log va analitika uchun bu yo'qotish arzimas; muhim xat uchun esa haqiqiy navbat (qayta urinish bilan) kerak — backend qismlarida.
7. connection() — "so'rovni kut"
Ba'zan komponent cookie ham, sarlavha ham o'qimaydi, lekin baribir har so'rovda boshqa natija berishi kerak: Math.random(), joriy vaqt. Build vaqtida hisoblansa, hamma bir xil "tasodifiy" sonni ko'rardi. await connection() (next/server) — "prerender shu yerda to'xtasin, qolgani faqat so'rov kelganda".
portfolio buni Serverda ma'lumot olish darsida tajriba qilgan (o'sha yerda batafsil): GitHub so'rovi try/catch ichida bo'lganda prerender rad etishi jimgina "yutilgan" edi. connection() bilan esa nima dinamik ekani aniq belgilandi (natijalar/tajriba/16-build-connection.txt):
├ ◐ /haqimdaKeyinroq kanon GitHub blokini "use cache" ga o'tkazdi va /haqimda yana ○ bo'ldi (1h 1d). Xulosa: connection() — kamdan-kam kerak bo'ladigan aniq vosita. Avval "bu ma'lumotni keshlasa bo'ladimi?" deb so'rang.
8. Umumiy jadval
| API | Qayerdan | Sahifani dinamik qiladimi | Qayerda ishlaydi |
|---|---|---|---|
cookies() |
next/headers |
ha (Suspense ichida) | o'qish — hamma joyda; yozish — action, handler |
headers() |
next/headers |
ha | faqat o'qish |
draftMode() |
next/headers |
yo'q (faqat cookie egasi uchun) | enable() — handler'da |
after() |
next/server |
yo'q | component, action, handler, proxy |
connection() |
next/server |
ha | component |
Yana bir yordamchi: userAgent(request) (next/server) — proxy va handler'da User-Agent sarlavhasini brauzer, qurilma va OS'ga ajratib beradi.
9. Portfolio qadami: after() va commit
9.1 O'zgarish
Bitta fayl — src/app/(sayt)/aloqa/_amal/xabarYubor.ts: «portfolio dagi after()» bo'limidagi diff. Oldingi darsdagi src/proxy.ts bilan birga bu holat 27.
9.2 Tekshiruv
npm run lint
npm run tip
npm run build
npm run startAsal idishni qo'lda sinash: aloqa sahifasida DevTools'da yashirin veb_sayt maydonini topib qiymat yozing va formani yuboring. Ekranda — oddiy "yuborildi" holati, next start terminalida esa:
{"tur":"aloqa-spam","sabab":"asal-idish"}Kanon o'lchovi: JS o'zgarmadi, axe 0, hydration 0; modal oqimi proxy bilan ham ishlaydi (natijalar/27-modal.txt).
9.3 Commit
git add -A
git commitCommit (kanon): Proxy: noma'lum loyiha/post slug'i darhol 404; aloqa spam loglari after() bilan.
10. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Build: encountered uncached or runtime data during prerendering |
cookies()/headers() <Suspense> dan tashqarida |
O'qiydigan qismni alohida komponent + Suspense |
Cookies can only be modified in a Server Action or Route Handler. |
Sahifada cookies().set() |
Action yoki Route Handler'da yozing |
Hamma sahifa ◐/ƒ bo'lib qoldi |
Root layout'da cookies() |
Faqat kerakli sahifada; yoki cookie'siz yechim |
cookies().get is not a function |
Eski sinxron uslub | (await cookies()).get(...) |
| Tezlik cheklovi aylanib o'tiladi | x-forwarded-for ga ko'r-ko'rona ishonch |
Sarlavhani qayta yozadigan ishonchli proksi |
after ichida log har tashrifda emas |
Sahifa statik — callback build'da | Analitika — client yoki dinamik joyda |
| Draft mode lokal ishlamaydi | Cookie Secure; SameSite=none |
HTTPS yoki brauzer sozlamasi |
11. Mashqlar
Mashqlar kurs/mashqlar/23/27-request-api/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): qayerda mumkin?
- Server Component'da cookie o'qish —
- Server Component'da cookie yozish —
- Route Handler'da cookie yozish —
headers()bilan javob sarlavhasini o'zgartirish —
Yechim
- mumkin — brauzer cookie'ni so'rov bilan yuborgan, o'qish xavfsiz (Suspense ichida).
- mumkin emas — oqim boshlangan,
Set-Cookiekech. - mumkin — handler javobini o'zi yaratadi, sarlavha qo'shish mumkin.
- mumkin emas —
headers()faqat so'rov sarlavhalarini o'qiydi. Javob sarlavhasi — Route Handler'ningResponseida, proxy'da yokinext.configheaders()da.
2-mashq (o'rta): Set-Cookie satrini yasang
setCookieYoz(nom, qiymat, variantlar) funksiyasini yozing: { httpOnly, sameSite, maxAge, path } dan Set-Cookie satrini yasasin. Qiymat encodeURIComponent bilan kodlansin. Tartib: nom=qiymat, Path, Max-Age, HttpOnly, SameSite. Satrlarni massivga yig'ib (push), join("; ") bilan birlashtiring.
Yechim
function setCookieYoz(nom, qiymat, v = {}) {
const qismlar = [`${nom}=${encodeURIComponent(qiymat)}`];
if (v.path) qismlar.push(`Path=${v.path}`);
if (v.maxAge !== undefined) qismlar.push(`Max-Age=${v.maxAge}`);
if (v.httpOnly) qismlar.push("HttpOnly");
if (v.sameSite) qismlar.push(`SameSite=${v.sameSite}`);
return qismlar.join("; ");
}
console.log(
setCookieYoz("tema", "qorongi", {
path: "/",
maxAge: 31536000,
httpOnly: true,
sameSite: "lax",
}),
);
console.log(setCookieYoz("savat", "osh,manti"));Konsolda:
tema=qorongi; Path=/; Max-Age=31536000; HttpOnly; SameSite=lax
savat=osh%2CmantiBirinchi qatorni darsdagi haqiqiy set-cookie bilan solishtiring: Next yana Expires ni qo'shadi (eski brauzerlar uchun, Max-Age dan hisoblanadi). Ikkinchi qatorda vergul kodlandi — aks holda ba'zi serverlar uni ajratuvchi deb o'ylardi.
3-mashq (qiyin): ishonchli IP
ipniOl ni yaxshilang: ishonchliProksilar soni (n) berilsin. x-forwarded-for ro'yxatini o'ngdan o'qing: oxirgi n ta element — o'zingizning proksilaringiz qo'shgan manzillar, ulardan oldingisi — haqiqiy mijoz. Ro'yxat qisqa bo'lsa — "lokal". Nega o'ngdan? Chunki chap tomonni mijozning o'zi yozishi mumkin. at() manfiy indeks bilan oxiridan oladi.
Yechim
function ipniOl(sarlavha, n = 1) {
const royxat = (sarlavha ?? "")
.split(",")
.map((s) => s.trim())
.filter(Boolean);
return royxat.at(-n) ?? "lokal";
}
// mijoz → 1 ta o'z proksimiz
console.log(ipniOl("203.0.113.5", 1));
// hujumchi soxta qiymat qo'shdi, proksi haqiqiysini oxiriga yozdi
console.log(ipniOl("1.2.3.4, 203.0.113.5", 1));
// ikki qatlam: CDN + nginx
console.log(ipniOl("203.0.113.5, 198.51.100.7", 2));
console.log(ipniOl(null, 1));Konsolda:
203.0.113.5
203.0.113.5
203.0.113.5
lokalIkkinchi qator — hujumchi X-Forwarded-For: 1.2.3.4 yubordi, lekin o'z proksimiz haqiqiy manzilni ro'yxat oxiriga qo'shdi. Chapdan o'qigan kod 1.2.3.4 ni olardi va aldanardi. O'ngdan n-chi — ishonchli. Shart: n aynan sizning proksilaringiz soniga teng bo'lishi kerak — buni infratuzilmangizdan bilasiz.
4-mashq: Portfolio qadami
O'z portfolio ingizdagi xabarYubor ga ikkita after() qo'shing (asal idish va tezlik cheklovi). Asal idishni DevTools orqali to'ldirib sinang va terminalda JSON log chiqishini ko'ring. Keyin proxy.ts (oldingi dars) bilan birga commit qiling.
Yechim
Namuna — «Portfolio qadami» bo'limidagi diff. Tezlik cheklovini sinash: formani 4 marta tez-tez yuboring (kanon cheklovi — 10 daqiqada 3 ta) — to'rtinchisida ekranda "Juda ko'p urinish" xabari, terminalda {"tur":"aloqa-spam","sabab":"limit"}. Log javobdan keyin chiqadi: brauzer javobni oldin oladi. Commit xabari — kanondagi yoki o'zingizniki.
12. Real ishda
- Auth tizimlari sessiya cookie'sini Server Action yoki Route Handler'da
httpOnlyqilib yozadi, sahifalar esacookies()bilan o'qiydi — bu 28-qism ning asosi. - CMS'li saytlar (Sanity, Contentful, Payload) draft mode'ni rasmiy qo'llanmalarida ko'rsatadi: "Preview" tugmasi —
draftMode().enable()li Route Handler. - Analitika va audit loglari
after()bilan yoziladi: foydalanuvchi kutmaydi. Vercel'daafterplatformaningwaitUntilimkoniyatiga ulanadi. - Intervyu savoli: "Nega Server Component'da cookie o'rnatib bo'lmaydi?" — javob: oqim boshlangan,
Set-Cookiesarlavhasi kech. Bu savol HTTP'ni tushunishni tekshiradi.
Xulosa
cookies()vaheaders()— async, so'rovni o'qiydi; Cache Components'da<Suspense>ichida bo'lishi shart, aks holda build to'xtaydi.- Cookie yozish — faqat Server Action va Route Handler'da: sahifa oqimi boshlangach
Set-Cookiekech (haqiqiy xato bilan ko'rdik). draftMode()— statik sahifani faqat__prerender_bypasscookie egasi uchun so'rov vaqtida chizadi; qolganlarga kesh.after()— javobdan keyingi ish;portfolioda spam loglari javob bilan bir millisekundda, kutdirmasdan.x-forwarded-for— foydalanuvchi ma'lumoti kabi;connection()— faqat haqiqatan har so'rovda boshqa natija kerak bo'lsa.
Keyingi dars: Client tomonda ma'lumot: TanStack Query bilan Next.js — serverda oldindan olingan ma'lumotni client keshiga uzatamiz va portfolio ga nega Query kerak emasligini o'lchov bilan ko'ramiz.
Manbalar
- Next.js hujjati (16.4.0,
node_modules/next/dist/docs/):cookies,headers,draftMode,after,connection,userAgentAPI reference; "Draft Mode" guide — nextjs.org/docs/app/api-reference/functions/cookies (2026-10-21 da o'qildi) - Kanon natijalari:
natijalar/27-after.txt,natijalar/27-modal.txt,natijalar/tajriba/16-build-connection.txt; holat 27 diff - Cookie,
cookies().set()vadraftModetajribalari — biz ishga tushirdik:portfolioholat 32 nusxasi, next 16.4.0,next build+next start+curl, 2026-10-21 - RFC 7239 / MDN:
X-Forwarded-For— developer.mozilla.org/docs/Web/HTTP/Headers/X-Forwarded-For
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!