Cách tích hợp MongoDB với ứng dụng Node của bạn
Khi làm việc với Node.js , bạn có thể thấy mình đang phát triển một dự án lưu trữ và truy vấn dữ liệu. Trong trường hợp này, bạn cần phải chọn giải pháp database phù hợp với dữ liệu và kiểu truy vấn của ứng dụng.Trong hướng dẫn này, bạn sẽ tích hợp database MongoDB với một ứng dụng Node hiện có. Database NoSQL như MongoDB có thể hữu ích nếu các yêu cầu dữ liệu bao gồm khả năng mở rộng và tính linh hoạt. MongoDB cũng tích hợp tốt với Node vì nó được thiết kế để hoạt động không đồng bộ với các đối tượng JSON .
Để tích hợp MongoDB vào dự án của bạn , bạn sẽ sử dụng Mongoose đối tượng lập bản đồ tài liệu (ODM) để tạo schemas và mô hình cho dữ liệu ứng dụng của bạn . Điều này sẽ cho phép bạn tổ chức mã ứng dụng của bạn theo mô hình kiến trúc model-view-controller (MVC) , cho phép bạn tách biệt logic về cách ứng dụng của bạn xử lý thông tin đầu vào của user với cách dữ liệu được cấu trúc và hiển thị cho user . Sử dụng mẫu này có thể tạo điều kiện thuận lợi cho việc thử nghiệm và phát triển trong tương lai bằng cách đưa vào cơ sở mã của bạn sự tách biệt các mối quan tâm.
Ở cuối hướng dẫn, bạn sẽ có một ứng dụng thông tin về cá mập đang hoạt động, ứng dụng này sẽ lấy thông tin đầu vào của user về những con cá mập yêu thích của họ và hiển thị kết quả trong trình duyệt:
Yêu cầu
-   Server  hoặc máy phát triển local  chạy Ubuntu 18.04, cùng với  user  không phải root có  quyền  sudovà firewall đang hoạt động. Để được hướng dẫn về cách cài đặt những điều này trên server 18.04, vui lòng xem hướng dẫn Cài đặt Server Ban đầu này.
- Node.js và npm được cài đặt trên máy tính hoặc server của bạn, làm theo các hướng dẫn sau về cài đặt với PPA do NodeSource quản lý .
- MongoDB được cài đặt trên máy hoặc server của bạn, làm theo Bước 1 của Cách cài đặt MongoDB trong Ubuntu 18.04 .
Bước 1 - Tạo user Mongo
Trước khi bắt đầu làm việc với mã ứng dụng, ta sẽ tạo một admin-user sẽ có quyền truy cập vào database ứng dụng của ta . User này sẽ có quyền quản trị trên bất kỳ database nào, điều này sẽ giúp bạn linh hoạt trong việc chuyển đổi và tạo database mới nếu cần.
Trước tiên, hãy kiểm tra xem MongoDB có đang chạy trên server của bạn không:
- sudo systemctl status mongodb 
Kết quả sau cho biết MongoDB đang chạy:
Output● mongodb.service - An object/document-oriented database    Loaded: loaded (/lib/systemd/system/mongodb.service; enabled; vendor preset: enabled)    Active: active (running) since Thu 2019-01-31 21:07:25 UTC; 21min ago ... Tiếp theo, mở shell Mongo để tạo user của bạn:
- mongo 
Điều này sẽ đưa bạn vào một shell quản trị:
OutputMongoDB shell version v3.6.3 connecting to: mongodb://127.0.0.1:27017 MongoDB server version: 3.6.3 ... > Bạn sẽ thấy một số cảnh báo quản trị khi bạn mở  shell  do quyền truy cập không hạn chế của bạn vào database  admin . Bạn có thể tìm hiểu thêm về cách hạn chế quyền truy cập này bằng cách đọc Cách cài đặt và bảo mật MongoDB trên Ubuntu 16.04 , khi bạn chuyển sang  cài đặt  production .
 Hiện tại, bạn có thể sử dụng quyền truy cập  của bạn  vào database  admin để tạo  user  có  quyền  userAdminAnyDatabase , cho phép truy cập được bảo vệ bằng password  vào database  của ứng dụng của bạn.
 Trong shell, chỉ định rằng bạn muốn sử dụng database  admin để tạo  user   của bạn :
- use admin 
Tiếp theo, tạo một  role  và password  bằng cách thêm tên  user  và password  bằng lệnh db.createUser . Sau khi bạn nhập lệnh này,  shell  sẽ thêm ba dấu chấm trước mỗi dòng cho đến khi lệnh hoàn tất. Đảm bảo thay thế  user  và password  được cung cấp ở đây bằng tên  user  và password   của bạn :
- db.createUser( 
-   { 
-     user: "sammy", 
-     pwd: "your_password", 
-     roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] 
-   } 
- ) 
Điều này tạo một mục nhập cho sammy  user  trong database  admin . Tên  user  bạn chọn và database  admin sẽ đóng  role  là số nhận dạng cho  user  của bạn.
Đầu ra cho toàn bộ quá trình sẽ như thế này, bao gồm thông báo cho biết rằng mục nhập đã thành công:
Output> db.createUser( ...  { ...    user: "sammy", ...    pwd: "your_password", ...    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] ...  } ...) Successfully added user: {         "user" : "sammy",         "roles" : [                 {                         "role" : "userAdminAnyDatabase",                         "db" : "admin"                 }         ] } Với user và password của bạn đã được tạo, bây giờ bạn có thể thoát khỏi Mongo shell:
- exit 
Đến đây bạn đã tạo user database của bạn , bạn có thể chuyển sang sao chép mã dự án khởi động và thêm thư viện Mongoose, cho phép bạn triển khai các schemas và mô hình cho các bộ sưu tập trong database của bạn .
Bước 2 - Thêm Mongoose và Thông tin Database vào Dự án
Các bước tiếp theo của ta sẽ là sao chép mã khởi động ứng dụng và thêm Mongoose và thông tin database MongoDB của ta vào dự án.
 Trong folder  chính của  user  không phải root của bạn, sao chép kho lưu trữ nodejs-image-demo từ tài khoản GitHub Cộng đồng DigitalOcean . Kho lưu trữ này bao gồm mã từ  cài đặt  được mô tả trong Cách tạo ứng dụng Node.js với Docker .
 Sao node_project repository  vào một folder  có tên là node_project :
- git clone https://github.com/do-community/nodejs-image-demo.git node_project 
Thay đổi thành folder  node_project :
- cd  node_project 
Trước khi sửa đổi mã dự án,  ta  hãy xem cấu trúc của dự án bằng lệnh tree .
 Mẹo: tree là một lệnh hữu ích để xem cấu trúc file  và folder  từ dòng lệnh. Bạn có thể cài đặt nó bằng lệnh sau:
- sudo apt install tree 
Để sử dụng nó, hãy cd vào một folder  nhất định và gõ tree . Bạn cũng có thể cung cấp đường dẫn đến điểm bắt đầu bằng lệnh như:
- tree /home/sammy/sammys-project 
node_project sau để xem folder  node_project :
- tree 
Cấu trúc của dự án hiện tại trông như thế này:
Output├── Dockerfile ├── README.md ├── app.js ├── package-lock.json ├── package.json └── views     ├── css     │   └── styles.css     ├── index.html     └── sharks.html  Ta  sẽ thêm các folder  vào dự án này khi  ta  chuyển qua hướng dẫn và tree sẽ là một lệnh hữu ích để giúp  ta  theo dõi tiến trình  của bạn .
 Tiếp theo, thêm gói npm mongoose vào dự án bằng lệnh npm install :
- npm install mongoose 
Lệnh này sẽ tạo một folder  node_modules trong folder  dự án của bạn, sử dụng các phần phụ thuộc được liệt kê trong file  package.json của dự án và sẽ thêm mongoose vào folder  đó. Nó cũng sẽ thêm mongoose vào các phụ thuộc được liệt kê trong file  package.json của bạn. Để có thảo luận chi tiết hơn về package.json , vui lòng xem Bước 1 trong Cách tạo ứng dụng Node.js với Docker .
Trước khi tạo bất kỳ schemas hoặc mô hình Mongoose nào, ta sẽ thêm thông tin kết nối database để ứng dụng của ta có thể kết nối với database của ta .
 Để tách biệt các mối quan tâm của ứng dụng của bạn nhiều nhất có thể, hãy tạo một file  riêng cho thông tin kết nối database  của bạn có tên là db.js Bạn có thể mở file  này bằng nano  hoặc editor bạn quen dùng :
- nano db.js 
Thứ nhất, nhập khẩu các mongoose mô-đun bằng cách sử dụng require chức năng:
const mongoose = require('mongoose'); Điều này sẽ cung cấp cho bạn quyền truy cập vào các phương thức có sẵn của Mongoose, mà bạn sẽ sử dụng để tạo kết nối với database của bạn .
 Tiếp theo, thêm các hằng số sau để xác định thông tin cho URI kết nối của Mongo. Mặc dù tên  user  và password  là tùy chọn,  ta  sẽ bao gồm chúng để  ta  có thể yêu cầu xác thực cho database   của bạn . Đảm bảo thay thế tên  user  và password  được liệt kê bên dưới bằng thông tin  của bạn  và vui lòng gọi database  là gì đó khác với ' sharkinfo ' nếu bạn muốn:
const mongoose = require('mongoose');  const MONGO_USERNAME = 'sammy'; const MONGO_PASSWORD = 'your_password'; const MONGO_HOSTNAME = '127.0.0.1'; const MONGO_PORT = '27017'; const MONGO_DB = 'sharkinfo'; Bởi vì  ta  đang chạy database   của bạn  local ,  ta  đã sử dụng 127.0.0.1 làm tên  server . Điều này sẽ thay đổi trong các bối cảnh phát triển khác: ví dụ: nếu bạn đang sử dụng một  server  database  riêng biệt hoặc làm việc với nhiều nút trong quy trình làm việc được chứa.
 Cuối cùng, xác định một hằng số cho URI và tạo kết nối bằng phương thức mongoose.connect() :
... const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;  mongoose.connect(url, {useNewUrlParser: true});  Lưu ý  trong URI,  ta  đã chỉ định authSource cho  user  của  ta  làm database  admin . Điều này là cần thiết vì  ta  đã chỉ định tên  user  trong chuỗi kết nối  của bạn . Sử dụng cờ useNewUrlParser với mongoose.connect() chỉ định rằng  ta  muốn sử dụng trình phân tích cú pháp URL mới của Mongo.
Lưu file khi bạn hoàn tất chỉnh sửa.
 Bước cuối cùng, hãy thêm thông tin kết nối database  vào file  app.js để ứng dụng có thể sử dụng. Mở app.js :
- nano app.js 
Các dòng đầu tiên của file sẽ giống như sau:
const express = require('express'); const app = express(); const router = express.Router();  const path = __dirname + '/views/'; ... Bên dưới router nghĩa hằng số router , nằm gần đầu file , thêm dòng sau:
... const router = express.Router(); const db = require('./db');  const path = __dirname + '/views/'; ... Điều này yêu cầu ứng dụng sử dụng thông tin kết nối database  được chỉ định trong db.js
Lưu file khi bạn hoàn tất chỉnh sửa.
 Với thông tin database  của bạn đã có sẵn và Mongoose được thêm vào dự án của bạn, bạn đã sẵn sàng tạo các schemas  và mô hình sẽ định hình dữ liệu trong bộ sưu tập sharks của bạn.
Bước 3 - Tạo các mô hình và schemas Mongoose
 Bước tiếp theo của  ta  sẽ là suy nghĩ về cấu trúc của bộ sưu tập sharks mà  user  sẽ tạo trong database  sharkinfo với thông tin đầu vào của họ.  Ta  muốn các tài liệu đã tạo này có cấu trúc nào? Trang thông tin về cá mập trong ứng dụng hiện tại của  ta  bao gồm một số chi tiết về các loài cá mập khác nhau và hành vi của chúng: 
Để phù hợp với chủ đề này, ta có thể yêu cầu user thêm những con cá mập mới với thông tin chi tiết về nhân vật tổng thể của chúng. Mục tiêu này sẽ định hình cách ta tạo schemas của bạn .
 Để giữ cho các schemas  và mô hình của bạn khác biệt với các phần khác của ứng dụng, hãy tạo một folder  models trong folder  dự án hiện tại:
- mkdir models 
Tiếp theo, mở một file  có tên là sharks.js để tạo schemas  và mô hình của bạn:
- nano models/sharks.js 
Nhập module  mongoose ở đầu file :
const mongoose = require('mongoose'); Dưới đây, xác định một đối tượng Schema để sử dụng làm cơ sở cho schemas  cá mập của bạn:
const mongoose = require('mongoose'); const Schema = mongoose.Schema;  Đến đây bạn  có thể xác định các trường bạn muốn đưa vào schemas   của bạn . Vì  ta  muốn tạo một bộ sưu tập với các cá mập riêng lẻ và thông tin về các hành vi của chúng, hãy bao gồm khóa name và khóa character . Thêm schemas  Shark sau bên dưới các định nghĩa hằng số của bạn:
... const Shark = new Schema ({         name: { type: String, required: true },         character: { type: String, required: true }, }); Định nghĩa này bao gồm thông tin về loại đầu vào mà ta mong đợi từ user - trong trường hợp này là một chuỗi - và liệu đầu vào đó có được yêu cầu hay không.
 Cuối cùng, tạo mô hình Shark bằng cách sử dụng hàm model() của Mongoose. Mô hình này sẽ cho phép bạn truy vấn các tài liệu từ bộ sưu tập  của bạn  và xác nhận các tài liệu mới. Thêm dòng sau vào cuối file :
... module.exports = mongoose.model('Shark', Shark) Dòng cuối cùng này làm cho mô hình Shark của  ta  có sẵn dưới dạng module  bằng cách sử dụng thuộc tính module.exports . Thuộc tính này xác định các giá trị mà module  sẽ xuất, làm cho chúng có sẵn để sử dụng ở những nơi khác trong ứng dụng.
 Tệp models/sharks.js đã hoàn thành trông giống như sau:
const mongoose = require('mongoose'); const Schema = mongoose.Schema;  const Shark = new Schema ({         name: { type: String, required: true },         character: { type: String, required: true }, });  module.exports = mongoose.model('Shark', Shark) Lưu file khi bạn hoàn tất chỉnh sửa.
 Với schemas  và mô hình Shark , bạn có thể bắt đầu làm việc trên logic sẽ xác định cách ứng dụng của bạn sẽ xử lý đầu vào của  user .
Bước 4 - Tạo bộ điều khiển
Bước tiếp theo của ta sẽ là tạo thành phần bộ điều khiển sẽ xác định cách thức nhập liệu của user được lưu vào database của ta và trả lại cho user .
Đầu tiên, tạo một folder cho bộ điều khiển:
- mkdir controllers 
Tiếp theo, mở một file  trong folder  đó có tên là sharks.js :
- nano controllers/sharks.js 
Ở đầu file ,  ta  sẽ nhập module  với mô hình Shark của  ta  để  ta  có thể sử dụng nó trong logic của bộ điều khiển.  Ta  cũng sẽ nhập mô-đun path để truy cập các tiện ích cho phép  ta  đặt đường dẫn đến biểu mẫu mà  user  sẽ nhập cá mập của họ.
 Thêm các hàm require sau vào đầu file :
const path = require('path'); const Shark = require('../models/sharks'); Tiếp theo,  ta  sẽ viết một chuỗi các hàm mà  ta  sẽ xuất với module  bộ điều khiển bằng cách sử dụng phím tắt exports của Node. Các chức năng này sẽ bao gồm ba tác vụ liên quan đến dữ liệu cá mập của  user  của  ta :
- Gửi cho user biểu mẫu đầu vào cá mập.
- Tạo một mục cá mập mới.
- Hiển thị lại cá mập cho user .
 Để bắt đầu, hãy tạo một hàm index để hiển thị trang cá mập với biểu mẫu đầu vào. Thêm chức năng này bên dưới nhập của bạn:
... exports.index = function (req, res) {     res.sendFile(path.resolve('views/sharks.html')); }; Tiếp theo, bên dưới hàm index , hãy thêm một hàm có tên là create để tạo một mục nhập cá mập mới trong bộ sưu tập sharks của bạn:
... exports.create = function (req, res) {     var newShark = new Shark(req.body);     console.log(req.body);     newShark.save(function (err) {             if(err) {             res.status(400).send('Unable to save shark to database');         } else {             res.redirect('/sharks/getshark');         }   });                }; Hàm này sẽ được gọi khi  user  đăng dữ liệu cá mập vào biểu mẫu trên trang sharks.html .  Ta  sẽ tạo tuyến với điểm cuối POST này ở phần sau của hướng dẫn khi  ta  tạo các tuyến ứng dụng  của bạn . Với phần body của yêu cầu POST, hàm create của  ta  sẽ tạo một đối tượng tài liệu cá mập mới, ở đây được gọi là newShark , sử dụng mô hình Shark mà  ta  đã nhập.  Ta  đã thêm một phương thức console.log để xuất mục nhập cá mập vào console  nhằm kiểm tra xem phương thức POST của  ta  có hoạt động như dự kiến hay không, nhưng bạn có thể bỏ qua điều này nếu muốn.
 Sử dụng đối tượng newShark , hàm create sau đó sẽ gọi phương thức model.save() của Mongoose để tạo một tài liệu cá mập mới bằng cách sử dụng các khóa bạn đã xác định trong mô hình Shark . Hàm gọi lại này tuân theo mô hình gọi lại Node tiêu chuẩn : callback(error, results) . Trong trường hợp xảy ra lỗi,  ta  sẽ gửi thông báo báo lỗi cho  user  của  ta  và trong trường hợp thành công,  ta  sẽ sử dụng phương thức res.redirect() để đưa  user  đến điểm cuối sẽ hiển thị thông tin cá mập của họ trở lại chúng trong trình duyệt.
 Cuối cùng, chức năng list sẽ hiển thị lại nội dung của bộ sưu tập cho  user . Thêm mã sau vào bên dưới hàm create :
... exports.list = function (req, res) {         Shark.find({}).exec(function (err, sharks) {                 if (err) {                         return res.send(500, err);                 }                 res.render('getshark', {                         sharks: sharks              });         }); }; Hàm này sử dụng mô hình Shark với phương thức model.find() của Mongoose để trả về các cá mập đã được nhập vào bộ sưu tập sharks . Nó thực hiện điều này bằng cách trả về đối tượng truy vấn - trong trường hợp này là tất cả các mục trong bộ sưu tập sharks - như một lời hứa, bằng cách sử dụng hàm executive exec() của Mongoose. Trong trường hợp có lỗi, chức năng gọi lại sẽ gửi lỗi 500.
 Đối tượng truy vấn được trả về với bộ sưu tập sharks sẽ được hiển thị trong một trang getshark mà  ta  sẽ tạo trong bước tiếp theo bằng cách sử dụng ngôn ngữ tạo khuôn mẫu EJS .
Tệp đã hoàn thành sẽ giống như sau:
const path = require('path'); const Shark = require('../models/sharks');  exports.index = function (req, res) {     res.sendFile(path.resolve('views/sharks.html')); };  exports.create = function (req, res) {     var newShark = new Shark(req.body);     console.log(req.body);     newShark.save(function (err) {             if(err) {             res.status(400).send('Unable to save shark to database');         } else {             res.redirect('/sharks/getshark');         }   });                };  exports.list = function (req, res) {         Shark.find({}).exec(function (err, sharks) {                 if (err) {                         return res.send(500, err);                 }                 res.render('getshark', {                         sharks: sharks              });         }); }; Lưu ý mặc dù ta không sử dụng các hàm mũi tên ở đây, bạn có thể cần đưa chúng vào khi lặp lại mã này trong quá trình phát triển của riêng mình.
Lưu file khi bạn hoàn tất chỉnh sửa.
 Trước khi chuyển sang bước tiếp theo, bạn có thể chạy lại tree từ folder  node_project  của bạn  để xem cấu trúc của dự án tại thời điểm này. Lần này, để ngắn gọn,  ta  sẽ yêu cầu tree bỏ qua folder  node_modules bằng cách sử dụng tùy chọn -I :
- tree -I node_modules 
Với những bổ sung bạn đã thực hiện, cấu trúc dự án của bạn sẽ giống như sau:
Output├── Dockerfile ├── README.md ├── app.js ├── controllers │   └── sharks.js ├── db.js ├── models │   └── sharks.js ├── package-lock.json ├── package.json └── views     ├── css     │   └── styles.css     ├── index.html     └── sharks.html Đến đây bạn đã có một thành phần bộ điều khiển để hướng dẫn cách thức nhập liệu của user được lưu và trả lại cho user , bạn có thể chuyển sang tạo các khung nhìn sẽ triển khai logic của bộ điều khiển của bạn.
Bước 5 - Sử dụng EJS và Express Middleware để thu thập và kết xuất dữ liệu
 Để cho phép ứng dụng của  ta  hoạt động với dữ liệu  user ,  ta  sẽ thực hiện hai việc: đầu tiên,  ta  sẽ bao gồm một chức năng phần mềm trung gian Express tích hợp sẵn, urlencoded() , cho phép ứng dụng của  ta  phân tích dữ liệu đã nhập của  user . Thứ hai,  ta  sẽ thêm các thẻ mẫu vào chế độ xem  của bạn  để cho phép tương tác động với dữ liệu  user  trong mã của  ta .
 Để làm việc với hàm urlencoded() của Express, trước tiên hãy mở file  app.js của bạn:
- nano app.js 
Bên trên hàm express.static() , hãy thêm dòng sau:
... app.use(express.urlencoded({ extended: true })); app.use(express.static(path)); ... Thêm chức năng này sẽ cho phép truy cập vào dữ liệu POST được phân tích cú pháp từ biểu mẫu thông tin cá mập của  ta .  Ta  đang chỉ định true với tùy chọn extended để cho phép linh hoạt hơn trong loại dữ liệu mà ứng dụng của  ta  sẽ phân tích cú pháp (bao gồm những thứ như các đối tượng lồng nhau). Vui lòng xem tài liệu chức năng để biết thêm thông tin về các tùy chọn.
Lưu file khi bạn hoàn tất chỉnh sửa.
 Tiếp theo,  ta  sẽ thêm chức năng mẫu vào chế độ xem của  ta . Đầu tiên, hãy cài đặt gói ejs với npm install :
- npm install ejs 
Tiếp theo, mở file  sharks.html trong folder  views :
- nano views/sharks.html 
Ở Bước 3, ta đã xem trang này để xác định cách ta nên viết schemas và mô hình Mongoose của bạn :
Bây giờ, thay vì có bố cục hai cột, ta sẽ giới thiệu cột thứ ba với biểu mẫu nơi user có thể nhập thông tin về cá mập.
 Bước đầu tiên, hãy thay đổi kích thước của các cột hiện có thành 4 để tạo ba cột có kích thước bằng nhau.  Lưu ý  bạn  cần  thực hiện thay đổi này trên hai dòng hiện đang đọc <div class="col-lg-6"> . Cả hai đều sẽ trở thành <div class="col-lg- 4 "> :
... <div class="container">     <div class="row">         <div class="col-lg-4">             <p>                 <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.                 </div>                 <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">             </p>         </div>         <div class="col-lg-4">             <p>                 <div class="caption">Other sharks are known to be friendly and welcoming!</div>                 <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">             </p>         </div>     </div>   </div>   </html>  Để biết phần giới thiệu về hệ thống lưới của Bootstrap, bao gồm bố cục hàng và cột của nó, vui lòng xem phần giới thiệu này về Bootstrap .
 Tiếp theo, thêm một cột khác bao gồm điểm cuối được đặt tên cho yêu cầu ĐĂNG với dữ liệu cá mập của  user  và các thẻ mẫu EJS sẽ nắm bắt dữ liệu đó. Cột này sẽ nằm bên dưới các </p> đóng </p> và </div> từ cột trước đó và phía trên các thẻ đóng cho hàng, containers  và  trang HTML . Các thẻ đóng này đã được đặt sẵn trong mã của bạn; chúng cũng được đánh dấu bên dưới với các  comment . Giữ nguyên chúng khi bạn thêm mã sau để tạo cột mới:
...        </p> <!-- closing p from previous column -->    </div> <!-- closing div from previous column --> <div class="col-lg-4">             <p>                 <form action="/sharks/addshark" method="post">                     <div class="caption">Enter Your Shark</div>                     <input type="text" placeholder="Shark Name" name="name" <%=sharks[i].name; %>                     <input type="text" placeholder="Shark Character" name="character" <%=sharks[i].character; %>                     <button type="submit">Submit</button>                 </form>             </p>         </div>      </div> <!-- closing div for row --> </div> <!-- closing div for container -->  </html> <!-- closing html tag --> Trong thẻ form , bạn đang thêm điểm cuối "/sharks/addshark" cho dữ liệu cá mập của  user  và chỉ định phương thức POST để gửi dữ liệu đó. Trong các trường đầu vào, bạn đang chỉ định các trường cho "Shark Name" và "Shark Character" , phù hợp với mô hình Shark bạn đã xác định trước đó.
 Để thêm đầu vào của  user  vào bộ sưu tập sharks của bạn, bạn đang sử dụng các thẻ mẫu EJS ( <%= , %> ) cùng với cú pháp JavaScript để ánh xạ các mục nhập của  user  đến các trường thích hợp trong tài liệu mới được tạo. Để biết thêm về các đối tượng JavaScript, vui lòng xem bài viết của  ta  về Tìm hiểu các đối tượng JavaScript . Để biết thêm về các thẻ mẫu EJS, vui lòng xem tài liệu EJS .
Toàn bộ containers có tất cả ba cột, bao gồm cả cột có biểu mẫu nhập cá mập của bạn, sẽ trông như thế này khi hoàn tất:
... <div class="container">     <div class="row">         <div class="col-lg-4">             <p>                 <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.                 </div>                 <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">             </p>         </div>         <div class="col-lg-4">             <p>                 <div class="caption">Other sharks are known to be friendly and welcoming!</div>                 <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">             </p>         </div>     <div class="col-lg-4">             <p>                 <form action="/sharks/addshark" method="post">                     <div class="caption">Enter Your Shark</div>                     <input type="text" placeholder="Shark Name" name="name" <%=sharks[i].name; %>                     <input type="text" placeholder="Shark Character" name="character" <%=sharks[i].character; %>                     <button type="submit">Submit</button>                 </form>             </p>         </div>     </div>   </div>  </html> Lưu file khi bạn hoàn tất chỉnh sửa.
Đến đây bạn đã có cách để thu thập thông tin đầu vào của user , bạn có thể tạo một điểm cuối để hiển thị những con cá mập đã trả lại và thông tin nhân vật liên quan của chúng.
 Sao chép file  sharks.html mới được sửa đổi vào file  có tên là getshark.html :
- cp views/sharks.html views/getshark.html 
Mở getshark.html :
- nano views/getshark.html 
Bên trong file ,  ta  sẽ sửa đổi cột mà  ta  đã sử dụng để tạo biểu mẫu nhập cá mập bằng cách thay thế nó bằng một cột sẽ hiển thị các cá mập trong bộ sưu tập sharks của  ta .   , mã của bạn sẽ nằm giữa các </p> và </div> từ cột trước đó và các thẻ đóng cho hàng, containers  và  trang HTML . Hãy nhớ giữ nguyên các thẻ này khi bạn thêm mã sau để tạo cột:
...        </p> <!-- closing p from previous column -->    </div> <!-- closing div from previous column --> <div class="col-lg-4">            <p>               <div class="caption">Your Sharks</div>                   <ul>                      <% sharks.forEach(function(shark) { %>                         <p>Name: <%= shark.name %></p>                         <p>Character: <%= shark.character %></p>                      <% }); %>                   </ul>             </p>         </div>     </div> <!-- closing div for row --> </div> <!-- closing div for container -->  </html> <!-- closing html tag --> Ở đây bạn đang sử dụng các thẻ mẫu EJS và phương thức forEach() để xuất từng giá trị trong bộ sưu tập sharks  của bạn , bao gồm thông tin về loài cá mập được thêm  mới nhất .
 Toàn bộ containers  có cả ba cột, bao gồm cả cột có bộ sưu tập sharks của bạn, sẽ trông như thế này khi hoàn thành:
... <div class="container">     <div class="row">         <div class="col-lg-4">             <p>                 <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.                 </div>                 <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">             </p>         </div>         <div class="col-lg-4">             <p>                 <div class="caption">Other sharks are known to be friendly and welcoming!</div>                 <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">             </p>         </div>     <div class="col-lg-4">             <p>               <div class="caption">Your Sharks</div>                   <ul>                      <% sharks.forEach(function(shark) { %>                         <p>Name: <%= shark.name %></p>                         <p>Character: <%= shark.character %></p>                      <% }); %>                   </ul>             </p>         </div>     </div>   </div>  </html> Lưu file khi bạn hoàn tất chỉnh sửa.
 Để ứng dụng sử dụng các mẫu bạn đã tạo, bạn  cần  thêm một vài dòng vào file  app.js  của bạn . Mở lại:
- nano app.js 
Ở trên nơi bạn đã thêm hàm express.urlencoded() , hãy thêm các dòng sau:
... app.engine('html', require('ejs').renderFile); app.set('view engine', 'html'); app.use(express.urlencoded({ extended: true })); app.use(express.static(path));  ... Phương thức app.engine yêu cầu ứng dụng ánh xạ công cụ mẫu EJS tới các file  HTML, trong khi app.set xác định công cụ xem mặc định.
 Tệp app.js của bạn bây giờ sẽ giống như sau:
const express = require('express'); const app = express(); const router = express.Router(); const db = require('./db');  const path = __dirname + '/views/'; const port = 8080;  router.use(function (req,res,next) {   console.log('/' + req.method);   next(); });  router.get('/',function(req,res){   res.sendFile(path + 'index.html'); });  router.get('/sharks',function(req,res){   res.sendFile(path + 'sharks.html'); });  app.engine('html', require('ejs').renderFile); app.set('view engine', 'html'); app.use(express.urlencoded({ extended: true })); app.use(express.static(path)); app.use('/', router);  app.listen(port, function () {   console.log('Example app listening on port 8080!') }) Đến đây bạn đã tạo các khung nhìn có thể hoạt động động với dữ liệu user , đã đến lúc tạo các tuyến của dự án để tập hợp các khung nhìn và logic bộ điều khiển của bạn.
Bước 6 - Tạo các tuyến đường
 Bước cuối cùng để kết hợp các thành phần của ứng dụng lại với nhau sẽ là tạo các tuyến.  Ta  sẽ phân tách các tuyến đường  của bạn  theo chức năng, bao gồm một tuyến đường đến trang đích của ứng dụng và một tuyến đường khác đến trang cá mập của  ta . Lộ trình sharks của  ta  sẽ là nơi  ta  tích hợp logic của bộ điều khiển của  ta  với các khung nhìn mà  ta  đã tạo ở bước trước.
 Đầu tiên, tạo một folder  định routes :
- mkdir routes 
Tiếp theo, mở một file  có tên là index.js trong folder  này:
- nano routes/index.js 
Trước tiên, file  này sẽ nhập các đối tượng express , router và path , cho phép  ta  xác định các tuyến đường mà  ta  muốn xuất với đối tượng router và giúp nó có thể hoạt động động với các đường dẫn file . Thêm mã sau vào đầu file :
const express = require('express'); const router = express.Router(); const path = require('path'); Tiếp theo, thêm hàm router.use sau, hàm này tải một chức năng phần mềm trung gian sẽ ghi lại các yêu cầu của bộ định tuyến và chuyển chúng vào tuyến của ứng dụng:
...  router.use (function (req,res,next) {   console.log('/' + req.method);   next(); }); Các yêu cầu tới folder  root  của ứng dụng của  ta  sẽ được chuyển đến đây trước tiên và từ đây  user  sẽ được dẫn đến trang đích của ứng dụng của  ta , lộ trình mà  ta  sẽ xác định tiếp theo. Thêm mã sau vào bên dưới hàm router.use để xác định đường dẫn đến trang đích:
...  router.get('/',function(req,res){   res.sendFile(path.resolve('views/index.html')); }); Khi  user  truy cập ứng dụng của  ta , nơi đầu tiên  ta  muốn đưa họ đến trang đích index.html mà  ta  có trong folder  views .
 Cuối cùng, để làm cho các tuyến này có thể truy cập được dưới dạng module  có thể nhập ở nơi khác trong ứng dụng, hãy thêm biểu thức đóng vào cuối file  để xuất đối tượng router :
...  module.exports = router; Tệp đã hoàn thành sẽ giống như sau:
const express = require('express'); const router = express.Router(); const path = require('path');  router.use (function (req,res,next) {   console.log('/' + req.method);   next(); });  router.get('/',function(req,res){   res.sendFile(path.resolve('views/index.html')); });  module.exports = router; Lưu file này khi bạn hoàn tất chỉnh sửa.
 Tiếp theo, mở một file  có tên sharks.js để xác định cách ứng dụng sẽ sử dụng các điểm cuối và chế độ xem khác nhau mà  ta  đã tạo để làm việc với đầu vào cá mập của  user :
- nano routes/sharks.js 
Ở đầu file , nhập các đối tượng express và router :
const express = require('express'); const router = express.Router(); Tiếp theo, nhập một module  có tên shark sẽ cho phép bạn làm việc với các chức năng đã xuất mà bạn đã xác định với bộ điều khiển  của bạn :
const express = require('express'); const router = express.Router(); const shark = require('../controllers/sharks');  Như vậy,  bạn có thể tạo các tuyến đường bằng cách sử dụng index , create và list hàm mà bạn đã xác định trong file  điều khiển sharks  của bạn . Mỗi tuyến đường sẽ được liên kết với phương thức HTTP thích hợp: NHẬN trong trường hợp hiển thị trang đích thông tin cá mập chính và trả lại danh sách cá mập cho  user  và ĐĂNG trong trường hợp tạo mục nhập cá mập mới:
...  router.get('/', function(req, res){     shark.index(req,res); });  router.post('/addshark', function(req, res) {     shark.create(req,res); });  router.get('/getshark', function(req, res) {     shark.list(req,res); }); Mỗi tuyến sử dụng chức năng có liên quan trong controllers/sharks.js , vì  ta  đã làm cho module  đó có thể truy cập được  bằng lệnh  nó ở đầu file  này.
 Cuối cùng, đóng file  bằng cách đính kèm các tuyến đường này vào đối tượng router và xuất chúng:
...  module.exports = router; Tệp đã hoàn thành sẽ giống như sau:
const express = require('express'); const router = express.Router(); const shark = require('../controllers/sharks');  router.get('/', function(req, res){     shark.index(req,res); });  router.post('/addshark', function(req, res) {     shark.create(req,res); });  router.get('/getshark', function(req, res) {     shark.list(req,res); });  module.exports = router; Lưu file khi bạn hoàn tất chỉnh sửa.
 Bước cuối cùng để làm cho các tuyến đường này có thể truy cập được vào ứng dụng của bạn là thêm chúng vào app.js Mở lại file  đó:
- nano app.js 
Bên dưới hằng số db của bạn, hãy thêm nhập sau cho các tuyến đường của bạn:
... const db = require('./db'); const sharks = require('./routes/sharks'); Tiếp theo, thay thế hàm app.use hiện đang gắn đối tượng router của bạn bằng dòng sau, dòng này sẽ gắn module  bộ định tuyến sharks :
... app.use(express.static(path)); app.use('/sharks', sharks);  app.listen(port, function () {         console.log("Example app listening on port 8080!") })  Đến đây bạn  có thể xóa các tuyến đường đã  được định nghĩa  đó trong file  này, vì bạn đang nhập các tuyến đường của ứng dụng bằng module  bộ định tuyến sharks .
 Phiên bản cuối cùng của file  app.js của bạn sẽ trông giống như sau:
const express = require('express'); const app = express(); const router = express.Router(); const db = require('./db'); const sharks = require('./routes/sharks');  const path = __dirname + '/views/'; const port = 8080;  app.engine('html', require('ejs').renderFile); app.set('view engine', 'html'); app.use(express.urlencoded({ extended: true })); app.use(express.static(path)); app.use('/sharks', sharks);  app.listen(port, function () {   console.log('Example app listening on port 8080!') }) Lưu file khi bạn hoàn tất chỉnh sửa.
  Đến đây bạn  có thể chạy lại tree để xem cấu trúc cuối cùng của dự án  của bạn :
- tree -I node_modules 
Cấu trúc dự án của bạn bây giờ sẽ giống như sau:
Output├── Dockerfile ├── README.md ├── app.js ├── controllers │   └── sharks.js ├── db.js ├── models │   └── sharks.js ├── package-lock.json ├── package.json ├── routes │   ├── index.js │   └── sharks.js └── views     ├── css     │   └── styles.css     ├── getshark.html     ├── index.html     └── sharks.html Với tất cả các thành phần ứng dụng của bạn đã được tạo và tại chỗ, bây giờ bạn đã sẵn sàng thêm một con cá mập thử nghiệm vào database của bạn !
 Nếu bạn đã làm theo hướng dẫn  cài đặt   server  ban đầu trong  yêu cầu , bạn  cần  phải sửa đổi firewall   của bạn , vì nó hiện chỉ cho phép lưu lượng SSH. Để cho phép giao thông đến cổng 8080 chạy:
- sudo ufw allow 8080 
Khởi động ứng dụng:
- node app.js 
Tiếp theo,  chuyển  trình duyệt của bạn đến http:// your_server_ip :8080 . Bạn sẽ thấy trang đích sau: 
Nhấp vào nút Nhận thông tin cá mập . Bạn sẽ thấy trang thông tin sau, với biểu mẫu nhập cá mập được thêm vào:
 Trong biểu mẫu, hãy thêm một con cá mập mà bạn chọn. Với mục đích của phần trình diễn này,  ta  sẽ thêm Megalodon Shark vào trường Shark Name và Ancient vào trường Shark Character : 
Bấm vào nút Gửi . Bạn sẽ thấy một trang với thông tin cá mập này được hiển thị lại cho bạn:
Bạn cũng sẽ thấy kết quả trong console của bạn cho biết rằng con cá mập đã được thêm vào bộ sưu tập của bạn:
OutputExample app listening on port 8080! { name: 'Megalodon Shark', character: 'Ancient' } Nếu bạn muốn tạo mục nhập cá mập mới, hãy quay lại trang Cá mập và lặp lại quy trình thêm cá mập.
Đến đây bạn có một ứng dụng thông tin cá mập đang hoạt động cho phép user thêm thông tin về những con cá mập yêu thích của họ.
Kết luận
Trong hướng dẫn này, bạn đã xây dựng một ứng dụng Node bằng cách tích hợp database MongoDB và viết lại logic của ứng dụng bằng cách sử dụng mẫu kiến trúc MVC. Ứng dụng này có thể hoạt động như một điểm khởi đầu tốt cho một ứng dụng CRUD chính thức.
Để biết thêm tài nguyên về mẫu MVC trong các ngữ cảnh khác, vui lòng xem loạt bài Phát triển Django của ta hoặc Cách Xây dựng Ứng dụng Web Hiện đại để Quản lý Thông tin Khách hàng với Django và React trên Ubuntu 18.04 .
Để biết thêm thông tin về cách làm việc với MongoDB, vui lòng xem thư viện hướng dẫn của ta về MongoDB .
Các tin liên quan
 

