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.

🕑 11 daqiqa o‘qish 📄 638 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Eng qisqa server
  2. So'rov obyekti
  3. Qo'lda marshrutlash
  4. So'rov tanasini o'qish
  5. Holat kodlari
  6. Faylni oqim bilan yuborish
  7. Xatolarni ushlash
  8. Nega freymvork kerak
  9. Xulosa

Endi haqiqiy server yozamiz. Boshida hech qanday kutubxonasiz - faqat Node ning o'zi bilan.

Eng qisqa server #

JavaScript
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();
Natija
Holat: 200
Tana: Salom, dunyo!

Uch qadam: createServer ishlovchi funksiyani oladi, listen portni tinglaydi, javob.end javobni yuboradi.

Nega port 0

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:

JavaScript
server.listen(process.env.PORT ?? 3000);

So'rov obyekti #

JavaScript
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();
Natija
{ usul: 'GET', yol: '/qidir', qidiruv: 'node', versiya: '1.1' }
XossaNima beradi
sorov.methodGET, POST, ...
sorov.urlYo'l va qidiruv qismi
sorov.headersSarlavhalar obyekti
sorov.url to'liq manzil emas

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 #

JavaScript
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();
Natija
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 #

JavaScript
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();
Natija
201
{ qabul: 'Malika', uzunlik: 16 }

So'rov obyekti - bu o'qish oqimi (9-bo'lim). Tanani olish uchun bo'laklarni yig'ib, birlashtirish kerak.

Tana hajmini cheklamasangiz - bu zaiflik

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:

JavaScript
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 #

JavaScript
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();
Natija
200 OK
201 Created
400 Bad Request
404 Not Found
500 Internal Server Error
GuruhMa'nosi
2xxMuvaffaqiyat
3xxQayta yo'naltirish
4xxMijoz xatosi
5xxServer xatosi

Farqi muhim: 4xx - "siz noto'g'ri so'radingiz", 5xx - "biz buzildik".

Faylni oqim bilan yuborish #

JavaScript
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");
Natija
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 #

JavaScript
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();
Natija
200
500 { xato: 'server xatosi' }
Xato tafsilotini mijozga bermang

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:

IshQo'lda
MarshrutlashUzun if zanjiri
Yo'l parametrlariQo'lda tahlil
JSON tanani o'qishBo'laklarni yig'ish
Xatolarni ushlashHar joyda try/catch
404Oxirgi 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.

Amaliy topshiriq
  1. Eng qisqa serverni yozib, brauzerda oching.
  2. sorov.method va sorov.url ni chiqaring.
  3. new URL bilan qidiruv parametrini oling.
  4. Uchta yo'l uchun qo'lda marshrutlash yozing.
  5. Noma'lum yo'lga 404 qaytaring.
  6. POST tanasini o'qib, JSON ga aylantiring.
  7. Tana hajmini cheklab, 413 qaytaring.
  8. Turli holat kodlarini sinab ko'ring.
  9. Faylni pipeline bilan yuboring.
  10. Xatoni ushlab, mijozga umumiy xabar bering.

Xulosa #

  • createServer ishlovchi funksiyani oladi: (sorov, javob).
  • sorov.url to'liq manzil emas - new URL ga asos kerak.
  • So'rov - o'qish oqimi; tanani bo'laklab yig'ish kerak va hajmini cheklash shart.
  • javob - yozish oqimi; faylni pipeline bilan uzatish mumkin.
  • 4xx - mijoz xatosi, 5xx - server xatosi.
  • Xato tafsilotini mijozga bermang; jurnalga yozing.
  • Serverga error tinglovchisi qo'ying.
  • Qo'lda marshrutlash tez o'sib ketadi - shu sababdan freymvork kerak.

Keyingi bo'limda Express bilan tanishamiz.

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.