ExpressJS - 快速指南


ExpressJS - 概述

ExpressJS 是一个 Web 应用程序框架,为您提供简单的 API 来构建网站、Web 应用程序和后端。使用 ExpressJS,您无需担心底层协议、流程等。

什么是快递?

Express 提供了一个最小的界面来构建我们的应用程序。它为我们提供了构建应用程序所需的工具。它很灵活,因为npm上有许多可用的模块,可以直接插入 Express 中。

Express 由TJ Holowaychuk开发,由Node.js基金会和众多开源贡献者维护。

为什么选择快递?

与 Rails 和 Django 等采用固执己见的方式构建应用程序的竞争对手不同,Express 没有“最佳方式”来做某事。它非常灵活且可插拔。

哈巴狗

Pug(以前称为 Jade)是一种用于编写 HTML 模板的简洁语言。它 -

  • 生成 HTML
  • 支持动态代码
  • 支持可重用性 (DRY)

它是 Express 使用的最流行的模板语言之一。

MongoDB 和猫鼬

MongoDB 是一个开源文档数据库,旨在简化开发和扩展。该数据库也用于存储数据。

Mongoose 是Node.js的客户端 API ,它使我们可以轻松地从 Express 应用程序访问我们的数据库。

ExpressJS - 环境

在本章中,我们将学习如何开始开发和使用 Express 框架。首先,您应该安装 Node 和 npm(节点包管理器)。如果您还没有这些,请转到节点设置以在本地系统上安装节点。通过在终端中运行以下命令来确认已安装 Node 和 npm。

node --version
npm --version

您应该得到类似于以下内容的输出。

v5.0.0
3.5.2

现在我们已经设置了Node 和npm ,让我们了解npm是什么以及如何使用它。

节点包管理器(npm)

npm 是 Node 的包管理器。npm 注册表是 Node.js、前端 Web 应用程序、移动应用程序、机器人、路由器以及 JavaScript 社区无数其他需求的开源代码包的公共集合。npm 允许我们访问所有这些包并在本地安装它们。您可以在npmJS浏览 npm 上可用的软件包列表。

如何使用npm?

使用 npm 安装包有两种方法:全局安装和本地安装。

  • 全局- 此方法通常用于安装开发工具和基于 CLI 的软件包。要全局安装包,请使用以下代码。

npm install -g <package-name>
  • 本地- 此方法通常用于安装框架和库。本地安装的软件包只能在其安装的目录中使用。要在本地安装软件包,请使用与上面相同的命令,但不带-g标志。

npm install <package-name>

每当我们使用 npm 创建项目时,我们都需要提供一个package.json文件,其中包含有关我们项目的所有详细信息。npm 使我们可以轻松设置此文件。让我们建立我们的开发项目。

步骤 1 - 启动终端/cmd,创建一个名为 hello-world 的新文件夹,然后 cd (创建目录)进入其中 -

npm 初始化信息

步骤 2 - 现在要使用 npm 创建 package.json 文件,请使用以下代码。

npm init

它会要求您提供以下信息。

npm 初始化信息

只需按住 Enter 键,然后在“作者姓名”字段中输入您的姓名即可。

步骤 3 - 现在我们已经设置了 package.json 文件,我们将进一步安装 Express。要安装 Express 并将其添加到我们的 package.json 文件中,请使用以下命令 -

npm install --save express

要确认 Express 已正确安装,请运行以下代码。

ls node_modules #(dir node_modules for windows)

提示- --save标志可以替换为-S标志。该标志确保 Express 作为依赖项添加到我们的package.json文件中。这样做有一个好处,下次我们需要安装项目的所有依赖项时,我们只需运行命令npm install,它就会在该文件中找到依赖项并为我们安装它们。

这就是我们使用 Express 框架开始开发所需的全部内容。为了使我们的开发过程变得更加容易,我们将安装来自 npm 的工具,nodemon。一旦我们对任何文件进行更改,该工具就会重新启动服务器,否则我们需要在每次文件修改后手动重新启动服务器。要安装nodemon,请使用以下命令 -

npm install -g nodemon

您现在可以开始使用 Express。

ExpressJS - 你好世界

我们已经设置了开发,现在是时候开始使用 Express 开发我们的第一个应用程序了。创建一个名为index.js的新文件并在其中输入以下内容。

var express = require('express');
var app = express();

app.get('/', function(req, res){
   res.send("Hello world!");
});

app.listen(3000);

保存文件,转到终端并输入以下内容。

nodemon index.js

这将启动服务器。要测试此应用程序,请打开浏览器并转到http://localhost:3000,将显示一条消息,如以下屏幕截图所示。

你好世界

该应用程序如何工作?

第一行在我们的文件中导入 Express,我们可以通过变量 Express 访问它。我们用它来创建一个应用程序并将其分配给 var app。

app.get(路由、回调)

此函数告诉在给定路由上调用get请求时要执行的操作。回调函数有 2 个参数,request(req)response(res)。请求对象(req)表示 HTTP 请求,并具有请求查询字符串、参数、正文、HTTP 标头等属性。同样,响应对象表示 Express 应用程序收到 HTTP 请求时发送的 HTTP 响应。

res.send()

该函数接受一个对象作为输入,并将其发送到请求客户端。这里我们发送字符串“Hello World!”

app.listen(端口, [主机], [待办事项], [回调]])

该函数绑定并侦听指定主机和端口上的连接。端口是此处唯一必需的参数。

编号 论点和描述
1

港口

服务器应接受传入请求的端口号。

2

主持人

域的名称。当您将应用程序部署到云端时,您需要对其进行设置。

3

积压

排队挂起连接的最大数量。默认值为 511。

4

打回来

当服务器开始侦听请求时调用的异步函数。

ExpressJS - 路由

Web 框架以不同的路径提供 HTML 页面、脚本、图像等资源。

以下函数用于定义 Express 应用程序中的路由 -

app.method(路径,处理程序)

此方法可以应用于任何一个 HTTP 动词 – get、set、put、delete。还存在一种替代方法,该方法的执行与请求类型无关。

Path 是请求运行的路径。

Handler 是一个回调函数,当在相关路由上找到匹配的请求类型时执行。例如,

var express = require('express');
var app = express();

app.get('/hello', function(req, res){
   res.send("Hello World!");
});

app.listen(3000);

如果我们运行应用程序并转到localhost:3000/hello ,服务器会在路由“/hello”处收到 get 请求,我们的 Express 应用程序会执行附加到该路由的回调函数并发送“Hello World!” 作为回应。

你好

我们还可以在同一条路线上有多种不同的方法。例如,

var express = require('express');
var app = express();

app.get('/hello', function(req, res){
   res.send("Hello World!");
});

app.post('/hello', function(req, res){
   res.send("You just called the post method at '/hello'!\n");
});

app.listen(3000);

要测试此请求,请打开终端并使用 cURL 执行以下请求 -

curl -X POST "http://localhost:3000/hello"

卷曲请求

Express 提供了一种特殊方法all ,用于使用相同的函数处理特定路由上的所有类型的 http 方法。要使用此方法,请尝试以下操作。

app.all('/test', function(req, res){
   res.send("HTTP method doesn't have any effect on this route!");
});

该方法通常用于定义中间件,我们将在中间件章节中讨论。

路由器

像上面这样定义路由维护起来非常繁琐。为了将路由与我们的主index.js文件分开,我们将使用Express.Router。创建一个名为things.js的新文件并在其中键入以下内容。

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

router.get('/', function(req, res){
   res.send('GET route on things.');
});
router.post('/', function(req, res){
   res.send('POST route on things.');
});

//export this router to use in our index.js
module.exports = router;

现在要在我们的index.js中使用此路由器,请在app.listen函数调用之前输入以下内容。

var express = require('Express');
var app = express();

var things = require('./things.js');

//both index.js and things.js should be in same directory
app.use('/things', things);

app.listen(3000);

路由“/things”上的 app.use函数调用将Things路由器附加到此路由。现在,我们的应用程序在“/things”处收到的任何请求都将由我们的 things.js 路由器处理。things.js 中的“/”路由实际上是“/things”的子路由。访问 localhost:3000/things/ 您将看到以下输出。

路由器的东西

路由器对于分离关注点并将代码的相关部分保持在一起非常有帮助。它们有助于构建可维护的代码。您应该在单个文件中定义与实体相关的路由,并使用上述方法将其包含在您的index.js文件中。

ExpressJS - HTTP 方法

HTTP 方法在请求中提供,并指定客户端请求的操作。下表列出了最常用的 HTTP 方法 -

编号 方法及说明
1

得到

GET 方法请求指定资源的表示。使用 GET 的请求应该只检索数据并且不应该有其他效果。

2

邮政

POST 方法请求服务器接受请求中包含的数据作为 URI 标识的资源的新对象/实体。

3

PUT 方法请求服务器接受请求中包含的数据,作为对 URI 标识的现有对象的修改。如果它不存在,则 PUT 方法应该创建一个。

4

删除

DELETE方法请求服务器删除指定的资源。

这些是最常见的 HTTP 方法。要了解有关这些方法的更多信息,请访问http://www.tutorialspoint.com/http/http_methods.htm

ExpressJS - URL 构建

我们现在可以定义路线,但这些路线是静态的或固定的。要使用动态路由,我们应该提供不同类型的路由。使用动态路由允许我们传递参数并基于它们进行处理。

这是动态路线的示例 -

var express = require('express');
var app = express();

app.get('/:id', function(req, res){
   res.send('The id you specified is ' + req.params.id);
});
app.listen(3000);

要测试此功能,请访问http://localhost:3000/123。将显示以下响应。

网址1号楼

您可以将 URL 中的“123”替换为其他内容,更改将反映在响应中。上面的一个更复杂的例子是 -

var express = require('express');
var app = express();

app.get('/things/:name/:id', function(req, res) {
   res.send('id: ' + req.params.id + ' and name: ' + req.params.name);
});
app.listen(3000);

要测试上述代码,请转到http://localhost:3000/things/tutorialspoint/12345

网址2号楼

您可以使用req.params对象来访问您在 url 中传递的所有参数。注意上面2个是不同的路径。它们永远不会重叠。另外,如果您想在获得“/things”时执行代码,那么您需要单独定义它。

模式匹配的路线

您还可以使用正则表达式来限制 URL 参数匹配。假设您需要id为 5 位长数字。您可以使用以下路由定义 -

var express = require('express');
var app = express();

app.get('/things/:id([0-9]{5})', function(req, res){
   res.send('id: ' + req.params.id);
});

app.listen(3000);

请注意,这只会匹配具有 5 位长id的请求。您可以使用更复杂的正则表达式来匹配/验证您的路线。如果您的路由均不匹配请求,您将收到“Cannot GET <your-request-route>”消息作为响应。使用这个简单的路线,此消息将被 404 未找到页面替换 -

var express = require('express');
var app = express();

//Other routes here
app.get('*', function(req, res){
   res.send('Sorry, this is an invalid URL.');
});
app.listen(3000);

重要提示- 这应该放在所有路由之后,因为 Express 会匹配index.js文件从头到尾的路由,包括您需要的外部路由器。

例如,如果我们定义与上面相同的路由,则在使用有效 URL 进行请求时,将显示以下输出。-

正确的正则表达式

对于不正确的 URL 请求,会显示以下输出。

无效的正则表达式(404)

ExpressJS - 中间件

中间件函数是可以访问请求对象 (req)响应对象 (res)以及应用程序请求-响应周期中的下一个中间件函数的函数。这些函数用于修改reqres对象,以执行解析请求正文、添加响应标头等任务。

这是中间件功能的一个简单示例 -

var express = require('express');
var app = express();

//Simple request time logger
app.use(function(req, res, next){
   console.log("A new request received at " + Date.now());
   
   //This function call is very important. It tells that more processing is
   //required for the current request and is in the next middleware
   function route handler.
   next();
});

app.listen(3000);

服务器上的每个请求都会调用上述中间件。因此,每次请求后,我们都会在控制台中收到以下消息 -

A new request received at 1467267512545

要将其限制为特定路由(及其所有子路由),请提供该路由作为app.use()的第一个参数。例如,

var express = require('express');
var app = express();

//Middleware function to log request protocol
app.use('/things', function(req, res, next){
   console.log("A request for things received at " + Date.now());
   next();
});

// Route handler that sends the response
app.get('/things', function(req, res){
   res.send('Things');
});

app.listen(3000);

现在,每当您请求“/things”的任何子路由时,它才会记录时间。

中间件调用顺序

Express 中中间件最重要的事情之一是它们在文件中写入/包含的顺序;考虑到还需要考虑路由匹配,它们的执行顺序。

例如,在下面的代码片段中,第一个函数首先执行,然后是路由处理程序,最后是结束函数。这个例子总结了如何在路由处理程序之前和之后使用中间件;以及如何将路由处理程序本身用作中间件。

var express = require('express');
var app = express();

//First middleware before response is sent
app.use(function(req, res, next){
   console.log("Start");
   next();
});

//Route handler
app.get('/', function(req, res, next){
   res.send("Middle");
   next();
});

app.use('/', function(req, res){
   console.log('End');
});

app.listen(3000);

当我们运行此代码后访问“/”时,我们会在控制台上收到中间的响应-

Start
End

下图总结了我们对中间件的了解 -

中间件

现在我们已经介绍了如何创建自己的中间件,让我们讨论一些最常用的社区创建的中间件。

第三方中间件

此处提供了 Express 第三方中间件的列表。以下是一些最常用的中间件;我们还将学习如何使用/安装这些 -

正文解析器

这用于解析附加有有效负载的请求正文。要安装主体解析器,我们需要使用npm install --save body-parser 来安装它并安装它,请在您的 index.js 中包含以下行 -

var bodyParser = require('body-parser');

//To parse URL encoded data
app.use(bodyParser.urlencoded({ extended: false }))

//To parse json data
app.use(bodyParser.json())

要查看 body-parser 的所有可用选项,请访问其 github 页面。

cookie解析器

它解析Cookie标头并使用以 cookie 名称为键的对象填充 req.cookies。要安装 cookie 解析器,我们需要使用 npm install --save cookie-parser 安装它并安装它,请在您的 index.js 中包含以下行 -

var cookieParser = require('cookie-parser');
app.use(cookieParser())

快速会话

它使用给定的选项创建一个会话中间件。我们将在会话部分讨论它的用法。

我们在 ExpressJS 中还有许多其他第三方中间件。然而,我们在这里只讨论了一些重要的问题。

ExpressJS - 模板

Pug 是 Express 的模板引擎。模板引擎用于消除服务器代码与 HTML 的混乱,将字符串与现有的 HTML 模板连接起来。Pug 是一个非常强大的模板引擎,它具有多种功能,包括过滤器、包含、继承、插值等。这方面有很多基础知识需要涵盖。

要将 Pug 与 Express 一起使用,我们需要安装它,

npm install --save pug

现在 Pug 已安装,请将其设置为应用程序的模板引擎。您不需要“要求”它。将以下代码添加到您的index.js文件中。

app.set('view engine', 'pug');
app.set('views','./views');

现在创建一个名为views的新目录。在其中创建一个名为first_view.pug的文件,并在其中输入以下数据。

doctype html
html
   head
      title = "Hello Pug"
   body
      p.greetings#people Hello World!

要运行此页面,请将以下路由添加到您的应用程序 -

app.get('/first_template', function(req, res){
   res.render('first_view');
});

您将得到如下输出:Hello World! Pug 将这个看起来非常简单的标记转换为 html。我们不需要跟踪关闭标签,不需要使用 class 和 id 关键字,而是使用 '.' 和“#”来定义它们。上面的代码首先被转换为 -

<!DOCTYPE html>
<html>
   <head>
      <title>Hello Pug</title>
   </head>
   
   <body>
      <p class = "greetings" id = "people">Hello World!</p>
   </body>
</html>

Pug 的能力远不止简化 HTML 标记。

巴哥犬的重要特征

现在让我们探讨 Pug 的一些重要特征。

简单标签

标签根据其缩进进行嵌套。就像上面的示例一样,<title>在<head>标记内缩进,因此它位于其中。但<body>标记位于相同的缩进位置,因此它是<head>标记的同级标记。

我们不需要关闭标签,一旦 Pug 遇到相同或外部缩进级别的下一个标签,它就会为我们关闭标签。

要将文本放入标签内,我们有 3 种方法 -

  • 空间分隔

h1 Welcome to Pug
  • 管道文本

div
   | To insert multiline text, 
   | You can use the pipe operator.
  • 文本块

div.
   But that gets tedious if you have a lot of text.
   You can use "." at the end of tag to denote block of text.
   To put tags inside this block, simply enter tag in a new line and 
   indent it accordingly.

评论

Pug 使用与JavaScript(//)相同的语法来创建注释。这些注释被转换为 html 注释(<!--comment-->)。例如,

//This is a Pug comment

此评论将转换为以下内容。

<!--This is a Pug comment-->

属性

为了定义属性,我们在括号中使用逗号分隔的属性列表。类和 ID 属性具有特殊的表示形式。下面的代码行涵盖了给定 html 标签的属性、类和 id 的定义。

div.container.column.main#division(width = "100", height = "100")

这行代码被转换为以下内容。-

<div class = "container column main" id = "division" width = "100" height = "100"></div>

将值传递给模板

当我们渲染 Pug 模板时,我们实际上可以从路由处理程序向其传递一个值,然后我们可以在模板中使用该值。使用以下内容创建一个新的路由处理程序。

var express = require('express');
var app = express();

app.get('/dynamic_view', function(req, res){
   res.render('dynamic', {
      name: "TutorialsPoint", 
      url:"http://www.tutorialspoint.com"
   });
});

app.listen(3000);

并在views目录中创建一个新的视图文件,名为dynamic.pug,代码如下:

html
   head
      title=name
   body
      h1=name
      a(href = url) URL

在浏览器中打开 localhost:3000/dynamic_view;您应该得到以下输出 -

模板中的变量

我们还可以在文本中使用这些传递的变量。要在标签文本之间插入传递的变量,我们使用#{variableName}语法。例如,在上面的示例中,如果我们想从TutorialsPoint 中添加问候语,那么我们可以执行以下操作。

html
   head
      title = name
   body
      h1 Greetings from #{name}
      a(href = url) URL

这种使用值的方法称为插值。上面的代码将显示以下输出。-

模板间

条件句

我们也可以使用条件语句和循环结构。

考虑以下几点 -

如果用户已登录,页面应显示“您好,用户”,如果未登录,则显示“登录/注册”链接。为了实现这一点,我们可以定义一个简单的模板,例如 -

html
   head
      title Simple template
   body
      if(user)
         h1 Hi, #{user.name}
      else
         a(href = "/sign_up") Sign Up

当我们使用路由渲染它时,我们可以传递一个对象,如以下程序所示 -

res.render('/dynamic',{
   user: {name: "Ayush", age: "20"}
});

您将收到一条消息 -嗨,Ayush。但是,如果我们不传递任何对象或传递没有用户密钥的对象,那么我们将获得一个注册链接。

包含和组件

Pug 提供了一种非常直观的方式来创建网页组件。例如,如果您看到一个新闻网站,带有徽标和类别的标题始终是固定的。我们可以使用包含功能,而不是将其复制到我们创建的每个视图中。以下示例展示了我们如何使用此功能 -

使用以下代码创建 3 个视图 -

标题.PUG

div.header.
   I'm the header for this website.

内容.PUG

html
   head
      title Simple template
   body
      include ./header.pug
      h3 I'm the main content
      include ./footer.pug

页脚.PUG

div.footer.
   I'm the footer for this website.

为此创建一条路线,如下所示 -

var express = require('express');
var app = express();

app.get('/components', function(req, res){
    res.render('content');
});

app.listen(3000);

转到 localhost:3000/components,您将收到以下输出 -

模板组件

include还可用于包含纯文本、css 和 JavaScript。

帕格还有更多的功能。但这些超出了本教程的范围。您可以在Pug进一步探索 Pug 。

ExpressJS - 提供静态文件

静态文件是客户端从服务器下载的文件。创建一个新目录public。Express 默认情况下不允许您提供静态文件。您需要使用以下内置中间件来启用它。

app.use(express.static('public'));

注意- Express 查找相对于静态目录的文件,因此静态目录的名称不是 URL 的一部分。

请注意,根路由现在设置为您的公共目录,因此您加载的所有静态文件都将被视为公共根目录。要测试它是否正常工作,请在新的公共目录中添加任何图像文件并将其名称更改为“ testimage.jpg ”。在您的视图中,创建一个新视图并包含此文件,例如 -

html
   head
   body
      h3 Testing static file serving:
      img(src = "/testimage.jpg", alt = "Testing Image

您应该得到以下输出 -

静态文件示例

多个静态目录

我们还可以使用以下程序设置多个静态资产目录 -

var express = require('express');
var app = express();

app.use(express.static('public'));
app.use(express.static('images'));

app.listen(3000);

虚拟路径前缀

我们还可以提供用于服务静态文件的路径前缀。例如,如果您想提供像'/static'这样的路径前缀,您需要在您的index.js文件中包含以下代码-

var express = require('express');
var app = express();

app.use('/static', express.static('public'));

app.listen(3000);

现在,每当您需要包含一个文件(例如,位于公共目录中的名为 main.js 的脚本文件)时,请使用以下脚本标记 -

<script src = "/static/main.js" />

当提供多个目录作为静态文件时,此技术可以派上用场。这些前缀可以帮助区分多个目录。

ExpressJS - 表单数据

表单是网络不可或缺的一部分。几乎我们访问的每个网站都提供为我们提交或获取一些信息的表格。要开始使用表单,我们将首先安装body-parser(用于解析 JSON 和 url 编码数据)和 multer(用于解析多部分/表单数据)中间件。

要安装body-parsermulter,请转到您的终端并使用 -

npm install --save body-parser multer

使用以下代码替换您的index.js文件内容 -

var express = require('express');
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer();
var app = express();

app.get('/', function(req, res){
   res.render('form');
});

app.set('view engine', 'pug');
app.set('views', './views');

// for parsing application/json
app.use(bodyParser.json()); 

// for parsing application/xwww-
app.use(bodyParser.urlencoded({ extended: true })); 
//form-urlencoded

// for parsing multipart/form-data
app.use(upload.array()); 
app.use(express.static('public'));

app.post('/', function(req, res){
   console.log(req.body);
   res.send("recieved your request!");
});
app.listen(3000);

导入 body 解析器和 multer 后,我们将使用body-parser解析 json 和 x-www-form-urlencoded 标头请求,同时使用multer解析 multipart/form-data。

让我们创建一个 html 表单来测试一下。使用以下代码创建一个名为form.pug的新视图-

html
html
   head
      title Form Tester
   body
      form(action = "/", method = "POST")
         div
            label(for = "say") Say:
            input(name = "say" value = "Hi")
         br
         div
            label(for = "to") To:
            input(name = "to" value = "Express forms")
         br
         button(type = "submit") Send my greetings

使用以下命令运行您的服务器。

nodemon index.js

现在转到 localhost:3000/ 并根据需要填写表格,然后提交。将显示以下响应 -

回复表单提交

看一下你的控制台;它将向您显示请求的正文作为 JavaScript 对象,如以下屏幕截图所示 -

表单的控制台输出

req.body对象包含已解析的请求正文。要使用该对象中的字段,只需像普通 JS 对象一样使用它们即可。

这是最推荐的发送请求的方式。还有很多其他方法,但这些与此处无关,因为我们的 Express 应用程序将以相同的方式处理所有这些请求。要了解有关提出请求的不同方式的更多信息,请查看页面。

ExpressJS - 数据库

我们不断收到请求,但最终没有将它们存储在任何地方。我们需要一个数据库来存储数据。为此,我们将使用名为MongoDB的 NoSQL 数据库。

要安装并阅读有关 Mongo 的信息,请点击此链接。

为了将 Mongo 与 Express 结合使用,我们需要一个用于节点的客户端 API。我们有多种选择,但在本教程中,我们将坚持使用mongoose。Mongoose 用于Node for MongoDB 中的文档建模。对于文档建模,我们创建一个模型(很像面向文档编程中的),然后使用该模型生成文档(就像我们在 OOP 中创建类的文档一样)。我们所有的处理都将在这些“文档”上完成,最后,我们将这些文档写入我们的数据库中。

设置猫鼬

现在您已经安装了 Mongo,让我们安装 Mongoose,就像我们安装其他节点包一样 -

npm install --save mongoose

在开始使用 mongoose 之前,我们必须使用 Mongo shell 创建一个数据库。要创建新数据库,请打开终端并输入“mongo”。Mongo shell 将启动,输入以下代码 -

use my_db

将为您创建一个新数据库。每当您打开 mongo shell 时,它都会默认为“test”数据库,您必须使用与上面相同的命令更改为数据库。

要使用 Mongoose,我们将在index.js文件中需要它,然后连接到在mongodb://localhost上运行的mongodb服务。

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

现在我们的应用程序已连接到我们的数据库,让我们创建一个新模型。该模型将充当我们数据库中的集合。要创建新模型,请在定义任何路线之前使用以下代码 -

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});
var Person = mongoose.model("Person", personSchema);

上面的代码定义了一个人的模式,并用于创建一个 Mongoose 模式Person

保存文档

现在,我们将创建一个新的 html 表单;此表格将帮助您获取某人的详细信息并将其保存到我们的数据库中。要创建表单,请在视图目录中创建一个名为person.pug的新视图文件,其中包含以下内容 -

html
head
   title Person
   body
      form(action = "/person", method = "POST")
      div
         label(for = "name") Name: 
         input(name = "name")
      br
      div
         label(for = "age") Age: 
         input(name = "age")
      br
      div
         label(for = "nationality") Nationality: 
         input(name = "nationality")
      br
      button(type = "submit") Create new person

还在index.js中添加一个新的获取路由来呈现此文档 -

app.get('/person', function(req, res){
   res.render('person');
});

转到“ localhost:3000/person ”检查表单是否显示正确的输出。请注意,这只是 UI,尚未运行。以下屏幕截图显示了表单的显示方式 -

猫鼬创建

我们现在将在“/person”定义一个后路由处理程序来处理此请求

app.post('/person', function(req, res){
   var personInfo = req.body; //Get the parsed information
   
   if(!personInfo.name || !personInfo.age || !personInfo.nationality){
      res.render('show_message', {
         message: "Sorry, you provided worng info", type: "error"});
   } else {
      var newPerson = new Person({
         name: personInfo.name,
         age: personInfo.age,
         nationality: personInfo.nationality
      });
		
      newPerson.save(function(err, Person){
         if(err)
            res.render('show_message', {message: "Database error", type: "error"});
         else
            res.render('show_message', {
               message: "New person added", type: "success", person: personInfo});
      });
   }
});

在上面的代码中,如果我们收到任何空字段或没有收到任何字段,我们将发送错误响应。但是,如果我们收到格式良好的文档,那么我们从 Person 模型创建一个newPerson文档,并使用newPerson.save()函数将其保存到我们的数据库中。这是在 Mongoose 中定义的,并接受回调作为参数。该回调有 2 个参数——错误和响应。这些参数将呈现show_message视图。

为了显示该路由的响应,我们还需要创建一个show_message视图。使用以下代码创建一个新视图 -

html
   head
      title Person
   body
      if(type == "error")
         h3(style = "color:red") #{message}
      else
         h3 New person, 
            name: #{person.name}, 
            age: #{person.age} and 
            nationality: #{person.nationality} added!

成功提交表单后,我们将收到以下回复(show_message.pug) -

猫鼬反应

我们现在有一个创建人员的界面。

检索文件

Mongoose 提供了很多用于检索文档的函数,我们将重点介绍其中的 3 个。所有这些函数也都将回调作为最后一个参数,就像 save 函数一样,它们的参数是错误和响应。这三个功能如下 -

Model.find(条件,回调)

该函数查找与条件对象中的字段匹配的所有文档。Mongo 中使用的相同运算符也适用于 mongoose。例如,

Person.find(function(err, response){
   console.log(response);
});

这将从该人的集合中获取所有文档。

Person.find({name: "Ayush", age: 20}, 
   function(err, response){
      console.log(response);
});

这将获取字段名称为“Ayush”且年龄为 20 的所有文档。

我们还可以提供我们需要的投影,即我们需要的字段。例如,如果我们只想要国籍“印度”的人的名字,我们使用 -

Person.find({nationality: "Indian"}, "name", function(err, response){
   console.log(response);
});

Model.findOne(条件,回调)

此函数始终获取单个最相关的文档。它具有与Model.find()完全相同的参数。

Model.findById(id, 回调)

该函数接受_id(由 mongo 定义)作为第一个参数,一个可选的投影字符串和一个处理响应的回调。例如,

Person.findById("507f1f77bcf86cd799439011", function(err, response){
   console.log(response);
});

现在让我们创建一条查看所有人员记录的路线 -

var express = require('express');
var app = express();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

app.get('/people', function(req, res){
   Person.find(function(err, response){
      res.json(response);
   });
});

app.listen(3000);

更新文件

Mongoose 提供了 3 个更新文档的函数。这些功能描述如下 -

Model.update(条件、更新、回调)

此函数接受条件并更新对象作为输入,并将更改应用于与集合中的条件匹配的所有文档。例如,以下代码将更新所有个人文档中的国籍“美国” -

Person.update({age: 25}, {nationality: "American"}, function(err, response){
   console.log(response);
});

Model.findOneAndUpdate(条件、更新、回调)

它根据查询找到一份文档,并根据第二个参数更新该文档。它还采用回调作为最后一个参数。让我们执行以下示例来理解该功能

Person.findOneAndUpdate({name: "Ayush"}, {age: 40}, function(err, response) {
   console.log(response);
});

Model.findByIdAndUpdate(id, 更新, 回调)

此函数更新由其 id 标识的单个文档。例如,

Person.findByIdAndUpdate("507f1f77bcf86cd799439011", {name: "James"}, 
   function(err, response){
      console.log(response);
});

现在让我们创建一条更新人员的路线。这将是一条PUT路由,其中​​ id 作为参数,详细信息位于有效负载中。

var express = require('express');
var app = express();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

app.put('/people/:id', function(req, res){
   Person.findByIdAndUpdate(req.params.id, req.body, function(err, response){
      if(err) res.json({message: "Error in updating person with id " + req.params.id});
      res.json(response);
   });
});

app.listen(3000);

要测试此路线,请在终端中输入以下内容(将 id 替换为您创建的人员中的 id ) -

curl -X PUT --data "name = James&age = 20&nationality = American
"http://localhost:3000/people/507f1f77bcf86cd799439011

这将使用上述详细信息更新与路线中提供的 ID 关联的文档。

删除文档

我们已经介绍了创建、读取更新,现在我们将了解如何使用 Mongoose删除文档。我们这里有 3 个函数,就像 update 一样。

Model.remove(条件, [回调])

该函数将条件对象作为输入,并删除所有符合条件的文档。例如,如果我们需要删除所有 20 岁的人,请使用以下语法 -

Person.remove({age:20});

Model.findOneAndRemove(条件, [回调])

此函数根据条件对象删除单个最相关的文档。让我们执行以下代码来理解这一点。

Person.findOneAndRemove({name: "Ayush"});

Model.findByIdAndRemove(id, [回调])

此函数删除由其 id 标识的单个文档。例如,

Person.findByIdAndRemove("507f1f77bcf86cd799439011");

现在让我们创建一条从数据库中删除人员的路线。

var express = require('express');
var app = express();

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

app.delete('/people/:id', function(req, res){
   Person.findByIdAndRemove(req.params.id, function(err, response){
      if(err) res.json({message: "Error in deleting record id " + req.params.id});
      else res.json({message: "Person with id " + req.params.id + " removed."});
   });
});

app.listen(3000);

要检查输出,请使用以下curl命令 -

curl -X DELETE http://localhost:3000/people/507f1f77bcf86cd799439011

这将删除具有给定 ID 的人,并产生以下消息 -

{message: "Person with id 507f1f77bcf86cd799439011 removed."}

这总结了我们如何使用 MongoDB、Mongoose 和 Express 创建简单的 CRUD 应用程序。要进一步探索 Mongoose,请阅读API 文档。

ExpressJS - Cookie

Cookie 是简单的小文件/数据,通过服务器请求发送到客户端并存储在客户端。每次用户重新加载网站时,此 cookie 都会随请求一起发送。这有助于我们跟踪用户的操作。

以下是 HTTP Cookie 的多种用途 -

  • 会话管理
  • 个性化(推荐系统)
  • 用户追踪

要在 Express 中使用 cookie,我们需要 cookie 解析器中间件。要安装它,请使用以下代码 -

npm install --save cookie-parser

现在要在 Express 中使用 cookie,我们需要cookie-parser。cookie-parser 是一个中间件,它解析附加到客户端请求对象的cookie。要使用它,我们需要在我们的index.js文件中使用它;这可以像我们使用其他中间件一样使用。在这里,我们将使用以下代码。

var cookieParser = require('cookie-parser');
app.use(cookieParser());

cookie-parser 解析 Cookie 标头并使用以 cookie 名称为键的对象填充req.cookies 。要设置新的 cookie,让我们在您的 Express 应用程序中定义一条新路线,例如 -

var express = require('express');
var app = express();

app.get('/', function(req, res){
   res.cookie('name', 'express').send('cookie set'); //Sets name = express
});

app.listen(3000);

要检查您的 cookie 是否已设置,只需转到浏览器,启动控制台,然后输入 -

console.log(document.cookie);

您将得到类似的输出(您可能由于浏览器中的扩展而设置了更多 cookie) -

"name = express"

浏览器每次查询服务器时也会发回cookie。要查看服务器中的 cookie,请在服务器控制台上的路由中,将以下代码添加到该路由。

console.log('Cookies: ', req.cookies);

下次您向此路由发送请求时,您将收到以下输出。

Cookies: { name: 'express' }

添加带有过期时间的 Cookie

您可以添加过期的 cookie。要添加过期的 cookie,只需传递一个将属性“expire”设置为您希望其过期的时间的对象。例如,

//Expires after 360000 ms from the time it is set.
res.cookie(name, 'value', {expire: 360000 + Date.now()}); 

设置过期时间的另一种方法是使用“maxAge”属性。利用这个属性,我们可以提供相对时间而不是绝对时间。以下是此方法的示例。

//This cookie also expires after 360000 ms from the time it is set.
res.cookie(name, 'value', {maxAge: 360000});

删除现有的 Cookie

要删除 cookie,请使用clearCookie 函数。例如,如果您需要清除名为foo的 cookie ,请使用以下代码。

var express = require('express');
var app = express();

app.get('/clear_cookie_foo', function(req, res){
   res.clearCookie('foo');
   res.send('cookie foo cleared');
});

app.listen(3000);

在下一章中,我们将看到如何使用 cookie 来管理会话。

ExpressJS - 会话

HTTP 是无状态的;为了将一个请求与任何其他请求相关联,您需要一种在 HTTP 请求之间存储用户数据的方法。Cookie 和 URL 参数都是在客户端和服务器之间传输数据的合适方法。但它们都是可读的并且位于客户端。Session 正好解决了这个问题。您为客户端分配一个 ID,它会使用该 ID 发出所有进一步的请求。与客户端关联的信息存储在与该 ID 链接的服务器上。

我们需要Express-session,因此使用以下代码安装它。

npm install --save express-session

我们将放置会话cookie 解析器中间件。在本例中,我们将使用默认存储来存储会话,即MemoryStore。切勿在生产环境中使用它。会话中间件为我们处理所有事情,即创建会话、设置会话cookie以及在req对象中创建会话对象。

每当我们再次从同一个客户端发出请求时,我们都会存储他们的会话信息(假设服务器没有重新启动)。我们可以向会话对象添加更多属性。在下面的示例中,我们将为客户端创建一个查看计数器。

var express = require('express');
var cookieParser = require('cookie-parser');
var session = require('express-session');

var app = express();

app.use(cookieParser());
app.use(session({secret: "Shh, its a secret!"}));

app.get('/', function(req, res){
   if(req.session.page_views){
      req.session.page_views++;
      res.send("You visited this page " + req.session.page_views + " times");
   } else {
      req.session.page_views = 1;
      res.send("Welcome to this page for the first time!");
   }
});
app.listen(3000);

上面代码的作用是,当用户访问该站点时,它会为用户创建一个新会话并为其分配一个 cookie。下次用户到来时,将检查 cookie,并相应更新page_view会话变量。

现在,如果您运行该应用程序并转到localhost:3000,将显示以下输出。

第一次访问

如果您重新访问该页面,页面计数器将会增加。下面截图中的页面刷新了42次。

第一次访问

ExpressJS - 身份验证

身份验证是一个过程,其中将所提供的凭据与本地操作系统或身份验证服务器内的授权用户信息数据库中记录的凭据进行比较。如果凭据匹配,则该过程完成,并且用户被授予访问授权。

为了创建身份验证系统,我们需要创建一个注册页面和一个用户密码存储。以下代码为我们创建一个帐户并将其存储在内存中。这仅用于演示目的;建议始终使用持久性存储(数据库或文件)来存储用户信息。

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer(); 
var session = require('express-session');
var cookieParser = require('cookie-parser');

app.set('view engine', 'pug');
app.set('views','./views');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true })); 
app.use(upload.array());
app.use(cookieParser());
app.use(session({secret: "Your secret key"}));

var Users = [];

app.get('/signup', function(req, res){
   res.render('signup');
});

app.post('/signup', function(req, res){
   if(!req.body.id || !req.body.password){
      res.status("400");
      res.send("Invalid details!");
   } else {
      Users.filter(function(user){
         if(user.id === req.body.id){
            res.render('signup', {
               message: "User Already Exists! Login or choose another user id"});
         }
      });
      var newUser = {id: req.body.id, password: req.body.password};
      Users.push(newUser);
      req.session.user = newUser;
      res.redirect('/protected_page');
   }
});

app.listen(3000);

现在对于注册表单,创建一个名为signup.jade 的新视图。

注册.JADE

html
   head
      title Signup
   body
      if(message)
         h4 #{message}
         form(action = "/signup" method = "POST")
         input(name = "id" type = "text" required placeholder = "User ID")
         input(name = "password" type = "password" required placeholder = "Password")
         button(type = "Submit") Sign me up!

通过访问 localhost:3000/signup 检查此页面是否加载。

注册表单

我们已经为这两个字段设置了 required 属性,因此在我们提供 ID 和密码之前,支持 HTML5 的浏览器不会让我们提交此表单。如果有人尝试在没有用户 ID 或密码的情况下使用curl 请求进行注册,则会显示错误。在视图中创建一个名为protected_pa​​ge.pug的新文件,其中包含以下内容 -

html
   head
      title Protected page
   body
      div Hey #{id}, How are you doing today?
      div Want to log out?
      div Logout

仅当用户刚刚注册或登录时,此页面才应可见。现在让我们定义其路由以及登录和注销的路由 -

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer(); 
var session = require('express-session');
var cookieParser = require('cookie-parser');

app.set('view engine', 'pug');
app.set('views','./views');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true })); 
app.use(upload.array());
app.use(cookieParser());
app.use(session({secret: "Your secret key"}));

var Users = [];

app.get('/signup', function(req, res){
   res.render('signup');
});

app.post('/signup', function(req, res){
   if(!req.body.id || !req.body.password){
      res.status("400");
      res.send("Invalid details!");
   } else {
      Users.filter(function(user){
         if(user.id === req.body.id){
            res.render('signup', {
               message: "User Already Exists! Login or choose another user id"});
         }
      });
      var newUser = {id: req.body.id, password: req.body.password};
      Users.push(newUser);
      req.session.user = newUser;
      res.redirect('/protected_page');
   }
});
function checkSignIn(req, res){
   if(req.session.user){
      next();     //If session exists, proceed to page
   } else {
      var err = new Error("Not logged in!");
      console.log(req.session.user);
      next(err);  //Error, trying to access unauthorized page!
   }
}
app.get('/protected_page', checkSignIn, function(req, res){
   res.render('protected_page', {id: req.session.user.id})
});

app.get('/login', function(req, res){
   res.render('login');
});

app.post('/login', function(req, res){
   console.log(Users);
   if(!req.body.id || !req.body.password){
      res.render('login', {message: "Please enter both id and password"});
   } else {
      Users.filter(function(user){
         if(user.id === req.body.id && user.password === req.body.password){
            req.session.user = user;
            res.redirect('/protected_page');
         }
      });
      res.render('login', {message: "Invalid credentials!"});
   }
});

app.get('/logout', function(req, res){
   req.session.destroy(function(){
      console.log("user logged out.")
   });
   res.redirect('/login');
});

app.use('/protected_page', function(err, req, res, next){
console.log(err);
   //User should be authenticated! Redirect him to log in.
   res.redirect('/login');
});

app.listen(3000);

我们创建了一个中间件函数checkSignIn来检查用户是否登录。protected_pa​​ge使用此函数。要注销用户,我们销毁会话。

现在让我们创建登录页面。将视图命名为login.pug并输入内容 -

html
   head
      title Signup
   body
      if(message)
         h4 #{message}
         form(action = "/login" method = "POST")
         input(name = "id" type = "text" required placeholder = "User ID")
         input(name = "password" type = "password" required placeholder = "Password")
         button(type = "Submit") Log in

我们的简单身份验证应用程序现已完成;现在让我们测试该应用程序。使用nodemon index.js运行应用程序,然后继续访问 localhost:3000/signup。

输入用户名和密码,然后单击“注册”。如果详细信息有效/唯一,您将被重定向到protected_pa​​ge -

受保护的页面

现在退出应用程序。这会将我们重定向到登录页面 -

授权登录

该路由受到保护,如果未经身份验证的人尝试访问它,他将被重定向到我们的登录页面。这都是关于基本的用户身份验证。始终建议我们使用持久会话系统并使用哈希值进行密码传输。现在有更好的方法来利用 JSON 令牌对用户进行身份验证。

ExpressJS - RESTful API

创建移动应用程序、单页应用程序、使用 AJAX 调用以及向客户端提供数据始终需要 API。关于如何构建和命名这些 API 和端点的流行架构风格称为REST(表述性传输状态)HTTP 1.1的设计牢记 REST 原则。REST 是由Roy Fielding于 2000 年在他的 Paper Fielding 论文中引入的。

RESTful URI 和方法为我们提供了处理请求所需的几乎所有信息。下表总结了各种动词的使用方式以及 URI 的命名方式。我们将在最后创建一个电影 API;现在让我们讨论如何构建它。

方法 统一资源标识符 细节 功能
得到 /电影 安全、可缓存 获取所有电影及其详细信息的列表
得到 /电影/1234 安全、可缓存 获取电影 id 1234 的详细信息
邮政 /电影 不适用 使用提供的详细信息创建一部新电影。响应包含此新创建的资源的 URI。
/电影/1234 幂等 修改电影 ID 1234(如果尚不存在则创建一个)。响应包含此新创建的资源的 URI。
删除 /电影/1234 幂等 电影 ID 1234(如果存在)应删除。响应应包含请求的状态。
删除或放置 /电影 无效的 应无效。DELETEPUT应指定它们正在处理哪个资源。

现在让我们在 Express 中创建这个 API。我们将使用 JSON 作为传输数据格式,因为它很容易在 JavaScript 中使用,并且具有其他优点。将您的index.js文件替换为movies.js文件,如以下程序所示。

索引.js

var express = require('express');
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer();

var app = express();

app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(upload.array());

//Require the Router we defined in movies.js
var movies = require('./movies.js');

//Use the Router on the sub route /movies
app.use('/movies', movies);

app.listen(3000);

现在我们已经设置了应用程序,让我们集中精力创建 API。

首先设置 movie.js 文件。我们没有使用数据库来存储电影,而是将它们存储在内存中;所以每次服务器重启时,我们添加的电影就会消失。这可以很容易地使用数据库或文件(使用节点 fs 模块)来模仿。

导入 Express 后,创建一个 Router 并使用module.exports导出它-

var express = require('express');
var router = express.Router();
var movies = [
   {id: 101, name: "Fight Club", year: 1999, rating: 8.1},
   {id: 102, name: "Inception", year: 2010, rating: 8.7},
   {id: 103, name: "The Dark Knight", year: 2008, rating: 9},
   {id: 104, name: "12 Angry Men", year: 1957, rating: 8.9}
];

//Routes will go here
module.exports = router;

获取路线

让我们定义获取所有电影的 GET 路由 -

router.get('/', function(req, res){
   res.json(movies);
});

要测试这是否正常工作,请运行您的应用程序,然后打开终端并输入 -

curl -i -H "Accept: application/json" -H "Content