Menu

HTTP server trong Node.js: http.createServer qua ví dụ

http.createServer() nhận một hàm chạy cho mỗi request với req và res. Bạn đọc phương thức và URL từ req, rồi đặt status, header và body trên res và gọi res.end().

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

http.createServer() tạo một web server từ một hàm duy nhất, gọi là request handler, được Node gọi cho mỗi request đến với hai object: req (phương thức, URL và header mà client gửi) và res (những gì bạn gửi lại). Đặt status code và header trên res, rồi gọi res.end() kèm body. server.listen(3000) bắt đầu nhận kết nối ở cổng 3000.

Request đầu tiên trả về You asked for GET /hello?name=Ada, và Console ghi ra cùng phương thức và URL đó. Gửi DELETE /anything từ thanh request: cùng handler đó trả lời, vì một server http thuần không có route. Mọi request đều đi qua một hàm duy nhất.

Gửi JSON và đặt status code

res.writeHead(status, headers) đặt status code và header trong một lần gọi. Với JSON, hãy đặt Content-Type: application/json và gửi kết quả của JSON.stringify(); res.end() chỉ nhận chuỗi hoặc Buffer.

GET /users?limit=1 trả về [{"id":1,"name":"Ada"}]. req.url là đường dẫn cộng với query string, nên new URL(req.url, 'http://localhost') tách nó thành pathname và searchParams (phần base chỉ có mặt vì URL cần một địa chỉ tuyệt đối). Gửi GET /nothing thì status chuyển thành 404.

Định tuyến request bằng switch

Một API nhỏ chỉ cần một câu lệnh switch theo phương thức và đường dẫn. Tạo một khóa như 'GET /users' và cho mỗi route một case riêng:

Gửi POST /users với body {"name": "Grace"}: câu trả lời là 201 với {"id":2,"name":"Grace"}, và giờ GET /users liệt kê cả hai người dùng. Gửi cùng request POST đó với body hello thì bạn nhận 400 với {"error":"Body must be JSON"}.

Đọc body của request POST

Node không tự parse body cho bạn. req là một luồng đọc (readable stream), body đến theo từng phần qua các sự kiện data, rồi end được phát khi đã nhận đủ. readBody() ở trên gom các phần thành một chuỗi và trả về promise, để handler có thể await nó. Vì req là async iterable, vòng lặp sau làm cùng việc đó:

let data = '';
for await (const chunk of req) data += chunk;
const body = JSON.parse(data);

Một server thật còn giới hạn kích thước (ngừng đọc và trả về 413 khi data.length vượt quá giới hạn) và kiểm tra req.headers['content-type'] trước khi gọi JSON.parse.

Lỗi thường gặp

Quên res.end(). Một nhánh đặt status mà không bao giờ kết thúc response sẽ để client chờ đến khi hết thời gian.

Đặt header sau body. Khi res.end() hoặc lần res.write() đầu tiên đã chạy, header đã được gửi đi, và res.setHeader() ném ERR_HTTP_HEADERS_SENT. Hãy dùng return khi bạn gửi response sớm, như trong các ví dụ.

So sánh req.url với một đường dẫn. req.url chứa cả query string, nên req.url === '/users' sai với /users?limit=1. Hãy so sánh pathname.

Khi nào nên dùng Express

Module http là đủ cho một health check, một nơi nhận webhook hoặc để học cách HTTP hoạt động. Khi bạn cần tham số route như /users/:id, parse body JSON, middleware và xử lý lỗi, bạn sẽ phải tự viết một framework nhỏ. Express chính là framework đó, xây trên cùng các object req và res; routing trong Express trình bày cùng API đó với app.get() và app.post().

Câu hỏi thường gặp

res.write và res.end khác nhau thế nào?

res.write() gửi một phần của body và giữ response mở, nên bạn có thể gọi nó nhiều lần. res.end() gửi phần cuối (không bắt buộc) và kết thúc response. Mỗi response cần đúng một lần res.end().

Làm sao dừng một HTTP server trong Node?

Gọi server.close(). Nó ngừng nhận kết nối mới ngay lập tức và chạy callback khi các kết nối đang mở đã xong, đúng thứ bạn cần để tắt server gọn gàng khi nhận SIGTERM.

Làm sao dùng HTTPS với module http?

Hãy dùng module https: https.createServer({ key, cert }, handler) nhận cùng handler kèm private key và chứng chỉ. Trên production, một proxy như Nginx hoặc nền tảng hosting thường xử lý TLS, còn server Node vẫn lắng nghe bằng HTTP thường.

Server Node nên lắng nghe ở cổng nào?

Mọi cổng trống lớn hơn 1024 đều dùng được mà không cần quyền admin; 3000 và 8080 hay được dùng khi phát triển. Các nền tảng hosting thường cho bạn biết cổng qua một biến môi trường, nên hãy viết server.listen(process.env.PORT || 3000).

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu