如何将MongoDB与您的节点应用程序集成

来自菜鸟教程
跳转至:导航、​搜索

介绍

当您使用 Node.js 时,您可能会发现自己正在开发一个存储和查询数据的项目。 在这种情况下,您将需要选择对您的应用程序的数据和查询类型有意义的数据库解决方案。

在本教程中,您会将 MongoDB 数据库与现有的 Node 应用程序集成。 如果您的数据要求包括可扩展性和灵活性,NoSQL 数据库 (如 MongoDB)会很有用。 MongoDB 还与 Node 很好地集成,因为它被设计为与 JSON 对象异步工作。

要将 MongoDB 集成到您的项目中,您将使用 Object Document Mapper (ODM) Mongoose 为您的应用程序数据创建模式和模型。 这将允许您按照 模型-视图-控制器 (MVC) 架构模式组织应用程序代码,这使您可以将应用程序如何处理用户输入的逻辑与数据的结构化和呈现方式分开用户。 使用这种模式可以通过在代码库中引入关注点分离来促进未来的测试和开发。

在本教程结束时,您将拥有一个工作的鲨鱼信息应用程序,它将接受用户关于他们最喜欢的鲨鱼的输入并在浏览器中显示结果:

先决条件

  • 运行 Ubuntu 18.04 的本地开发机器或服务器,以及具有 sudo 权限和活动防火墙的非 root 用户。 有关如何在 18.04 服务器上进行设置的指导,请参阅此 初始服务器设置指南
  • Node.js 和 npm 安装在您的机器或服务器上,遵循 这些关于使用 NodeSource 管理的 PPA 安装的说明。
  • MongoDB 按照 如何在 Ubuntu 18.04 中安装 MongoDB 的第 1 步安装在您的机器或服务器上。

第 1 步 — 创建 Mongo 用户

在开始使用应用程序代码之前,我们将创建一个可以访问应用程序数据库的管理用户。 该用户将拥有任何数据库的管理权限,这将使您能够根据需要灵活地切换和创建新数据库。

首先,检查 MongoDB 是否在您的服务器上运行:

sudo systemctl status mongodb

以下输出表明 MongoDB 正在运行:

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
...

接下来,打开 Mongo shell 以创建您的用户:

mongo

这将使您进入管理外壳:

OutputMongoDB shell version v3.6.3
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.3
...
>

由于您可以不受限制地访问 admin 数据库,因此您在打开 shell 时会看到一些管理警告。 您可以通过阅读 如何在 Ubuntu 16.04 上安装和保护 MongoDB 来了解有关限制此访问的更多信息,以便您进入生产设置。

现在,您可以使用您对 admin 数据库的访问权限来创建一个具有 userAdminAnyDatabase 权限的用户,这将允许密码保护访问您的应用程序的数据库。

在 shell 中,指定您要使用 admin 数据库来创建您的用户:

use admin

接下来,通过使用 db.createUser 命令添加用户名和密码来创建角色和密码。 键入此命令后,shell 将在每行之前添加三个点,直到命令完成。 请务必将此处提供的用户名和密码替换为您自己的用户名和密码:

db.createUser(
  {
    user: "sammy",
    pwd: "your_password",
    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
  }
)

这会在 admin 数据库中为用户 sammy 创建一个条目。 您选择的用户名和 admin 数据库将用作您的用户的标识符。

整个过程的输出将如下所示,包括指示输入成功的消息:

Output> db.createUser(
...  {
...    user: "sammy",
...    pwd: "your_password",
...    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
...  }
...)
Successfully added user: {
        "user" : "sammy",
        "roles" : [
                {
                        "role" : "userAdminAnyDatabase",
                        "db" : "admin"
                }
        ]
}

创建用户名和密码后,您现在可以退出 Mongo shell:

exit

现在您已经创建了数据库用户,您可以继续克隆启动项目代码并添加 Mongoose 库,这将允许您为数据库中的集合实现模式和模型。

第 2 步 — 将 Mongoose 和数据库信息添加到项目中

我们的下一步将是克隆应用程序启动代码并将 Mongoose 和我们的 MongoDB 数据库信息添加到项目中。

在非 root 用户的主目录中,从 DigitalOcean 社区 GitHub 帐户 克隆 nodejs-image-demo 存储库。 此存储库包含 如何使用 Docker 构建 Node.js 应用程序中描述的设置中的代码。

将存储库克隆到名为 node_project 的目录中:

git clone https://github.com/do-community/nodejs-image-demo.git node_project

切换到 node_project 目录:

cd  node_project

在修改工程代码之前,我们先看一下使用tree命令的工程结构。

提示: tree is a useful command for viewing file and directory structures from the command line. You can install it with the following command:

sudo apt install tree

要使用它,将 cd 放入给定目录并键入 tree。 您还可以使用以下命令提供到起点的路径:

tree /home/sammy/sammys-project

键入以下内容以查看 node_project 目录:

tree

当前项目的结构如下所示:

Output├── Dockerfile
├── README.md
├── app.js
├── package-lock.json
├── package.json
└── views
    ├── css
    │   └── styles.css
    ├── index.html
    └── sharks.html

随着教程的进行,我们将向这个项目添加目录,tree 将是一个有用的命令,可以帮助我们跟踪我们的进度。

接下来,使用 npm install 命令将 mongoose npm 包添加到项目中:

npm install mongoose

此命令将使用项目的 package.json 文件中列出的依赖项在您的项目目录中创建一个 node_modules 目录,并将 mongoose 添加到该目录。 它还将 mongoose 添加到 package.json 文件中列出的依赖项中。 有关 package.json 的更详细讨论,请参阅 How To Build a Node.js Application with Docker 中的 Step 1

在创建任何 Mongoose 模式或模型之前,我们将添加我们的数据库连接信息,以便我们的应用程序能够连接到我们的数据库。

为了尽可能地分离您的应用程序的关注点,请为您的数据库连接信息创建一个名为 db.js 的单独文件。 您可以使用 nano 或您喜欢的编辑器打开此文件:

nano db.js

首先,使用require函数导入mongoose模块

~/node_project/db.js

const mongoose = require('mongoose');

这将使您能够访问 Mongoose 的内置方法,您将使用这些方法来创建与数据库的连接。

接下来,添加以下 constants 来定义 Mongo 的连接 URI 的信息。 虽然用户名和密码是可选的,但我们将包括它们,以便我们可以要求对我们的数据库进行身份验证。 请务必将下面列出的用户名和密码替换为您自己的信息,如果您愿意,可以随意将数据库称为 'sharkinfo' 以外的名称:

~/node_project/db.js

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';

因为我们在本地运行数据库,所以我们使用 127.0.0.1 作为主机名。 这会在其他开发环境中发生变化:例如,如果您使用单独的数据库服务器或在容器化工作流中使用多个节点。

最后,为 URI 定义一个常量并使用 mongoose.connect() 方法创建连接:

~/node_project/db.js

...
const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;

mongoose.connect(url, {useNewUrlParser: true});

请注意,在 URI 中,我们为我们的用户指定了 authSource 作为 admin 数据库。 这是必要的,因为我们在连接字符串中指定了用户名。 将 useNewUrlParser 标志与 mongoose.connect() 一起使用指定我们要使用 Mongo 的 新 URL 解析器

完成编辑后保存并关闭文件。

作为最后一步,将数据库连接信息添加到 app.js 文件中,以便应用程序可以使用它。 打开app.js

nano app.js

该文件的第一行将如下所示:

~/node_project/app.js

const express = require('express');
const app = express();
const router = express.Router();

const path = __dirname + '/views/';
...

在位于文件顶部附近的 router 常量定义下方,添加以下行:

~/node_project/app.js

...
const router = express.Router();
const db = require('./db');

const path = __dirname + '/views/';
...

这告诉应用程序使用 db.js 中指定的数据库连接信息。

完成编辑后保存并关闭文件。

准备好数据库信息并将 Mongoose 添加到您的项目后,您就可以创建模式和模型来塑造 sharks 集合中的数据。

第 3 步——创建 Mongoose 模式和模型

我们下一步将考虑用户将在 sharkinfo 数据库中使用输入创建的 sharks 集合的结构。 我们希望这些创建的文档具有什么结构? 我们当前应用程序的鲨鱼信息页面包括一些关于不同鲨鱼及其行为的详细信息:

为了与这个主题保持一致,我们可以让用户添加新的鲨鱼,并提供有关其整体性格的详细信息。 这个目标将塑造我们创建模式的方式。

为了使您的模式和模型与应用程序的其他部分不同,请在当前项目目录中创建一个 models 目录:

mkdir models

接下来,打开一个名为 sharks.js 的文件来创建您的模式和模型:

nano models/sharks.js

导入文件顶部的 mongoose 模块:

~/node_project/models/sharks.js

const mongoose = require('mongoose');

在此之下,定义一个 Schema 对象用作您的鲨鱼模式的基础:

~/node_project/models/sharks.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

您现在可以定义要包含在架构中的字段。 因为我们想要创建一个包含单个鲨鱼及其行为信息的集合,所以让我们包含一个 name key 和一个 character 键。 在常量定义下添加以下 Shark 模式:

~/node_project/models/sharks.js

...
const Shark = new Schema ({
        name: { type: String, required: true },
        character: { type: String, required: true },
});

此定义包括有关我们期望用户输入的类型(在本例中为 string)以及是否需要该输入的信息。

最后,使用 Mongoose 的 model() 函数 创建 Shark 模型。 此模型将允许您从集合中查询文档并验证新文档。 在文件底部添加以下行:

~/node_project/models/sharks.js

...
module.exports = mongoose.model('Shark', Shark)

最后一行使用 module.exports 属性 使我们的 Shark 模型可用作模块。 此属性定义模块将导出的值,使它们可用于应用程序的其他地方。

完成的 models/sharks.js 文件如下所示:

~/node_project/models/sharks.js

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)

完成编辑后保存并关闭文件。

有了 Shark 模式和模型,您就可以开始研究确定应用程序如何处理用户输入的逻辑。

第 4 步 - 创建控制器

我们的下一步将是创建控制器组件,该组件将确定用户输入如何保存到我们的数据库并返回给用户。

首先,为控制器创建一个目录:

mkdir controllers

接下来,在该文件夹中打开一个名为 sharks.js 的文件:

nano controllers/sharks.js

在文件的顶部,我们将使用我们的 Shark 模型导入模块,以便我们可以在控制器的逻辑中使用它。 我们还将导入 路径模块 以访问实用程序,这些实用程序允许我们将路径设置为用户输入鲨鱼的表单。

将以下 require 函数添加到文件开头:

~/node_project/controllers/sharks.js

const path = require('path');
const Shark = require('../models/sharks');

接下来,我们将编写一系列函数,我们将使用 Node 的 导出快捷方式 将这些函数与控制器模块一起导出。 这些功能将包括与我们用户的鲨鱼数据相关的三个任务:

  • 向用户发送鲨鱼输入表单。
  • 创建一个新的鲨鱼条目。
  • 向用户展示鲨鱼。

首先,创建一个 index 函数来显示带有输入表单的鲨鱼页面。 在您的导入下方添加此功能:

~/node_project/controllers/sharks.js

...
exports.index = function (req, res) {
    res.sendFile(path.resolve('views/sharks.html'));
};

接下来,在 index 函数下方,添加一个名为 create 的函数,以在 sharks 集合中创建一个新的鲨鱼条目:

~/node_project/controllers/sharks.js

...
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');
        }
  });
               };

当用户在 sharks.html 页面上的表单中发布鲨鱼数据时,将调用此函数。 我们将在本教程后面创建应用程序的路由时使用此 POST 端点创建路由。 使用 POST 请求的 body,我们的 create 函数将使用我们的 Shark 模型创建一个新的鲨鱼文档对象,这里称为 newShark已经进口了。 我们添加了一个 console.log 方法 来将鲨鱼条目输出到控制台,以检查我们的 POST 方法是否按预期工作,但如果您愿意,可以随意省略它。

使用 newShark 对象,create 函数将调用 Mongoose 的 model.save() 方法 使用您在 Shark 型号。 这个回调函数遵循标准节点回调模式callback(error, results)。 在错误的情况下,我们将向我们的用户发送一条报告错误的消息,在成功的情况下,我们将使用 res.redirect() 方法 将用户发送到将在浏览器中将他们的鲨鱼信息呈现给他们。

最后,list 函数将集合的内容显示给用户。 在create函数下面添加如下代码:

~/node_project/controllers/sharks.js

...
exports.list = function (req, res) {
        Shark.find({}).exec(function (err, sharks) {
                if (err) {
                        return res.send(500, err);
                }
                res.render('getshark', {
                        sharks: sharks
             });
        });
};

此函数使用 Shark 模型和 Mongoose 的 model.find() 方法 返回已输入到 sharks 集合中的鲨鱼。 它通过使用 Mongoose 的 exec() 函数 作为承诺返回查询对象(在本例中为 sharks 集合中的所有条目)来实现这一点。 如果发生错误,回调函数将发送 500 错误。

返回的带有 sharks 集合的查询对象将呈现在 getshark 页面中,我们将在下一步中使用 EJS 模板语言创建该页面。

完成的文件将如下所示:

~/node_project/controllers/sharks.js

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
             });
        });
};

请记住,虽然我们在这里没有使用 箭头函数 ,但您可能希望在您自己的开发过程中迭代此代码时包含它们。

完成编辑后保存并关闭文件。

在继续下一步之前,您可以从 node_project 目录再次运行 tree 以查看此时项目的结构。 这次,为了简洁起见,我们将告诉 tree 使用 -I 选项省略 node_modules 目录:

tree -I node_modules

通过您所做的添加,您的项目结构将如下所示:

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

现在您有了一个控制器组件来指导如何保存用户输入并将其返回给用户,您可以继续创建将实现控制器逻辑的视图。

第 5 步 — 使用 EJS 和 Express 中间件收集和呈现数据

为了使我们的应用程序能够处理用户数据,我们将做两件事:首先,我们将包含一个内置的 Express 中间件函数,urlencoded(),这将使我们的应用程序能够解析用户输入的数据. 其次,我们将在视图中添加模板标签,以在我们的代码中实现与用户数据的动态交互。

要使用 Express 的 urlencoded() 函数,首先打开您的 app.js 文件:

nano app.js

express.static() 函数上方,添加以下行:

~/node_project/app.js

...
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path));
...

添加此功能将允许从我们的鲨鱼信息表单访问已解析的 POST 数据。 我们使用 extended 选项指定 true 以使我们的应用程序将解析的数据类型(包括嵌套对象之类的内容)具有更大的灵活性。 有关选项的更多信息,请参阅 功能文档

完成编辑后保存并关闭文件。

接下来,我们将向视图添加模板功能。 首先,用npm install安装ejs包

npm install ejs

接下来,打开views文件夹下的sharks.html文件:

nano views/sharks.html

在第 3 步中,我们查看了此页面以确定我们应该如何编写 Mongoose 模式和模型:

现在,我们将引入第三列,而不是两列 layout,用户可以在其中输入有关鲨鱼的信息。

第一步,将现有列的尺寸更改为 4 以创建三个大小相等的列。 请注意,您需要在当前读取为 <div class="col-lg-6"> 的两行上进行此更改。 这些都将变为 <div class="col-lg-4">

~/node_project/views/sharks.html

...
<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> 

有关 Bootstrap 的网格系统的介绍,包括其行和列布局,请参阅此 Bootstrap 简介

接下来,添加另一列,其中包含 POST 请求的命名端点以及用户的鲨鱼数据和将捕获该数据的 EJS 模板标签。 此列将位于前一列的结束 </p></div> 标记的下方,以及行、容器和 HTML 文档的结束标记的上方。 这些结束标签已经在您的代码中到位; 它们还在下面标有注释。 在添加以下代码以创建新列时将它们保留在原位:

~/node_project/views/sharks.html

...
       </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 -->

form 标签中,您正在为用户的鲨鱼数据添加一个 "/sharks/addshark" 端点并指定 POST 方法来提交它。 在输入字段中,您正在为 "Shark Name""Shark Character" 指定字段,与您之前定义的 Shark 模型对齐。

要将用户输入添加到您的 sharks 集合,您使用 EJS 模板标签(<%=%>)和 JavaScript 语法将用户的条目映射到新创建的文档。 有关 JavaScript 对象的更多信息,请参阅我们关于 Understanding JavaScript Objects 的文章。 有关 EJS 模板标签的更多信息,请参阅 EJS 文档

包含所有三列的整个容器,包括带有鲨鱼输入表单的列,完成后将如下所示:

~/node_project/views/sharks.html

...
<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>

完成编辑后保存并关闭文件。

现在您有了收集用户输入的方法,您可以创建一个端点来显示返回的鲨鱼及其相关的字符信息。

将新修改的 sharks.html 文件复制到名为 getshark.html 的文件中:

cp views/sharks.html views/getshark.html

打开getshark.html

nano views/getshark.html

在文件中,我们将修改用于创建鲨鱼输入表单的列,将其替换为将显示 sharks 集合中的鲨鱼的列。 同样,您的代码将在前一列中现有的 </p></div> 标记与行、容器和 HTML 文档的结束标记之间移动。 请记住在添加以下代码以创建列时保留这些标签:

~/node_project/views/getshark.html

...
       </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 -->

在这里,您使用 EJS 模板标签和 forEach() 方法 输出 sharks 集合中的每个值,包括有关最近添加的鲨鱼的信息。

包含所有三列的整个容器,包括包含 sharks 集合的列,完成后将如下所示:

~/node_project/views/getshark.html

...
<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>

完成编辑后保存并关闭文件。

为了让应用程序使用您创建的模板,您需要在 app.js 文件中添加几行。 再次打开它:

nano app.js

在您添加 express.urlencoded() 函数的上方,添加以下行:

~/node_project/app.js

...
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path));

...

app.engine 方法告诉应用程序将 EJS 模板引擎映射到 HTML 文件,而 app.set 定义默认视图引擎。

您的 app.js 文件现在应该如下所示:

~/node_project/app.js

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!')
})

现在您已经创建了可以动态处理用户数据的视图,是时候创建项目的路由以将视图和控制器逻辑结合在一起了。

第 6 步 - 创建路由

将应用程序的组件组合在一起的最后一步是创建路由。 我们将按功能分隔我们的路线,包括通往我们应用程序登录页面的路线和通往鲨鱼页面的另一条路线。 我们的 sharks 路由将是我们将控制器的逻辑与我们在上一步中创建的视图集成的地方。

首先,创建一个routes目录:

mkdir routes

接下来,在此目录中打开一个名为 index.js 的文件:

nano routes/index.js

此文件将首先导入 expressrouterpath 对象,允许我们使用 router 对象定义要导出的路线,以及使动态处理文件路径成为可能。 在文件顶部添加以下代码:

~/node_project/routes/index.js

const express = require('express');
const router = express.Router();
const path = require('path');

接下来,添加以下 router.use 函数,它加载一个 中间件函数,它将记录路由器的请求并将它们传递给应用程序的路由:

~/node_project/routes/index.js

...

router.use (function (req,res,next) {
  console.log('/' + req.method);
  next();
});

对我们应用程序根目录的请求将首先被定向到这里,然后用户将从这里被定向到我们应用程序的登录页面,我们接下来将定义路由。 在 router.use 函数下方添加以下代码以定义到登录页面的路线:

~/node_project/routes/index.js

...

router.get('/',function(req,res){
  res.sendFile(path.resolve('views/index.html'));
});

当用户访问我们的应用程序时,我们首先要将他们发送到我们的 views 目录中的 index.html 登录页面。

最后,为了使这些路由可以在应用程序的其他地方作为可导入模块访问,在文件末尾添加一个结束表达式以导出 router 对象:

~/node_project/routes/index.js

...

module.exports = router;

完成的文件将如下所示:

~/node_project/routes/index.js

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;

完成编辑后保存并关闭此文件。

接下来,打开一个名为 sharks.js 的文件来定义应用程序应该如何使用我们创建的不同端点和视图来处理用户的鲨鱼输入:

nano routes/sharks.js

在文件顶部,导入 expressrouter 对象:

~/node_project/routes/sharks.js

const express = require('express');
const router = express.Router();

接下来,导入一个名为 shark 的模块,它允许您使用您在控制器中定义的导出函数:

~/node_project/routes/sharks.js

const express = require('express');
const router = express.Router();
const shark = require('../controllers/sharks');

现在您可以使用您在 sharks 控制器文件中定义的 indexcreatelist 函数创建路由。 每条路由都会关联相应的 HTTP 方法:在渲染主鲨鱼信息登陆页面并将鲨鱼列表返回给用户的情况下为 GET,在创建新鲨鱼条目的情况下为 POST:

~/node_project/routes/sharks.js

...

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);
});

每个路由都使用 controllers/sharks.js 中的相关函数,因为我们已经通过在此文件顶部导入该模块来访问该模块。

最后,通过将这些路由附加到 router 对象并导出它们来关闭文件:

~/node_project/routes/index.js

...

module.exports = router;

完成的文件将如下所示:

~/node_project/routes/sharks.js

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;

完成编辑后保存并关闭文件。

使您的应用程序可以访问这些路由的最后一步是将它们添加到 app.js。 再次打开该文件:

nano app.js

在您的 db 常量下方,为您的路线添加以下导入:

~/node_project/app.js

...
const db = require('./db');
const sharks = require('./routes/sharks');

接下来,用以下行替换当前挂载router对象的app.use函数,这将挂载sharks路由器模块:

~/node_project/app.js

...
app.use(express.static(path));
app.use('/sharks', sharks);

app.listen(port, function () {
        console.log("Example app listening on port 8080!")
})

您现在可以删除之前在此文件中定义的路由,因为您正在使用 sharks 路由器模块导入应用程序的路由。

app.js 文件的最终版本将如下所示:

~/node_project/app.js

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!')
})

完成编辑后保存并关闭文件。

您现在可以再次运行 tree 以查看项目的最终结构:

tree -I node_modules

您的项目结构现在将如下所示:

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

创建并安装所有应用程序组件后,您现在可以向数据库中添加测试鲨鱼了!

如果您按照先决条件中的初始服务器设置教程进行操作,则需要修改防火墙,因为它目前只允许 SSH 流量。 要允许到端口 8080 的流量,请运行:

sudo ufw allow 8080

启动应用程序:

node app.js

接下来,将浏览器导航到 http://your_server_ip:8080。 您将看到以下登录页面:

单击获取鲨鱼信息按钮。 您将看到以下信息页面,其中添加了鲨鱼输入表单:

在表格中,添加您选择的鲨鱼。 出于演示的目的,我们将 Megalodon Shark 添加到 Shark Name 字段,并将 Ancient 添加到 Shark Character 字段:

单击提交按钮。 您将看到一个页面,其中向您显示此鲨鱼信息:

您还将在控制台中看到输出,表明鲨鱼已添加到您的收藏中:

OutputExample app listening on port 8080!
{ name: 'Megalodon Shark', character: 'Ancient' }

如果您想创建一个新的鲨鱼条目,请返回 Sharks 页面并重复添加鲨鱼的过程。

您现在有一个工作的鲨鱼信息应用程序,它允许用户添加有关他们最喜欢的鲨鱼的信息。

结论

在本教程中,您通过集成 MongoDB 数据库并使用 MVC 架构模式重写应用程序的逻辑来构建 Node 应用程序。 此应用程序可以作为成熟 CRUD 应用程序的良好起点。

有关其他上下文中 MVC 模式的更多资源,请参阅我们的 Django 开发系列如何构建现代 Web 应用程序以使用 Django 管理客户信息并在 Ubuntu 18.04 上做出反应。

有关使用 MongoDB 的更多信息,请参阅我们的 MongoDB 教程库。