Cách tạo web server trong Node.js bằng module HTTP
Khi bạn xem một trang web trong trình duyệt của bạn , bạn đang thực hiện một yêu cầu tới một máy tính khác trên internet, máy tính này sau đó sẽ cung cấp cho bạn trang web đó dưới dạng phản hồi. Máy tính bạn đang nói chuyện qua internet là một web server . Web server nhận yêu cầu HTTP từ client , như trình duyệt của bạn và cung cấp phản hồi HTTP, như trang HTML hoặc JSON từ API.Rất nhiều phần mềm liên quan đến một server để trả về một trang web. Phần mềm này thường chia thành hai loại: giao diện user và backend . Mã giao diện user quan tâm đến cách trình bày nội dung, chẳng hạn như màu sắc của thanh chuyển và kiểu văn bản. Mã back-end liên quan đến cách dữ liệu được trao đổi, xử lý và lưu trữ. Mã xử lý các yêu cầu mạng từ trình duyệt của bạn hoặc giao tiếp với database chủ yếu được quản lý bằng mã back-end.
Node.js cho phép các nhà phát triển sử dụng JavaScript để viết mã back-end, mặc dù theo truyền thống, nó được sử dụng trong trình duyệt để viết mã front-end. Việc kết hợp cả frontend và backend như vậy sẽ giảm bớt công sức tạo ra một web server , đó là lý do chính khiến Node.js là một lựa chọn phổ biến để viết mã back-end.
Trong hướng dẫn này, bạn sẽ học cách xây dựng web server bằng mô-đun http
có trong Node.js. Bạn sẽ xây dựng các web server có thể trả về dữ liệu JSON, file CSV và các trang web HTML.
Yêu cầu
- Đảm bảo rằng Node.js được cài đặt trên máy phát triển của bạn. Hướng dẫn này sử dụng Node.js version 10.19.0. Để cài đặt phần mềm này trên macOS hoặc Ubuntu 18.04, hãy làm theo các bước trong Cách cài đặt Node.js và Tạo Môi trường Phát triển Cục bộ trên macOS hoặc phần Cài đặt Sử dụng PPA của Cách Cài đặt Node.js trên Ubuntu 18.04 .
- Nền tảng Node.js hỗ trợ tạo web server ngay lập tức. Để bắt đầu, hãy đảm bảo bạn đã quen thuộc với những điều cơ bản về Node.js. Bạn có thể bắt đầu bằng cách xem lại hướng dẫn của ta về Cách viết và chạy chương trình đầu tiên của bạn trong Node.js.
- Ta cũng sử dụng lập trình không đồng bộ cho một trong các phần của ta . Nếu bạn không quen với lập trình không đồng bộ trong Node.js hoặc module
fs
để tương tác với file , bạn có thể tìm hiểu thêm với bài viết của ta về Cách viết mã không đồng bộ trong Node.js.
Bước 1 - Tạo server HTTP cơ bản
Hãy bắt đầu bằng cách tạo một server trả về văn bản thuần túy cho user . Điều này sẽ bao gồm các khái niệm chính cần thiết để cài đặt một server , điều này sẽ cung cấp nền tảng cần thiết để trả về các định dạng dữ liệu phức tạp hơn như JSON.
Đầu tiên, ta cần cài đặt một môi trường mã hóa có thể truy cập để thực hiện các bài tập của ta , cũng như các môi trường khác trong bài viết. Trong terminal , tạo một folder có tên là first-servers
:
- mkdir first-servers
Sau đó nhập folder đó:
- cd first-servers
Bây giờ, hãy tạo file chứa mã:
- touch hello.js
Mở file trong editor . Ta sẽ sử dụng nano
vì nó có sẵn trong terminal :
- nano hello.js
Ta bắt đầu bằng cách tải module http
tiêu chuẩn với tất cả các cài đặt Node.js. Thêm dòng sau vào hello.js
:
const http = require("http");
Mô-đun http
chứa chức năng tạo server mà ta sẽ thấy ở phần sau. Nếu bạn muốn tìm hiểu thêm về các module trong Node.js, hãy xem bài viết Cách tạo module Node.js của ta .
Bước tiếp theo của ta sẽ là xác định hai hằng số, server và cổng mà server của ta sẽ bị ràng buộc:
... const host = 'localhost'; const port = 8000;
Như đã đề cập trước đây, web server chấp nhận yêu cầu từ trình duyệt và các client khác. Ta có thể tương tác với web server bằng lệnh domain , được server DNS dịch sang địa chỉ IP. Địa chỉ IP là một dãy số duy nhất xác định một máy trên mạng, chẳng hạn như internet. Để biết thêm thông tin về các khái niệm domain , hãy xem bài viết Giới thiệu về Thuật ngữ, Thành phần và Khái niệm DNS của ta .
Giá trị localhost
là một địa chỉ riêng đặc biệt mà máy tính sử dụng để tham chiếu đến chính nó. Nó thường tương đương với địa chỉ IP nội bộ 127.0.0.1
và nó chỉ khả dụng với máy tính local , không có sẵn cho bất kỳ mạng local nào mà ta đã tham gia hoặc với internet.
Cổng là một số mà server sử dụng làm điểm cuối hoặc "cửa" tới địa chỉ IP của ta . Trong ví dụ của ta , ta sẽ sử dụng cổng 8000
cho web server của bạn . Cổng 8080
và 8000
thường được sử dụng làm cổng mặc định trong quá trình phát triển và trong hầu hết các trường hợp, các nhà phát triển sẽ sử dụng chúng thay vì các cổng khác cho server HTTP.
Khi ta liên kết server của bạn với server và cổng này, ta sẽ có thể truy cập server của bạn bằng cách truy cập http://localhost:8000
trong trình duyệt local .
Hãy thêm một chức năng đặc biệt, trong Node.js, ta gọi là trình lắng nghe yêu cầu . Hàm này dùng để xử lý một yêu cầu HTTP đến và trả về một phản hồi HTTP. Hàm này phải có hai đối số, một đối tượng yêu cầu và một đối tượng phản hồi. Đối tượng yêu cầu nắm bắt tất cả dữ liệu của yêu cầu HTTP đến. Đối tượng phản hồi được sử dụng để trả lại phản hồi HTTP cho server .
Ta muốn server đầu tiên của bạn trả lại thông báo này khi nào ai đó truy cập vào nó: "My first server!"
.
Hãy thêm chức năng đó tiếp theo:
... const requestListener = function (req, res) { res.writeHead(200); res.end("My first server!"); };
Hàm thường sẽ được đặt tên dựa trên chức năng của nó. Ví dụ: nếu ta đã tạo một hàm lắng nghe yêu cầu để trả về một danh sách sách, ta có thể sẽ đặt tên nó là listBooks()
. Vì đây là một trường hợp mẫu, ta sẽ sử dụng tên chung requestListener
.
Tất cả các hàm lắng nghe yêu cầu trong Node.js đều chấp nhận hai đối số: req
và res
( ta có thể đặt tên khác nếu muốn). Yêu cầu HTTP mà user gửi được ghi lại trong một đối tượng Yêu cầu, tương ứng với đối số đầu tiên, req
. Phản hồi HTTP mà ta trả lại cho user được hình thành bằng cách tương tác với đối tượng Phản hồi trong đối số thứ hai, res
.
Dòng đầu tiên res.writeHead(200);
đặt mã trạng thái HTTP của phản hồi. Mã trạng thái HTTP cho biết server xử lý một yêu cầu HTTP tốt như thế nào. Trong trường hợp này, mã trạng thái 200
tương ứng với "OK"
. Nếu bạn muốn tìm hiểu về các mã HTTP khác nhau mà web server của bạn có thể trả về với ý nghĩa mà chúng biểu thị, hướng dẫn của ta về Cách khắc phục sự cố mã lỗi HTTP phổ biến là một nơi tốt để tham khảo .
Dòng tiếp theo của hàm, res.end("My first server!");
, ghi lại phản hồi HTTP cho khách hàng đã yêu cầu. Hàm này trả về bất kỳ dữ liệu nào mà server phải trả về. Trong trường hợp này, nó trả về dữ liệu văn bản.
Cuối cùng, bây giờ ta có thể tạo server của bạn và sử dụng trình xử lý yêu cầu của ta :
... const server = http.createServer(requestListener); server.listen(port, host, () => { console.log(`Server is running on http://${host}:${port}`); });
Lưu và thoát nano
bằng cách nhấn CTRL+X
Trong dòng đầu tiên, ta tạo một đối tượng server
mới thông qua hàm createServer()
của module http
. Server này chấp nhận các yêu cầu HTTP và chuyển chúng đến hàm requestListener()
của ta .
Sau khi ta tạo server của bạn , ta phải liên kết nó với một địa chỉ mạng. Ta thực hiện điều đó với phương thức server.listen()
. Nó chấp nhận ba đối số: port
, host
và một hàm gọi lại kích hoạt khi server bắt đầu lắng nghe.
Tất cả các đối số này là tùy chọn, nhưng bạn nên nêu rõ cổng và server lưu trữ nào mà ta muốn web server sử dụng. Khi triển khai web server đến các môi trường khác nhau, cần biết cổng và server mà nó đang chạy để cài đặt cân bằng tải hoặc alias DNS .
Chức năng gọi lại ghi một thông báo vào console của ta để ta có thể biết khi nào server bắt đầu lắng nghe các kết nối.
Lưu ý: Mặc dù requestListener()
không sử dụng đối tượng req
, nó vẫn phải là đối số đầu tiên của hàm.
Với ít hơn mười lăm dòng mã, bây giờ ta có một web server . Hãy xem nó hoạt động và kiểm tra nó từ đầu đến cuối bằng cách chạy chương trình:
- node hello.js
Trong console , ta sẽ thấy kết quả này:
OutputServer is running on http://localhost:8000
Lưu ý dấu nhắc biến mất. Điều này là do server Node.js là một quá trình chạy lâu dài. Nó chỉ thoát nếu gặp lỗi khiến nó bị treo và thoát hoặc nếu ta dừng quá trình Node.js đang chạy server .
Trong một cửa sổ terminal riêng biệt, ta sẽ giao tiếp với server bằng cURL , một công cụ CLI để truyền dữ liệu đến và đi từ mạng. Nhập lệnh để thực hiện yêu cầu HTTP GET
đến server đang chạy của ta :
- curl http://localhost:8000
Khi ta nhấn ENTER
, terminal của ta sẽ hiển thị kết quả sau:
OutputMy first server!
Bây giờ ta đã cài đặt một server và nhận được phản hồi server đầu tiên của ta .
Hãy phân tích những gì đã xảy ra khi ta kiểm tra server của bạn . Sử dụng cURL, ta đã gửi một yêu cầu GET
tới server tại địa chỉ http://localhost:8000
. Server Node.js của ta đã lắng nghe các kết nối từ địa chỉ đó. Server đã chuyển yêu cầu đó đến hàm requestListener()
. Hàm trả về dữ liệu văn bản với mã trạng thái 200
. Sau đó, server đã gửi phản hồi đó trở lại cURL, hiển thị thông báo trong terminal của ta .
Trước khi tiếp tục, hãy thoát khỏi server đang chạy của ta bằng cách nhấn CTRL+C
Điều này làm gián đoạn quá trình thực thi của server của ta , đưa ta trở lại dấu nhắc dòng lệnh.
Trong hầu hết các trang web ta truy cập hoặc các API ta sử dụng, các phản hồi của server hiếm khi ở dạng văn bản thuần túy. Ta lấy các trang HTML và dữ liệu JSON làm định dạng phản hồi phổ biến. Trong bước tiếp theo, ta sẽ tìm hiểu cách trả lại phản hồi HTTP ở các định dạng dữ liệu phổ biến mà ta gặp trên web.
Bước 2 - Trả lại các loại nội dung khác nhau
Phản hồi mà ta trả về từ web server có thể có nhiều định dạng. JSON và HTML đã được đề cập trước đây và ta cũng có thể trả về các định dạng văn bản khác như XML và CSV. Cuối cùng, web server có thể trả về dữ liệu không phải văn bản như PDF, file nén, âm thanh và video.
Trong bài viết này, ngoài văn bản thuần túy mà ta vừa trả lại, bạn sẽ học cách trả về các loại dữ liệu sau:
- JSON
- CSV
- HTML
Ba loại dữ liệu này đều dựa trên văn bản và là các định dạng phổ biến để cung cấp nội dung trên web. Nhiều ngôn ngữ và công cụ phát triển phía server có hỗ trợ trả về các kiểu dữ liệu khác nhau này. Trong ngữ cảnh của Node.js, ta cần làm hai việc:
- Đặt tiêu đề
Content-Type
trong phản hồi HTTP của ta với giá trị thích hợp. - Đảm bảo rằng
res.end()
nhận dữ liệu ở định dạng phù hợp.
Hãy xem điều này hoạt động với một số ví dụ. Mã ta sẽ viết trong phần này và những đoạn sau có nhiều điểm tương đồng với mã ta đã viết trước đó. Hầu hết các thay đổi tồn tại trong hàm requestListener()
. Hãy tạo file bằng “mã mẫu” này để làm cho các phần trong tương lai dễ theo dõi hơn.
Tạo một file mới có tên html.js
Tệp này sẽ được sử dụng sau đó để trả về văn bản HTML trong phản hồi HTTP. Ta sẽ đặt mã mẫu ở đây và sao chép nó vào các server khác trả về nhiều loại khác nhau.
Trong terminal , nhập như sau:
- touch html.js
Bây giờ hãy mở file này trong một editor :
- nano html.js
Hãy sao chép “mã mẫu”. Nhập cái này vào nano
:
const http = require("http"); const host = 'localhost'; const port = 8000; const requestListener = function (req, res) {}; const server = http.createServer(requestListener); server.listen(port, host, () => { console.log(`Server is running on http://${host}:${port}`); });
Lưu và thoát html.js
bằng CTRL+X
, sau đó quay lại terminal .
Bây giờ, hãy sao chép file này thành hai file mới. Tệp đầu tiên sẽ trả về dữ liệu CSV trong phản hồi HTTP:
- cp html.js csv.js
Tệp thứ hai sẽ trả về phản hồi JSON trong server :
- cp html.js json.js
Các file còn lại sẽ dành cho các bài tập sau:
- cp html.js htmlFile.js
- cp html.js routes.js
Bây giờ ta đã cài đặt để tiếp tục các bài tập của bạn . Hãy bắt đầu với việc trả về JSON.
Cung cấp JSON
JavaScript Object Notation , thường được gọi là JSON, là một định dạng trao đổi dữ liệu dựa trên văn bản. Như tên gọi của nó, nó có nguồn root từ các đối tượng JavaScript, nhưng nó độc lập với ngôn ngữ, nghĩa là nó được dùng bởi bất kỳ ngôn ngữ lập trình nào có thể phân tích cú pháp của nó.
JSON thường được sử dụng bởi các API để chấp nhận và trả về dữ liệu. Tính phổ biến của nó là do kích thước truyền dữ liệu thấp hơn so với các tiêu chuẩn trao đổi dữ liệu trước đây như XML, cũng như công cụ tồn tại cho phép các chương trình phân tích cú pháp mà không cần nỗ lực quá nhiều. Nếu bạn muốn tìm hiểu thêm về JSON, bạn có thể đọc hướng dẫn của ta về Cách làm việc với JSON trong JavaScript .
Mở file json.js
bằng nano
:
- nano json.js
Ta muốn trả lại một phản hồi JSON. Hãy sửa đổi hàm requestListener()
để trả về tiêu đề thích hợp mà tất cả các phản hồi JSON có bằng cách thay đổi các dòng được đánh dấu như sau:
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); }; ...
Phương thức res.setHeader()
thêm một tiêu đề HTTP vào phản hồi. Tiêu đề HTTP là thông tin bổ sung có thể được đính kèm vào một yêu cầu hoặc phản hồi. Phương thức res.setHeader()
nhận hai đối số: tên của tiêu đề và giá trị của nó.
Tiêu đề Content-Type
được sử dụng để biểu thị định dạng của dữ liệu, còn gọi là loại phương tiện, được gửi cùng với yêu cầu hoặc phản hồi. Trong trường hợp này Content-Type
của ta là application/json
.
Bây giờ, hãy trả lại nội dung JSON cho user . Sửa đổi json.js
để nó trông giống như sau:
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); res.writeHead(200); res.end(`{"message": "This is a JSON response"}`); }; ...
Giống như trước đây, ta cho user biết rằng yêu cầu của họ đã thành công bằng cách trả về mã trạng thái là 200
. Lần này trong response.end()
gọi response.end()
, đối số chuỗi của ta chứa JSON hợp lệ.
Lưu và thoát json.js
bằng cách nhấn CTRL+X
Bây giờ, hãy chạy server bằng lệnh node
:
- node json.js
Trong một terminal khác, hãy truy cập server bằng cách sử dụng cURL:
- curl http://localhost:8000
Khi ta nhấn ENTER
, ta sẽ thấy kết quả sau:
Output{"message": "This is a JSON response"}
Như vậy, ta đã trả lại thành công phản hồi JSON, giống như nhiều API phổ biến mà ta tạo ứng dụng. Đảm bảo thoát khỏi server đang chạy bằng CTRL+C
để ta có thể quay lại dấu nhắc terminal tiêu chuẩn. Tiếp theo, hãy xem xét một định dạng trả về dữ liệu phổ biến khác: CSV.
Phục vụ CSV
Định dạng file Giá trị được phân tách bằng dấu phẩy (CSV) là một tiêu chuẩn văn bản thường được sử dụng để cung cấp dữ liệu dạng bảng. Trong hầu hết các trường hợp, mỗi hàng được phân tách bằng một dòng mới và mỗi mục trong hàng được phân tách bằng dấu phẩy.
Trong không gian làm việc của ta , hãy mở file csv.js
bằng editor văn bản:
- nano csv.js
Hãy thêm các dòng sau vào hàm requestListener()
của ta :
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/csv"); res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv"); }; ...
Lần này, Content-Type
của ta cho biết rằng file CSV đang được trả về dưới dạng giá trị là text/csv
. Tiêu đề thứ hai ta thêm là Content-Disposition
. Tiêu đề này cho trình duyệt biết cách hiển thị dữ liệu, đặc biệt là trong trình duyệt hoặc dưới dạng file riêng biệt.
Khi ta trả lại phản hồi CSV, hầu hết các trình duyệt hiện đại sẽ tự động download file ngay cả khi tiêu đề Content-Disposition
không được đặt. Tuy nhiên, khi trả về file CSV, ta vẫn nên thêm tiêu đề này vì nó cho phép ta đặt tên của file CSV. Trong trường hợp này, ta báo hiệu với trình duyệt rằng file CSV này là file đính kèm và nên được download . Sau đó, ta cho trình duyệt biết rằng tên của file là oceanpals.csv
.
Hãy ghi dữ liệu CSV trong phản hồi HTTP:
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/csv"); res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv"); res.writeHead(200); res.end(`id,name,email\n1,Sammy Shark,shark@ocean.com`); }; ...
Giống như trước khi ta trả về trạng thái 200
/ OK
với phản hồi của ta . Lần này, lệnh gọi res.end()
của ta có một chuỗi là CSV hợp lệ. Dấu phẩy phân tách giá trị trong mỗi cột và ký tự dòng mới ( \n
) phân tách các hàng. Ta có hai hàng, một cho tiêu đề bảng và một cho dữ liệu.
Ta sẽ kiểm tra server này trong trình duyệt. Lưu csv.js
và thoát khỏi editor bằng CTRL+X
Chạy server bằng lệnh Node.js:
- node csv.js
Trong một Terminal khác, hãy truy cập server bằng cách sử dụng cURL:
- curl http://localhost:8000
Control panel sẽ hiển thị điều này:
Outputid,name,email 1,Sammy Shark,shark@ocean.com
Nếu ta truy cập http://localhost:8000
trong trình duyệt của bạn , một file CSV sẽ được download . Tên file của nó sẽ là oceanpals.csv
.
Thoát khỏi server đang chạy bằng CTRL+C
để quay lại dấu nhắc terminal tiêu chuẩn.
Sau khi trả lại JSON và CSV, ta đã đề cập đến hai trường hợp phổ biến đối với API. Hãy chuyển sang cách ta trả lại dữ liệu cho các trang web mà mọi người xem trong trình duyệt.
Cung cấp HTML
HTML, Ngôn ngữ đánh dấu siêu văn bản , là định dạng phổ biến nhất được sử dụng khi ta muốn user tương tác với server của ta thông qua trình duyệt web. Nó được tạo ra để cấu trúc nội dung web. Các trình duyệt web được xây dựng để hiển thị nội dung HTML, cũng như bất kỳ kiểu nào ta thêm vào bằng CSS , một công nghệ web front-end khác cho phép ta thay đổi tính thẩm mỹ của các trang web của bạn .
Hãy mở lại html.js
bằng editor của ta :
- nano html.js
Sửa đổi hàm requestListener()
để trả về tiêu đề Content-Type
thích hợp cho phản hồi HTML:
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/html"); }; ...
Bây giờ, hãy trả lại nội dung HTML cho user . Thêm các dòng được đánh dấu vào html.js
để nó trông giống như sau:
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(`<html><body><h1>This is HTML</h1></body></html>`); }; ...
Đầu tiên ta thêm mã trạng thái HTTP. Sau đó, ta gọi response.end()
với đối số chuỗi chứa HTML hợp lệ. Khi ta truy cập vào server của bạn trong trình duyệt, ta sẽ thấy một trang HTML với một thẻ tiêu đề chứa This is HTML
.
Hãy lưu và thoát bằng cách nhấn CTRL+X
Bây giờ, hãy chạy server bằng lệnh node
:
- node html.js
Ta sẽ thấy Server is running on http://localhost:8000
khi chương trình của ta đã bắt đầu.
Bây giờ hãy vào trình duyệt và truy cập http://localhost:8000
. Trang của ta sẽ trông như thế này:
Hãy thoát khỏi server đang chạy bằng CTRL+C
và quay lại dấu nhắc terminal tiêu chuẩn.
Thông thường HTML được viết trong một file , tách biệt với mã phía server như các chương trình Node.js của ta . Tiếp theo, hãy xem cách ta có thể trả lại phản hồi HTML từ file .
Bước 3 - Cung cấp trang HTML từ file
Ta có thể cung cấp HTML dưới dạng chuỗi trong Node.js cho user , nhưng tốt hơn là ta tải các file HTML và phân phát nội dung của chúng. Bằng cách này, khi file HTML phát triển, ta không phải duy trì các chuỗi dài trong mã Node.js của bạn , giữ cho nó ngắn gọn hơn và cho phép ta làm việc trên từng khía cạnh của trang web một cách độc lập. Sự “tách biệt các mối quan tâm” này phổ biến trong nhiều cài đặt phát triển web, vì vậy bạn nên biết cách tải các file HTML để hỗ trợ nó trong Node.js
Để phân phát các file HTML, ta tải file HTML bằng mô-đun fs
và sử dụng dữ liệu của nó khi viết phản hồi HTTP của ta .
Đầu tiên, ta sẽ tạo một file HTML mà web server sẽ trả về. Tạo một file HTML mới:
- touch index.html
Bây giờ hãy mở index.html
trong một editor :
- nano index.html
Trang web của ta sẽ rất nhỏ. Nó sẽ có nền màu cam và sẽ hiển thị một số văn bản chúc mừng ở trung tâm. Thêm mã này vào file :
<!DOCTYPE html> <head> <title>My Website</title> <style> *, html { margin: 0; padding: 0; border: 0; } html { width: 100%; height: 100%; } body { width: 100%; height: 100%; position: relative; background-color: rgb(236, 152, 42); } .center { width: 100%; height: 50%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; font-family: "Trebuchet MS", Helvetica, sans-serif; text-align: center; } h1 { font-size: 144px; } p { font-size: 64px; } </style> </head> <body> <div class="center"> <h1>Hello Again!</h1> <p>This is served from a file</p> </div> </body> </html>
Trang web này hiển thị hai dòng văn bản: Hello Again!
và This is served from a file
. Các dòng xuất hiện ở giữa trang, một bên trên nhau. Dòng đầu tiên của văn bản được hiển thị trong một tiêu đề, nghĩa là nó sẽ lớn. Dòng văn bản thứ hai sẽ nhỏ hơn một chút. Tất cả văn bản sẽ có màu trắng và trang web có nền màu cam.
Mặc dù nó không thuộc phạm vi của bài viết hoặc loạt bài này, nhưng nếu bạn muốn tìm hiểu thêm về HTML, CSS và các công nghệ web front-end khác, bạn có thể xem hướng dẫn Bắt đầu với Web của Mozilla .
Đó là tất cả những gì ta cần cho HTML, vì vậy hãy lưu và thoát file bằng CTRL+X
Bây giờ ta có thể chuyển sang mã server .
Đối với bài tập này, ta sẽ làm việc trên htmlFile.js
. Mở nó bằng editor :
- nano htmlFile.js
Khi ta phải đọc một file , hãy bắt đầu bằng lệnh module fs
:
const http = require("http"); const fs = require('fs').promises; ...
Mô-đun này chứa hàm readFile()
mà ta sẽ sử dụng để tải file HTML tại chỗ. Ta nhập biến thể hứa hẹn phù hợp với các phương pháp hay nhất về JavaScript hiện đại. Ta sử dụng các lời hứa làm cú pháp của nó ngắn gọn hơn các lệnh gọi lại, ta sẽ phải sử dụng nếu ta gán fs
cho chỉ require('fs')
. Để tìm hiểu thêm về các phương pháp hay nhất về lập trình không đồng bộ, bạn có thể đọc hướng dẫn Cách viết mã không đồng bộ trong Node.js của ta .
Ta muốn file HTML của bạn được đọc khi user yêu cầu hệ thống của ta . Hãy bắt đầu bằng cách sửa đổi requestListener()
để đọc file :
... const requestListener = function (req, res) { fs.readFile(__dirname + "/index.html") }; ...
Ta sử dụng phương thức fs.readFile()
để tải file . Đối số của nó có __dirname + "/index.html"
. Biến đặc biệt __dirname
có đường dẫn tuyệt đối về nơi mã Node.js đang được chạy. Sau đó, ta thêm /index.html
để ta có thể tải file HTML mà ta đã tạo trước đó.
Bây giờ, hãy trả lại trang HTML sau khi nó được tải:
... const requestListener = function (req, res) { fs.readFile(__dirname + "/index.html") .then(contents => { res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(contents); }) }; ...
Nếu lời hứa fs.readFile()
giải quyết thành công, nó sẽ trả về dữ liệu của nó. Ta sử dụng phương thức then()
để xử lý trường hợp này. Tham số contents
chứa dữ liệu của file HTML.
Đầu tiên, ta đặt tiêu đề Content-Type
thành text/html
để cho khách hàng biết rằng ta đang trả lại dữ liệu HTML. Sau đó, ta viết mã trạng thái để cho biết yêu cầu đã thành công. Cuối cùng, ta gửi cho khách hàng trang HTML mà ta đã tải, với dữ liệu trong biến contents
.
Phương thức fs.readFile()
có thể bị lỗi, vì vậy ta nên xử lý trường hợp này khi gặp lỗi. Thêm cái này vào hàm requestListener()
:
... const requestListener = function (req, res) { fs.readFile(__dirname + "/index.html") .then(contents => { res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(contents); }) .catch(err => { res.writeHead(500); res.end(err); return; }); }; ...
Lưu file và thoát nano
bằng CTRL+X
Khi một lời hứa gặp lỗi, nó sẽ bị từ chối. Ta xử lý trường hợp đó bằng phương thức catch()
. Nó chấp nhận lỗi mà fs.readFile()
trả về, đặt mã trạng thái thành 500
biểu thị rằng đã gặp lỗi nội bộ và trả lại lỗi cho user .
Chạy server của ta bằng lệnh node
:
- node htmlFile.js
Trong trình duyệt web, hãy truy cập http://localhost:8000
. Bạn sẽ thấy trang này:
Đến đây bạn đã trả lại một trang HTML từ server cho user . Bạn có thể thoát khỏi server đang chạy bằng CTRL+C
Bạn sẽ thấy dấu nhắc terminal quay lại khi bạn thực hiện.
Khi viết mã như thế này trong production , bạn có thể không muốn tải trang HTML mỗi khi nhận được yêu cầu HTTP. Trong khi trang HTML này có kích thước khoảng 800 byte, các trang web phức tạp hơn có thể có kích thước hàng megabyte. Các file lớn có thể mất một lúc để tải. Nếu trang web đang mong đợi nhiều lưu lượng truy cập, tốt nhất có thể tải các file HTML khi khởi động và lưu nội dung của chúng. Sau khi chúng được tải, bạn có thể cài đặt server và làm cho nó lắng nghe các yêu cầu trên một địa chỉ.
Để chứng minh phương pháp này, hãy xem cách ta có thể làm lại server của bạn để hiệu quả hơn và có thể mở rộng.
Cung cấp HTML một cách hiệu quả
Thay vì tải HTML cho mọi yêu cầu, trong bước này, ta sẽ tải nó một lần vào đầu. Yêu cầu sẽ trả về dữ liệu mà ta đã tải khi khởi động.
Trong terminal, mở lại tập lệnh Node.js bằng editor :
- nano htmlFile.js
Hãy bắt đầu bằng cách thêm một biến mới trước khi tạo hàm requestListener()
:
... let indexFile; const requestListener = function (req, res) { ...
Khi ta chạy chương trình này, biến này sẽ giữ nội dung của file HTML.
Bây giờ, hãy điều chỉnh lại hàm requestListener()
. Thay vì tải file , bây giờ nó sẽ trả về nội dung của indexFile
:
... const requestListener = function (req, res) { res.setHeader("Content-Type", "text/html"); res.writeHead(200); res.end(indexFile); }; ...
Tiếp theo, ta chuyển logic đọc file từ hàm requestListener()
sang khởi động server của ta . áp dụng các thay đổi sau khi ta tạo server :
... const server = http.createServer(requestListener); fs.readFile(__dirname + "/index.html") .then(contents => { indexFile = contents; server.listen(port, host, () => { console.log(`Server is running on http://${host}:${port}`); }); }) .catch(err => { console.error(`Could not read index.html file: ${err}`); process.exit(1); });
Lưu file và thoát nano
bằng CTRL+X
Mã đọc file tương tự như những gì ta đã viết trong lần thử đầu tiên. Tuy nhiên, khi ta đọc file thành công, bây giờ ta lưu nội dung vào biến indexFile
toàn cục của ta . Sau đó, ta khởi động server với phương thức listen()
. Điều quan trọng là file được tải trước khi server được chạy. Bằng cách này, hàm requestListener()
sẽ chắc chắn trả về một trang HTML, vì indexFile
không còn là một biến trống.
Trình xử lý lỗi của ta cũng đã thay đổi. Nếu không thể tải file , ta sẽ ghi lại lỗi và in ra console của ta . Sau đó ta thoát khỏi chương trình Node.js bằng hàm exit()
mà không cần khởi động server . Bằng cách này, ta có thể biết lý do tại sao đọc file không thành công, giải quyết sự cố và sau đó khởi động lại server .
Hiện ta đã tạo các web server khác nhau trả về nhiều loại dữ liệu khác nhau cho user . Lúc này, ta chưa sử dụng bất kỳ dữ liệu yêu cầu nào để xác định những gì nên được trả lại. Ta cần sử dụng dữ liệu yêu cầu khi cài đặt các tuyến hoặc đường dẫn khác nhau trong server Node.js, vì vậy tiếp theo hãy xem cách chúng hoạt động cùng nhau.
Bước 4 - Quản lý các tuyến sử dụng một đối tượng yêu cầu HTTP
Hầu hết các trang web ta truy cập hoặc các API ta sử dụng thường có nhiều hơn một điểm cuối để ta có thể truy cập các tài nguyên khác nhau. Một ví dụ điển hình là hệ thống quản lý sách, một hệ thống được dùng trong thư viện. Nó không chỉ cần quản lý dữ liệu sách, mà còn quản lý dữ liệu tác giả để tạo danh mục và tìm kiếm thuận tiện.
Mặc dù dữ liệu về sách và tác giả có liên quan với nhau nhưng chúng là hai đối tượng khác nhau. Trong những trường hợp này, các nhà phát triển phần mềm thường viết mã cho từng đối tượng trên các điểm cuối khác nhau như một cách để chỉ ra cho user API biết họ đang tương tác với loại dữ liệu nào.
Hãy tạo một server mới cho một thư viện nhỏ, nó sẽ trả về hai loại dữ liệu khác nhau. Nếu user truy cập địa chỉ server của ta tại /books
, họ sẽ nhận được danh sách sách trong JSON. Nếu họ truy cập /authors
, họ sẽ nhận được danh sách thông tin tác giả trong JSON.
Lúc này, ta đã trả lại cùng một phản hồi cho mọi yêu cầu mà ta nhận được. Hãy minh họa điều này một cách nhanh chóng.
Chạy lại ví dụ phản hồi JSON của ta :
- node json.js
Trong một terminal khác, hãy thực hiện yêu cầu cURL như trước:
- curl http://localhost:8000
Bạn sẽ thấy:
Output{"message": "This is a JSON response"}
Bây giờ hãy thử một lệnh curl khác:
- curl http://localhost:8000/todos
Sau khi nhấn Enter
, bạn sẽ thấy kết quả tương tự:
Output{"message": "This is a JSON response"}
Ta chưa xây dựng bất kỳ logic đặc biệt nào trong hàm requestListener()
mình để xử lý một yêu cầu có URL chứa /todos
, vì vậy, Node.js trả về cùng một thông báo JSON theo mặc định.
Khi ta muốn xây dựng một server quản lý thư viện thu nhỏ, bây giờ ta sẽ tách loại dữ liệu được trả về dựa trên điểm cuối mà user truy cập.
Đầu tiên, hãy thoát khỏi server đang chạy bằng CTRL+C
Bây giờ hãy mở routes.js
trong editor của bạn:
- nano routes.js
Hãy bắt đầu bằng cách lưu trữ dữ liệu JSON của ta trong các biến trước hàm requestListener()
:
... const books = JSON.stringify([ { title: "The Alchemist", author: "Paulo Coelho", year: 1988 }, { title: "The Prophet", author: "Kahlil Gibran", year: 1923 } ]); const authors = JSON.stringify([ { name: "Paulo Coelho", countryOfBirth: "Brazil", yearOfBirth: 1947 }, { name: "Kahlil Gibran", countryOfBirth: "Lebanon", yearOfBirth: 1883 } ]); ...
Biến books
là một chuỗi chứa JSON cho một mảng các đối tượng sách. Mỗi cuốn sách có tiêu đề hoặc tên, tác giả và năm xuất bản.
Biến authors
là một chuỗi có chứa JSON cho một mảng các đối tượng tác giả. Mỗi tác giả đều có tên, quê quán và năm sinh của họ.
Bây giờ ta có dữ liệu mà các phản hồi của ta sẽ trả về, hãy bắt đầu sửa đổi hàm requestListener()
để trả chúng về các tuyến chính xác.
Trước tiên, ta sẽ đảm bảo mọi phản hồi từ server của ta đều có tiêu đề Content-Type
chính xác:
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); } ...
Bây giờ, ta muốn trả về JSON phù hợp tùy thuộc vào đường dẫn URL mà user truy cập. Hãy tạo một câu lệnh switch
trên URL của yêu cầu:
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); switch (req.url) {} } ...
Để lấy đường dẫn URL từ một đối tượng yêu cầu, ta cần truy cập thuộc tính url
của nó. Bây giờ ta có thể thêm các trường hợp vào câu lệnh switch
để trả về JSON thích hợp.
Câu lệnh switch
của JavaScript cung cấp một cách để kiểm soát mã nào được chạy tùy thuộc vào giá trị của một đối tượng hoặc biểu thức JavaScript (ví dụ: kết quả của các phép toán). Nếu bạn cần một bài học hoặc dấu nhắc về cách sử dụng chúng, hãy xem hướng dẫn của ta về Cách Sử dụng Câu lệnh Chuyển đổi trong JavaScript .
Hãy tiếp tục bằng cách thêm case
khi user muốn nhận danh sách sách của ta :
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); switch (req.url) { case "/books": res.writeHead(200); res.end(books); break } } ...
Ta đặt mã trạng thái của bạn thành 200
để cho biết yêu cầu vẫn ổn và trả lại JSON chứa danh sách sách của ta . Bây giờ hãy thêm một case
khác cho các tác giả của ta :
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); switch (req.url) { case "/books": res.writeHead(200); res.end(books); break case "/authors": res.writeHead(200); res.end(authors); break } } ...
Giống như trước đây, mã trạng thái sẽ là 200
theo yêu cầu. Lần này ta trả về JSON chứa danh sách các tác giả của ta .
Ta muốn trả về lỗi nếu user cố gắng đi đến bất kỳ đường dẫn nào khác. Hãy thêm trường hợp mặc định để thực hiện việc này:
... const requestListener = function (req, res) { res.setHeader("Content-Type", "application/json"); switch (req.url) { case "/books": res.writeHead(200); res.end(books); break case "/authors": res.writeHead(200); res.end(authors); break default: res.writeHead(404); res.end(JSON.stringify({error:"Resource not found"})); } } ...
Ta sử dụng từ khóa default
trong câu lệnh switch
để nắm bắt tất cả các trường hợp khác mà các trường hợp trước đây của ta không nắm bắt được. Ta đặt mã trạng thái thành 404
để cho biết rằng không tìm thấy URL mà họ đang tìm kiếm. Sau đó, ta đặt một đối tượng JSON có chứa thông báo lỗi.
Hãy kiểm tra server của ta để xem liệu nó có hoạt động như ta mong đợi hay không. Trong một terminal khác, trước tiên hãy chạy một lệnh để xem liệu ta có lấy lại danh sách sách của bạn hay không:
- curl http://localhost:8000/books
Nhấn Enter
để xem kết quả sau:
Output[{"title":"The Alchemist","author":"Paulo Coelho","year":1988},{"title":"The Prophet","author":"Kahlil Gibran","year":1923}]
Càng xa càng tốt. Hãy thử tương tự cho /authors
. Nhập lệnh sau vào terminal:
- curl http://localhost:8000/authors
Bạn sẽ thấy kết quả sau khi lệnh hoàn tất:
Output[{"name":"Paulo Coelho","countryOfBirth":"Brazil","yearOfBirth":1947},{"name":"Kahlil Gibran","countryOfBirth":"Lebanon","yearOfBirth":1883}]
Cuối cùng, hãy thử một URL sai đảm bảo rằng requestListener()
trả về phản hồi lỗi:
- curl http://localhost:8000/notreal
Nhập lệnh đó sẽ hiển thị thông báo sau:
Output{"error":"Resource not found"}
Bạn có thể thoát khỏi server đang chạy bằng CTRL+C
Như vậy, ta đã tạo ra các con đường khác nhau để user có được dữ liệu khác nhau. Ta cũng đã thêm phản hồi mặc định trả về lỗi HTTP nếu user nhập URL mà ta không hỗ trợ.
Kết luận
Trong hướng dẫn này, bạn đã tạo một loạt các server HTTP Node.js. Đầu tiên, bạn đã trả lại một phản hồi dạng văn bản cơ bản. Sau đó, bạn tiếp tục trả về nhiều loại dữ liệu khác nhau từ server của ta : JSON, CSV và HTML. Từ đó, bạn có thể kết hợp tải file với phản hồi HTTP để trả lại trang HTML từ server cho user và tạo một API sử dụng thông tin về yêu cầu của user để xác định dữ liệu nào sẽ được gửi trong phản hồi của nó.
Như vậy, bạn được trang bị để tạo các web server có thể xử lý nhiều yêu cầu và phản hồi khác nhau. Với kiến thức này, bạn có thể tạo một server trả về nhiều trang HTML cho user ở các điểm cuối khác nhau. Bạn cũng có thể tạo API của riêng mình.
Để tìm hiểu thêm về các web server HTTP trong Node.js, bạn có thể đọc tài liệu Node.js trên module http
. Nếu bạn muốn tiếp tục học Node.js, bạn có thể quay lại trang Cách viết mã trong chuỗi Node.js.
Các tin liên quan
Mã thông báo web JSON (JWT) trong Express.js2020-02-19
Phát triển bản địa với API thông báo web
2020-02-12
Cách tạo ứng dụng chuyển văn bản thành giọng nói với API giọng nói trên web
2019-12-12
Cách tạo băng chuyền image danh mục đầu tư với các thanh trượt được đồng bộ hóa trên trang web
2019-12-12
Cách tạo thông báo trên web bằng kênh Laravel và Pusher
2019-12-12
Khả năng truy cập web cho người mới bắt đầu
2019-12-12
Cách cài đặt web server OpenLiteSpeed trên Ubuntu 18.04
2019-12-02
Sử dụng Phông chữ Google trong các Trang web của bạn
2019-08-22
Cách triển khai ứng dụng web Go bằng Nginx trên Ubuntu 18.04
2019-07-24
Biến Gatsby thành PWA: Service Worker và Web App Manifest
2019-07-18