10-bo‘lim
HTTP server
node:http bilan server yaratish, so'rov va javob obyektlari, qo'lda marshrutlash, so'rov tanasini o'qish, holat kodlari va freymvork nima uchun kerak.
Ushbu bo‘lim mundarijasi
Endi haqiqiy server yozamiz. Boshida hech qanday kutubxonasiz - faqat Node ning o'zi bilan.
Eng qisqa server #
import { createServer } from "node:http";
const server = createServer((sorov, javob) => {
javob.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
javob.end("Salom, dunyo!");
});
server.listen(0);
const port = server.address().port;
const natija = await fetch(`http://localhost:${port}/`);
console.log("Holat:", natija.status);
console.log("Tana:", await natija.text());
server.close();
Holat: 200
Tana: Salom, dunyo!
Uch qadam: createServer ishlovchi funksiyani oladi, listen portni
tinglaydi, javob.end javobni yuboradi.
listen(0) - "bo'sh portni o'zing tanla" degani. Keyin uni
server.address().port dan o'qib olamiz.
Bu darslikda shu usul ishlatiladi, chunki aniq port band bo'lishi mumkin va misol boshqa kompyuterda ishlamay qolardi.
O'z loyihangizda esa aniq port beriladi:
server.listen(process.env.PORT ?? 3000);
So'rov obyekti #
import { createServer } from "node:http";
const server = createServer((sorov, javob) => {
const url = new URL(sorov.url, `http://${sorov.headers.host}`);
javob.writeHead(200, { "Content-Type": "application/json" });
javob.end(JSON.stringify({
usul: sorov.method,
yol: url.pathname,
qidiruv: url.searchParams.get("q"),
versiya: sorov.httpVersion,
}));
});
server.listen(0);
const port = server.address().port;
const javob = await fetch(`http://localhost:${port}/qidir?q=node`);
console.log(await javob.json());
server.close();
{ usul: 'GET', yol: '/qidir', qidiruv: 'node', versiya: '1.1' }
| Xossa | Nima beradi |
|---|---|
sorov.method | GET, POST, ... |
sorov.url | Yo'l va qidiruv qismi |
sorov.headers | Sarlavhalar obyekti |
sorov.url faqat /qidir?q=node qismini beradi - unda na protokol,
na domen bor.
Shuning uchun uni new URL() ga to'g'ridan-to'g'ri berib bo'lmaydi:
ikkinchi argument - asos manzil - kerak.
Yuqorida asos sorov.headers.host dan olindi. Bu odatiy yechim,
lekin yodda tuting: Host sarlavhasi mijozdan keladi, ya'ni
unga to'liq ishonib bo'lmaydi. Manzilni javobda qaytarish yoki
qayta yo'naltirish uchun ishlatsangiz, uni tekshiring.
Qo'lda marshrutlash #
import { createServer } from "node:http";
const server = createServer((sorov, javob) => {
const { pathname } = new URL(sorov.url, "http://localhost");
if (sorov.method === "GET" && pathname === "/") {
javob.writeHead(200, { "Content-Type": "text/plain" });
javob.end("bosh sahifa");
return;
}
if (sorov.method === "GET" && pathname === "/salom") {
javob.writeHead(200, { "Content-Type": "application/json" });
javob.end(JSON.stringify({ xabar: "salom" }));
return;
}
javob.writeHead(404, { "Content-Type": "text/plain" });
javob.end("topilmadi");
});
server.listen(0);
const port = server.address().port;
const asos = `http://localhost:${port}`;
for (const yol of ["/", "/salom", "/yoq"]) {
const j = await fetch(asos + yol);
console.log(j.status, await j.text());
}
server.close();
200 bosh sahifa
200 {"xabar":"salom"}
404 topilmadi
Har bir yo'l uchun if yozish kerak. Ikkita yo'l uchun bu normal,
yigirmata uchun esa chidab bo'lmaydigan bo'lib qoladi.
Aynan shuning uchun freymvorklar bor - keyingi bo'limda.
So'rov tanasini o'qish #
import { createServer } from "node:http";
const server = createServer(async (sorov, javob) => {
if (sorov.method !== "POST") {
javob.writeHead(405).end();
return;
}
const bolaklar = [];
for await (const bolak of sorov) {
bolaklar.push(bolak);
}
const tana = Buffer.concat(bolaklar).toString("utf8");
const malumot = JSON.parse(tana);
javob.writeHead(201, { "Content-Type": "application/json" });
javob.end(JSON.stringify({ qabul: malumot.ism, uzunlik: tana.length }));
});
server.listen(0);
const port = server.address().port;
const javob = await fetch(`http://localhost:${port}/`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ism: "Malika" }),
});
console.log(javob.status);
console.log(await javob.json());
server.close();
201
{ qabul: 'Malika', uzunlik: 16 }
So'rov obyekti - bu o'qish oqimi (9-bo'lim). Tanani olish uchun bo'laklarni yig'ib, birlashtirish kerak.
Yuqoridagi kod hamma bo'lakni xotiraga yig'adi. Agar kimdir bir gigabaytlik so'rov yuborsa, serveringiz xotirasi tugaydi.
Bu xizmatdan bosh tortishga (DoS) olib keladigan oddiy hujum.
Shuning uchun haqiqiy serverda tana hajmi cheklanadi:
let hajm = 0;
for await (const bolak of sorov) {
hajm += bolak.length;
if (hajm > 1_000_000) {
javob.writeHead(413).end("juda katta");
return;
}
bolaklar.push(bolak);
}
Express da bu express.json({ limit: "1mb" }) bilan hal qilinadi -
13-bo'limda ko'ramiz. 413 - "tana juda katta" degan holat kodi.
Holat kodlari #
import { createServer } from "node:http";
import { STATUS_CODES } from "node:http";
const server = createServer((sorov, javob) => {
const kod = Number(new URL(sorov.url, "http://x").searchParams.get("k"));
javob.writeHead(kod, { "Content-Type": "text/plain" });
javob.end(STATUS_CODES[kod] ?? "noma'lum");
});
server.listen(0);
const port = server.address().port;
for (const kod of [200, 201, 400, 404, 500]) {
const j = await fetch(`http://localhost:${port}/?k=${kod}`);
console.log(j.status, await j.text());
}
server.close();
200 OK
201 Created
400 Bad Request
404 Not Found
500 Internal Server Error
| Guruh | Ma'nosi |
|---|---|
2xx | Muvaffaqiyat |
3xx | Qayta yo'naltirish |
4xx | Mijoz xatosi |
5xx | Server xatosi |
Farqi muhim: 4xx - "siz noto'g'ri so'radingiz", 5xx - "biz
buzildik".
Faylni oqim bilan yuborish #
import { createServer } from "node:http";
import { createReadStream } from "node:fs";
import { writeFile, rm } from "node:fs/promises";
import { pipeline } from "node:stream/promises";
await writeFile("sahifa.html", "<h1>Salom</h1>", "utf8");
const server = createServer(async (sorov, javob) => {
javob.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
await pipeline(createReadStream("sahifa.html"), javob);
});
server.listen(0);
const port = server.address().port;
const j = await fetch(`http://localhost:${port}/`);
console.log(j.headers.get("content-type"));
console.log(await j.text());
server.close();
await rm("sahifa.html");
text/html; charset=utf-8
<h1>Salom</h1>
javob - bu yozish oqimi, shuning uchun unga to'g'ridan-to'g'ri
pipeline qilish mumkin.
Katta fayl uchun bu juda muhim: fayl xotiraga yuklanmaydi, balki diskdan tarmoqqa bo'laklab o'tadi.
Xatolarni ushlash #
import { createServer } from "node:http";
const server = createServer((sorov, javob) => {
try {
if (sorov.url === "/buzuq") {
throw new Error("ichki nosozlik");
}
javob.writeHead(200).end("yaxshi");
} catch (xato) {
javob.writeHead(500, { "Content-Type": "application/json" });
javob.end(JSON.stringify({ xato: "server xatosi" }));
}
});
server.on("error", (xato) => console.log("server xatosi:", xato.message));
server.listen(0);
const port = server.address().port;
console.log((await fetch(`http://localhost:${port}/`)).status);
const yomon = await fetch(`http://localhost:${port}/buzuq`);
console.log(yomon.status, await yomon.json());
server.close();
200
500 { xato: 'server xatosi' }
Javobda "server xatosi" yozildi, xato.message emas.
Bu ataylab: xato xabarlarida ko'pincha fayl yo'llari, jadval nomlari yoki kutubxona versiyalari bo'ladi. Ular hujumchiga tizim haqida ma'lumot beradi.
To'g'ri yondashuv: jurnalga to'liq yozing, mijozga esa umumiy xabar bering.
E'tibor bering, serverning o'ziga ham error tinglovchisi
qo'yilgan - 8-bo'limdagi qoida: tinglanmagan error jarayonni
o'ldiradi.
Nega freymvork kerak #
Shu paytgacha qo'lda yozdik va quyidagilarni o'zimiz qildik:
| Ish | Qo'lda |
|---|---|
| Marshrutlash | Uzun if zanjiri |
| Yo'l parametrlari | Qo'lda tahlil |
| JSON tanani o'qish | Bo'laklarni yig'ish |
| Xatolarni ushlash | Har joyda try/catch |
| 404 | Oxirgi else |
Freymvork bularning hammasini tayyor beradi. Keyingi bo'limda Express ni ko'ramiz.
Lekin bu bo'lim behuda emas edi: Express ham ichkarida aynan shu
node:http ustida ishlaydi. Muammoni bilsangiz, yechimni ham
yaxshiroq tushunasiz.
- Eng qisqa serverni yozib, brauzerda oching.
sorov.methodvasorov.urlni chiqaring.new URLbilan qidiruv parametrini oling.- Uchta yo'l uchun qo'lda marshrutlash yozing.
- Noma'lum yo'lga 404 qaytaring.
POSTtanasini o'qib, JSON ga aylantiring.- Tana hajmini cheklab, 413 qaytaring.
- Turli holat kodlarini sinab ko'ring.
- Faylni
pipelinebilan yuboring. - Xatoni ushlab, mijozga umumiy xabar bering.
Xulosa #
createServerishlovchi funksiyani oladi:(sorov, javob).sorov.urlto'liq manzil emas -new URLga asos kerak.- So'rov - o'qish oqimi; tanani bo'laklab yig'ish kerak va hajmini cheklash shart.
javob- yozish oqimi; faylnipipelinebilan uzatish mumkin.4xx- mijoz xatosi,5xx- server xatosi.- Xato tafsilotini mijozga bermang; jurnalga yozing.
- Serverga
errortinglovchisi qo'ying. - Qo'lda marshrutlash tez o'sib ketadi - shu sababdan freymvork kerak.
Keyingi bo'limda Express bilan tanishamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.