ExpressJS - 快速指南
ExpressJS - Overview
ExpressJS是一个Web应用程序框架,为您提供构建网站,Web应用程序和后端的简单API。 使用ExpressJS,您无需担心低级协议,进程等。
什么是快递?
Express提供了构建应用程序的最小接口。 它为我们提供了构建应用程序所需的工具。 它非常灵活,因为npm上有许多模块,可以直接插入Express。
Express由TJ Holowaychuk开发,由Node.js基金会和众多开源贡献者维护。
为何快递?
与像Rails和Django这样的竞争对手不同,它们有一种建立应用程序的看法,Express没有“最佳方式”去做某事。 它非常灵活且可插拔。
Pug
Pug(以前称为Jade)是一种用于编写HTML模板的简洁语言。 它 -
- 生成HTML
- 支持动态代码
- Supports reusability (DRY)
它是Express使用的最流行的模板语言之一。
MongoDB和Mongoose
MongoDB是一个开源的文档数据库,旨在简化开发和扩展。 该数据库还用于存储数据。
Mongoose是node.js的客户端API,可以从Express应用程序轻松访问我们的数据库。
ExpressJS - Environment
在本章中,我们将学习如何开始开发和使用Express Framework。 首先,您应该安装Node和npm(节点包管理器)。 如果您还没有这些,请转到节点设置以在本地系统上安装节点。 通过在终端中运行以下命令,确认已安装节点和npm。
node --version
npm --version
您应该得到类似于以下的输出。
v5.0.0
3.5.2
现在我们已经设置了Node和npm ,让我们了解npm是什么以及如何使用它。
Node Package Manager(npm)
npm是节点的包管理器。 npm Registry是Node.js,前端Web应用程序,移动应用程序,机器人,路由器以及JavaScript社区无数其他需求的开源代码包的公共集合。 npm允许我们访问所有这些包并在本地安装它们。 您可以在npmJS上浏览npm上可用的软件包列表。
如何使用npm?
使用npm安装程序包有两种方法:全局和本地。
Globally - 此方法通常用于安装开发工具和基于CLI的程序包。 要全局安装软件包,请使用以下代码。
npm install -g <package-name>
Locally - 此方法通常用于安装框架和库。 本地安装的软件包只能在其安装的目录中使用。 要在本地安装软件包,请使用与上面相同的命令,而不使用-g标志。
npm install <package-name>
每当我们使用npm创建项目时,我们需要提供一个package.json文件,该文件包含有关我们项目的所有详细信息。 npm使我们可以轻松设置此文件。 让我们建立我们的开发项目。
Step 1 - 启动终端/ cmd,创建一个名为hello-world的新文件夹和cd(创建目录)到其中 -
Step 2 - 现在使用npm创建package.json文件,使用以下代码。
npm init
它会询问您以下信息。
只需按住回车键,然后在“作者姓名”字段中输入您的姓名即可。
Step 3 - 现在我们已经设置了package.json文件,我们将进一步安装Express。 要安装Express并将其添加到我们的package.json文件,请使用以下命令 -
npm install --save express
要确认Express已正确安装,请运行以下代码。
ls node_modules #(dir node_modules for windows)
Tip - - save标志可以替换为-S标志。 此标志确保将Express添加为package.json文件的依赖项。 这有一个优点,下次我们需要安装项目的所有依赖项时,我们可以运行命令npm install ,它将在此文件中找到依赖项并为我们安装它们。
这就是我们使用Express框架开始开发所需的全部内容。 为了使我们的开发过程更容易,我们将从npm,nodemon安装一个工具。 一旦我们对任何文件进行更改,此工具就会重新启动我们的服务器,否则我们需要在每次修改文件后手动重启服务器。 要安装nodemon,请使用以下命令 -
npm install -g nodemon
您现在可以开始使用Express。
ExpressJS - Hello World
我们已经设置了开发,现在是时候开始使用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(route, callback)
此函数说明在调用给定路由的get请求时要执行的操作。 回调函数有2个参数, request(req)和response(res) 。 请求object(req)表示HTTP请求,并具有请求查询字符串,参数,正文,HTTP标object(req)属性。类似地,响应对象表示Express应用程序在收到HTTP请求时发送的HTTP响应。
res.send()
此函数将对象作为输入,并将其发送到请求客户端。 在这里,我们发送字符串"Hello World!" 。
app.listen(port, [host], [backlog], [callback]])
此函数绑定并侦听指定主机和端口上的连接。 端口是此处唯一必需的参数。
S.No. | 论点和描述 |
---|---|
1 | port 服务器应接受传入请求的端口号。 |
2 | host 域名。 将应用程序部署到云时,需要进行设置。 |
3 | backlog 排队的挂起连接的最大数量。 默认值为511。 |
4 | callback 在服务器开始侦听请求时调用的异步函数。 |
ExpressJS - Routing
Web框架在不同的路由上提供诸如HTML页面,脚本,图像等的资源。
以下函数用于在Express应用程序中定义路由 -
app.method(path, handler)
此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应用程序执行附加到此路由的callback函数并发送"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提供了一种特殊方法,用于使用相同的函数处理特定路径上的所有类型的http方法。 要使用此方法,请尝试以下操作。
app.all('/test', function(req, res){
res.send("HTTP method doesn't have any effect on this route!");
});
此方法通常用于定义中间件,我们将在中间件章节中讨论。
Routers
定义上述路线非常繁琐。 要将路由与我们的主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.js路由器处理。 things.js中的'/'路由实际上是'/ things' '/'路由。 访问localhost:3000/things /,您将看到以下输出。
路由器非常有助于分离问题并将代码的相关部分保持在一起。 它们有助于构建可维护的代码。 您应该在单个文件中定义与实体相关的路由,并使用index.js文件中的上述方法将其包含在内。
ExpressJS - HTTP Methods
HTTP方法在请求中提供,并指定客户端请求的操作。 下表列出了最常用的HTTP方法 -
S.No. | 方法和描述 |
---|---|
1 | GET GET方法请求指定资源的表示。 使用GET的请求应该只检索数据,不应该有其他影响。 |
2 | POST POST方法请求服务器接受请求中包含的数据,作为URI标识的资源的新对象/实体。 |
3 | PUT PUT方法请求服务器接受请求中包含的数据,作为对URI标识的现有对象的修改。 如果它不存在那么PUT方法应该创建一个。 |
4 | DELETE DELETE方法请求服务器删除指定的资源。 |
这些是最常见的HTTP方法。 要了解有关这些方法的更多信息,请访问http://www.iowiki.com/http/http_methods.htm 。
ExpressJS - URL Building
我们现在可以定义路由,但这些路由是静态的或固定的。 要使用动态路由,我们应该提供不同类型的路由。 使用动态路由允许我们根据它们传递参数和进程。
以下是动态路线的示例 -
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 。 将显示以下响应。
您可以使用其他任何内容替换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/iowiki/12345 。
您可以使用req.params对象访问您在URL中传递的所有参数。 注意,上面的2是不同的路径。 他们永远不会重叠。 此外,如果你想在获得'/things'时执行代码,那么你需要单独定义它。
模式匹配路由
您还可以使用regex来限制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);
请注意,这only匹配具有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);
Important - 这应该放在所有路由之后,因为Express匹配index.js文件从开始到结束的路由,包括您需要的外部路由器。
例如,如果我们定义与上面相同的路由,则在使用有效URL进行请求时,将显示以下输出。 -
对于不正确的URL请求,将显示以下输出。
ExpressJS - Middleware
中间件函数是可以访问request object (req) , response object (res)以及应用程序请求 - 响应周期中的下一个中间件函数的函数。 这些函数用于修改req和res对象,用于解析请求体,添加响应头等任务。
这是一个中间件功能的简单示例 -
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);
当我们在运行此代码后访问'/'时,我们会在Middle和控制台上收到响应 -
Start
End
下图总结了我们对中间件的了解 -
现在我们已经介绍了如何创建自己的中间件,让我们讨论一些最常用的社区创建的中间件。
第三方中间件
here提供Express的第三方中间件列表。 以下是一些最常用的中间件; 我们还将学习如何使用/装载这些 -
body-parser
这用于解析附加了有效负载的请求的主体。 要安装body解析器,我们需要使用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-parser
它解析Cookie标头并使用由cookie名称键入的对象填充req.cookies。 要安装cookie解析器,我们需要使用npm install --save cookie-parser安装它并安装它,在index.js中包含以下行 -
var cookieParser = require('cookie-parser');
app.use(cookieParser())
express-session
它使用给定的选项创建会话中间件。 我们将在Sessions部分讨论它的用法。
我们在ExpressJS中有许多其他第三方中间件。 但是,我们在这里只讨论了一些重要的问题。
ExpressJS - Templating
帕格是Express的模板引擎。 模板引擎用于通过HTML消除我们的服务器代码的混乱,将字符串与现有的HTML模板串联起来。 帕格是一个非常强大的模板引擎,具有各种功能,包括filters, includes, inheritance, interpolation等。有很多地面可以覆盖。
要使用Pug with 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! 帕格将这个非常简单的标记转换为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标记。
帕格的重要特征
现在让我们探讨一下帕格的一些重要特征。
简单标签
标签根据其缩进嵌套。 就像在上面的例子中一样, 《title》在《head》标签中缩进,因此它位于其中。 但是《body》标签是在同一个缩进上,所以它是《head》标签的兄弟。
我们不需要关闭标签,只要Pug遇到相同或外部缩进级别的下一个标签,它就会为我们关闭标签。
要将文本放在标记内,我们有3种方法 -
Space seperated
h1 Welcome to Pug
Piped text
div
| To insert multiline text,
| You can use the pipe operator.
Block of text
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.
注释 (Comments)
Pug使用与JavaScript(//)相同的语法来创建注释。 这些注释转换为html注释(“! - comment--”)。 例如,
//This is a Pug comment
此评论将转换为以下内容。
<!--This is a Pug comment-->
属性 (Attributes)
为了定义属性,我们在括号中使用逗号分隔的属性列表。 类和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: "IoWiki",
url:"http://www.iowiki.com"
});
});
app.listen(3000);
并在views目录中创建一个名为dynamic.pug的新视图文件,其代码如下 -
html
head
title=name
body
h1=name
a(href = url) URL
在浏览器中打开localhost:3000/dynamic_view; 你应该得到以下输出 -
我们也可以在文本中使用这些传递的变量。 要在标记文本之间插入传递的变量,我们使用#{variableName}语法。 例如,在上面的例子中,如果我们想从IoWiki中提出问候语,那么我们就可以完成以下操作。
html
head
title = name
body
h1 Greetings from #{name}
a(href = url) URL
这种使用值的方法称为interpolation 。 上面的代码将显示以下输出。 -
条件(Conditionals)
我们也可以使用条件语句和循环结构。
考虑以下 -
如果用户已登录,则页面应显示"Hi, User" ,如果没有,则显示"Login/Sign Up"链接。 为此,我们可以定义一个简单的模板,如 -
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"}
});
你会收到一条消息 - Hi, Ayush 。 但是如果我们没有传递任何对象或传递没有用户密钥的对象,那么我们将获得一个注册链接。
包含和组件
Pug提供了一种非常直观的方式来为网页创建组件。 例如,如果您看到新闻网站,则标识和类别的标题始终是固定的。 我们可以使用include功能,而不是将其复制到我们创建的每个视图。 以下示例显示了我们如何使用此功能 -
使用以下代码创建3个视图 -
HEADER.PUG
div.header.
I'm the header for this website.
CONTENT.PUG
html
head
title Simple template
body
include ./header.pug
h3 I'm the main content
include ./footer.pug
FOOTER.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 - Serving static files
静态文件是客户端从服务器下载的文件。 创建一个新目录public 。 Express,默认情况下不允许您提供静态文件。 您需要使用以下内置中间件启用它。
app.use(express.static('public'));
Note - Express会查找相对于静态目录的文件,因此静态目录的名称不是URL的一部分。
请注意,根路由现在设置为您的公共目录,因此您加载的所有静态文件将以root身份考虑公开。 要测试这是否正常,请在新的public目录中添加任何图像文件,并将其名称更改为“ 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 - Form data
表格是网络不可分割的一部分。 我们访问的几乎每个网站都为我们提供了提交或获取一些信息的表格。 要开始使用表单,我们将首先安装body-parser (用于解析JSON和url编码的数据)和multer(用于解析multipart/form数据)中间件。
要安装body-parser和multer ,请转到终端并使用 -
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应用程序将以相同的方式处理所有这些请求。 要阅读有关提出请求的不同方式的更多信息,请查看this页面。
ExpressJS - Database
我们不断收到请求,但最终没有将它们存储在任何地方。 我们需要一个数据库来存储数据。 为此,我们将使用名为MongoDB的NoSQL数据库。
要安装和阅读有关Mongo的信息,请点击此链接。
为了将Mongo与Express一起使用,我们需要一个用于节点的客户端API。 我们有多种选择,但对于本教程,我们将坚持使用mongoose 。 Mongoose用于MongoDB的Node中的document Modeling 。 对于文档建模,我们创建一个Model (非常类似于面向文档的编程中的class ),然后我们使用这个Model生成documents (就像我们在OOP中创建documents of a class )。 我们所有的处理都将在这些“文档”上完成,最后,我们将在这些文档中编写这些文档。
设置Mongoose
现在您已经安装了Mongo,让我们安装Mongoose,就像我们安装其他节点包一样 -
npm install --save mongoose
在我们开始使用mongoose之前,我们必须使用Mongo shell创建一个数据库。 要创建新数据库,请打开终端并输入“mongo”。 一个Mongo shell将启动,输入以下代码 -
use my_db
将为您创建一个新数据库。 无论何时打开mongo shell,它都将默认为“test”db,您必须使用与上面相同的命令更改为数据库。
要使用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表单; 此表单将帮助您获取人员的详细信息并将其保存到我们的数据库中。 要创建表单,请在views目录中创建一个名为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添加一个new get route来呈现此文档 -
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()函数将其保存到我们的DB中。 这是在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!
我们将在成功提交form(show_message.pug)收到以下回复form(show_message.pug) -
我们现在有一个创建persons的界面。
检索文件
Mongoose提供了许多检索文档的功能,我们将重点关注其中的3个。 所有这些函数也将回调作为最后一个参数,就像save函数一样,它们的参数是错误和响应。 这三个功能如下 -
Model.find(conditions, callback)
此函数查找与条件对象中的字段匹配的所有文档。 Mongo中使用的相同运算符也适用于mongoose。 例如,
Person.find(function(err, response){
console.log(response);
});
这将从该人的集合中获取所有文档。
Person.find({name: "Ayush", age: 20},
function(err, response){
console.log(response);
});
这将获取字段名称为“Ayush”且年龄为20的所有文档。
我们还可以提供我们需要的投影,即我们需要的字段。 例如,如果我们只想要nationality为"Indian"的人的names ,我们使用 -
Person.find({nationality: "Indian"}, "name", function(err, response){
console.log(response);
});
Model.findOne(conditions, callback)
此函数始终提取单个最相关的文档。 它与Model.find()具有完全相同的参数。
Model.findById(id, callback)
此函数将_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(condition, updates, callback)
此函数采用条件并将对象更新为输入,并将更改应用于与集合中的条件匹配的所有文档。 例如,以下代码将更新所有Person文档中的“American”国籍 -
Person.update({age: 25}, {nationality: "American"}, function(err, response){
console.log(response);
});
Model.findOneAndUpdate(condition, updates, callback)
它根据查询找到一个文档,并根据第二个参数进行更新。 它还需要一个回调作为最后一个参数。 让我们执行以下示例来理解该函数
Person.findOneAndUpdate({name: "Ayush"}, {age: 40}, function(err, response) {
console.log(response);
});
Model.findByIdAndUpdate(id, updates, callback)
此函数更新由其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替换为您创建的people的ID) -
curl -X PUT --data "name = James&age = 20&nationality = American
"http://localhost:3000/people/507f1f77bcf86cd799439011
这将使用上述详细信息更新与路径中提供的id相关联的文档。
删除文件
我们已经介绍了Create, Read和Update ,现在我们将看到如何使用Mongoose来Delete文档。 我们这里有3个功能,就像更新一样。
Model.remove(condition, [callback])
此函数将条件对象作为输入,并删除与条件匹配的所有文档。 例如,如果我们需要删除所有20岁的人,请使用以下语法 -
Person.remove({age:20});
Model.findOneAndRemove(condition, [callback])
此函数根据条件对象删除single最相关的文档。 让我们执行以下代码来理解相同的内容。
Person.findOneAndRemove({name: "Ayush"});
Model.findByIdAndRemove(id, [callback])
此函数删除由其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 - Cookies
Cookie是简单的小文件/数据,通过服务器请求发送到客户端并存储在客户端。 每次用户加载网站时,都会随请求一起发送此cookie。 这有助于我们跟踪用户的操作。
以下是HTTP Cookie的众多用途 -
- 会话管理
- 个性化(推荐系统)
- User tracking
要在Express中使用cookie,我们需要cookie-parser中间件。 要安装它,请使用以下代码 -
npm install --save cookie-parser
现在要使用Express,我们将需要cookie-parser 。 cookie-parser是一个中间件,用于parses cookies attached to the client request object 。 要使用它,我们将在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 - Sessions
HTTP是无状态的; 为了将请求与任何其他请求相关联,您需要一种在HTTP请求之间存储用户数据的方法。 Cookie和URL参数都是在客户端和服务器之间传输数据的合适方式。 但它们都是可读的并且在客户端。 Sessions解决了这个问题。 您为客户端分配一个ID,并使用该ID进行所有进一步的请求。 与客户端关联的信息存储在链接到此ID的服务器上。
我们需要Express-session ,所以使用以下代码安装它。
npm install --save express-session
我们将会话和cookie-parser中间件放在适当的位置。 在此示例中,我们将使用默认存储来存储会话,即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 - Authentication
身份验证是将提供的凭据与本地操作系统或身份验证服务器中授权用户信息的数据库中的凭据进行比较的过程。 如果凭据匹配,则完成该过程并授予用户访问权限。
为了我们创建一个身份验证系统,我们需要创建一个注册页面和一个用户密码存储。 以下代码为我们创建了一个帐户并将其存储在内存中。 这只是为了演示的目的; 建议始终使用持久存储(数据库或文件)来存储用户信息。
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.的新视图signup.jade.
SIGNUP.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检查此页面是否加载。
我们已经为这两个字段设置了必需的属性,因此在我们提供id和密码之前,支持HTML5的浏览器不会让我们提交此表单。 如果有人尝试使用没有用户ID或密码的卷曲请求进行注册,则会显示错误。 在视图中创建一个名为protected_page.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来检查用户是否已登录checkSignIn protected_page使用此函数。 要将用户注销,我们会销毁会话。
现在让我们创建登录页面。 将视图命名为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_page -
现在退出应用程序。 这会将我们重定向到登录页面 -
此路由受到保护,如果未经身份验证的人试图访问它,他将被编辑到我们的登录页面。 这完全是关于基本用户身份验证。 始终建议我们使用持久会话系统并使用哈希进行密码传输。 现在有更好的方法来验证用户,利用JSON令牌。
ExpressJS - RESTFul APIs
始终需要API来创建移动应用程序,单页面应用程序,使用AJAX调用并向客户端提供数据。 如何构造和命名这些API和端点的流行架构风格称为REST(Representational Transfer State) 。 HTTP 1.1设计考虑了REST原则。 REST由Roy Fielding在2000年的Paper Fielding Dissertations中引入。
RESTful URI和方法为我们提供了处理请求所需的几乎所有信息。 下面给出的表总结了如何使用各种动词以及如何命名URI。 我们将在最后创建一个电影API; 现在让我们讨论一下它的结构。
方法 | URI | 细节 | 功能 |
---|---|---|---|
GET | /movies | Safe, cachable | 获取所有电影及其详细信息的列表 |
GET | /movies/1234 | Safe, cachable | 获取Movie id 1234的详细信息 |
POST | /movies | N/A | 使用提供的详细信息创建新电影。 Response包含此新创建资源的URI。 |
PUT | /movies/1234 | Idempotent | 修改影片ID 1234(如果尚不存在,则创建一个)。 Response包含此新创建资源的URI。 |
DELETE | /movies/1234 | Idempotent | 应删除电影ID 1234(如果存在)。 响应应包含请求的状态。 |
DELETE or PUT | /movies | Invalid | 应该是无效的。 DELETE和PUT应指定他们正在处理的资源。 |
现在让我们在Express中创建此API。 我们将使用JSON作为我们的传输数据格式,因为它易于在JavaScript中使用并具有其他好处。 将movies.js文件替换为movies.js文件,如以下程序中所示。
index.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。
首先设置movies.js文件。 我们没有使用数据库来存储电影,而是将它们存储在内存中; 所以每次服务器重启时,我们添加的电影都会消失。 这可以使用数据库或文件(使用节点fs模块)轻松模仿。
一旦导入Express,然后创建一个路由器并使用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路线
让我们定义获取所有电影的GET路线 -
router.get('/', function(req, res){
res.json(movies);
});
要测试这是否正常,运行您的应用程序,然后打开您的终端并输入 -
curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET
localhost:3000/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}]
我们有一条获得所有电影的路线。 现在让我们创建一个通过其id获取特定电影的路线。
router.get('/:id([0-9]{3,})', function(req, res){
var currMovie = movies.filter(function(movie){
if(movie.id == req.params.id){
return true;
}
});
if(currMovie.length == 1){
res.json(currMovie[0])
} else {
res.status(404);//Set status to 404 as movie was not found
res.json({message: "Not Found"});
}
});
这将根据我们提供的ID为我们提供电影。 要检查输出,请在终端中使用以下命令 -
curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET
localhost:3000/movies/101
你会得到以下回应 -
{"id":101,"name":"Fight Club","year":1999,"rating":8.1}
如果您访问无效路由,则会产生cannot GET error而如果您访问ID不存在的有效路由,则会产生404错误。
我们完成了GET路由,让我们现在转到POST路由。
POST路线
使用以下路由处理POSTed数据 -
router.post('/', function(req, res){
//Check if all fields are provided and are valid:
if(!req.body.name ||
!req.body.year.toString().match(/^[0-9]{4}$/g) ||
!req.body.rating.toString().match(/^[0-9]\.[0-9]$/g)){
res.status(400);
res.json({message: "Bad Request"});
} else {
var newId = movies[movies.length-1].id+1;
movies.push({
id: newId,
name: req.body.name,
year: req.body.year,
rating: req.body.rating
});
res.json({message: "New movie created.", location: "/movies/" + newId});
}
});
这将创建一个新电影并将其存储在电影变量中。 要检查此路线,请在终端中输入以下代码 -
curl -X POST --data "name = Toy%20story&year = 1995&rating = 8.5" http://localhost:3000/movies
将显示以下回复 -
{"message":"New movie created.","location":"/movies/105"}
要测试是否已将其添加到movies对象,请再次运行/movies/105的get请求。 将显示以下回复 -
{"id":105,"name":"Toy story","year":"1995","rating":"8.5"}
让我们继续创建PUT和DELETE路由。
PUT路线
PUT路由与POST路由几乎相同。 我们将指定要更新/创建的对象的id。 按以下方式创建路径。
router.put('/:id', function(req, res){
//Check if all fields are provided and are valid:
if(!req.body.name ||
!req.body.year.toString().match(/^[0-9]{4}$/g) ||
!req.body.rating.toString().match(/^[0-9]\.[0-9]$/g) ||
!req.params.id.toString().match(/^[0-9]{3,}$/g)){
res.status(400);
res.json({message: "Bad Request"});
} else {
//Gets us the index of movie with given id.
var updateIndex = movies.map(function(movie){
return movie.id;
}).indexOf(parseInt(req.params.id));
if(updateIndex === -1){
//Movie not found, create new
movies.push({
id: req.params.id,
name: req.body.name,
year: req.body.year,
rating: req.body.rating
});
res.json({message: "New movie created.", location: "/movies/" + req.params.id});
} else {
//Update existing movie
movies[updateIndex] = {
id: req.params.id,
name: req.body.name,
year: req.body.year,
rating: req.body.rating
};
res.json({message: "Movie id " + req.params.id + " updated.",
location: "/movies/" + req.params.id});
}
}
});
此路由将执行上表中指定的功能。 它将使用新的详细信息更新对象(如果存在)。 如果它不存在,它将创建一个新对象。 要检查路由,请使用以下curl命令。 这将更新现有的电影。 要创建新电影,只需将ID更改为不存在的ID。
curl -X PUT --data "name = Toy%20story&year = 1995&rating = 8.5"
http://localhost:3000/movies/101
Response
{"message":"Movie id 101 updated.","location":"/movies/101"}
删除路线
使用以下代码创建删除路由。 -
router.delete('/:id', function(req, res){
var removeIndex = movies.map(function(movie){
return movie.id;
}).indexOf(req.params.id); //Gets us the index of movie with given id.
if(removeIndex === -1){
res.json({message: "Not found"});
} else {
movies.splice(removeIndex, 1);
res.send({message: "Movie id " + req.params.id + " removed."});
}
});
检查路线的方式与检查其他路线的方式相同。 删除成功后(例如id 105),您将获得以下输出 -
{message: "Movie id 105 removed."}
最后,我们的movies.js文件将如下所示。
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}
];
router.get('/:id([0-9]{3,})', function(req, res){
var currMovie = movies.filter(function(movie){
if(movie.id == req.params.id){
return true;
}
});
if(currMovie.length == 1){
res.json(currMovie[0])
} else {
res.status(404); //Set status to 404 as movie was not found
res.json({message: "Not Found"});
}
});
router.post('/', function(req, res){
//Check if all fields are provided and are valid:
if(!req.body.name ||
!req.body.year.toString().match(/^[0-9]{4}$/g) ||
!req.body.rating.toString().match(/^[0-9]\.[0-9]$/g)){
res.status(400);
res.json({message: "Bad Request"});
} else {
var newId = movies[movies.length-1].id+1;
movies.push({
id: newId,
name: req.body.name,
year: req.body.year,
rating: req.body.rating
});
res.json({message: "New movie created.", location: "/movies/" + newId});
}
});
router.put('/:id', function(req, res) {
//Check if all fields are provided and are valid:
if(!req.body.name ||
!req.body.year.toString().match(/^[0-9]{4}$/g) ||
!req.body.rating.toString().match(/^[0-9]\.[0-9]$/g) ||
!req.params.id.toString().match(/^[0-9]{3,}$/g)){
res.status(400);
res.json({message: "Bad Request"});
} else {
//Gets us the index of movie with given id.
var updateIndex = movies.map(function(movie){
return movie.id;
}).indexOf(parseInt(req.params.id));
if(updateIndex === -1){
//Movie not found, create new
movies.push({
id: req.params.id,
name: req.body.name,
year: req.body.year,
rating: req.body.rating
});
res.json({
message: "New movie created.", location: "/movies/" + req.params.id});
} else {
//Update existing movie
movies[updateIndex] = {
id: req.params.id,
name: req.body.name,
year: req.body.year,
rating: req.body.rating
};
res.json({message: "Movie id " + req.params.id + " updated.",
location: "/movies/" + req.params.id});
}
}
});
router.delete('/:id', function(req, res){
var removeIndex = movies.map(function(movie){
return movie.id;
}).indexOf(req.params.id); //Gets us the index of movie with given id.
if(removeIndex === -1){
res.json({message: "Not found"});
} else {
movies.splice(removeIndex, 1);
res.send({message: "Movie id " + req.params.id + " removed."});
}
});
module.exports = router;
这样就完成了我们的REST API。 现在,您可以使用这种简单的架构风格和Express创建更复杂的应用程序。
ExpressJS - Scaffolding
脚手架允许我们轻松地skeleton for a web application创建skeleton for a web application 。 我们手动创建公共目录,添加中间件,创建单独的路径文件等。脚手架工具为我们设置所有这些东西,以便我们可以直接开始构建我们的应用程序。
我们将使用的脚手架叫做Yeoman 。 它是为Node.js构建的脚手架工具,但也有几个其他框架的生成器(如flask,rails,django等)。 要安装Yeoman,请在终端中输入以下命令 -
npm install -g yeoman
Yeoman使用生成器来构建应用程序。 要查看npm上可用的生成器与Yeoman一起使用,您可以单击此link 。 在本教程中,我们将使用'generator-Express-simple' 。 要安装此生成器,请在终端中输入以下命令 -
npm install -g generator-express-simple
要使用此生成器,请输入以下命令 -
yo express-simple test-app
系统会向您询问一些简单的问题,例如您希望在应用中使用哪些内容。 选择以下答案,或者如果您已经了解这些技术,那么请选择您希望的方式。
express-simple comes with bootstrap and jquery
[?] Select the express version you want: 4.x
[?] Do you want an mvc express app: Yes
[?] Select the css preprocessor you would like to use: sass
[?] Select view engine you would like to use: jade
[?] Select the build tool you want to use for this project: gulp
[?] Select the build tool you want to use for this project: gulp
[?] Select the language you want to use for the build tool: javascript
create public/sass/styles.scss
create public/js/main.js
create views/layout.jade
create views/index.jade
create views/404.jade
create app.js
create config.js
create routes/index.js
create package.json
create bower.json
identical .bowerrc
identical .editorconfig
identical .gitignore
identical .jshintrc
create gulpfile.js
I'm all done. Running bower install & npm install for you to install the
required dependencies. If this fails, try running the command yourself.
然后它将为您创建一个新的应用程序,安装所有依赖项,向您的应用程序添加几个页面(主页,404找不到页面等)并为您提供一个目录结构。
这台发电机为我们创造了一个非常简单的结构 探索可用于Express的众多发电机,并选择适合您的发电机。 使用所有发电机的步骤是相同的。 您需要安装一台发电机,使用Yeoman运行它; 它会问你一些问题,然后根据你的答案为你的应用程序创建一个框架。
ExpressJS - Error Handling
Express中的错误处理是使用中间件完成的。 但是这个中间件具有特殊属性。 错误处理中间件的定义方式与其他中间件函数的定义相同,只是错误处理函数MUST have four arguments而不是三个 - err, req, res, next 。 例如,要发送任何错误的响应,我们可以使用 -
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});
直到现在我们正在处理路线本身的错误。 错误处理中间件允许我们分离错误逻辑并相应地发送响应。 我们在中间件中讨论的next()方法将我们带到下一个middleware/route handler 。
对于错误处理,我们有next(err)函数。 对此函数的调用会跳过所有中间件并将我们匹配到该路由的下一个错误处理程序。 让我们通过一个例子来理解这一点。
var express = require('express');
var app = express();
app.get('/', function(req, res){
//Create an error and pass it to the next function
var err = new Error("Something went wrong");
next(err);
});
/*
* other route handlers and middleware here
* ....
*/
//An error handling middleware
app.use(function(err, req, res, next) {
res.status(500);
res.send("Oops, something went wrong.")
});
app.listen(3000);
可以在路由或包含条件之后策略性地放置此错误处理中间件以检测错误类型并相应地响应客户端。 以上程序将显示以下输出。
ExpressJS - Debugging
Express使用Debug模块在内部记录有关路由匹配,中间件功能,应用程序模式等的信息。
要查看Express中使用的所有内部日志,请在启动应用程序时将DEBUG环境变量设置为Express:* -
DEBUG = express:* node index.js
将显示以下输出。
当应用程序的某个组件无法正常运行时,这些日志非常有用。 这个详细的输出可能有点压倒性。 您还可以将DEBUG变量限制为要记录的特定区域。 例如,如果您希望将记录器限制为应用程序和路由器,则可以使用以下代码。
DEBUG = express:application,express:router node index.js
默认情况下,调试处于关闭状态,并在生产环境中自动打开。 调试也可以扩展以满足您的需求,您可以在其npm页面上阅读更多相关信息。
ExpressJS - Best Practices
与具有定义的处理方式,文件结构等的Django和Rails不同,Express不遵循定义的方式。 这意味着您可以按照自己喜欢的方式构建应用程序。 但是随着应用程序的大小增加,如果它没有明确定义的结构,则很难维护它。 在本章中,我们将介绍常用的目录结构和关注点分离,以构建我们的应用程序。
首先,我们将讨论创建节点和Express应用程序的最佳实践。
始终使用npm init开始一个节点项目。
始终使用--save或--save-dev安装依赖项。 这将确保如果您移动到其他平台,则可以运行npm install来安装所有依赖项。
坚持使用小写文件名和camelCase变量。 如果查看任何npm模块,它以小写字母命名并用短划线分隔。 每当您需要这些模块时,请使用camelCase。
不要将node_modules推送到您的存储库。 相反,npm会在开发机器上安装所有内容。
使用config文件存储变量
将路由分组并隔离到自己的文件。 例如,在我们在REST API页面中看到的电影示例中进行CRUD操作。
目录结构
现在让我们讨论Express的目录结构。
Websites
Express没有用于创建应用程序的社区定义结构。 以下是网站的主要项目结构。
test-project/
node_modules/
config/
db.js //Database connection and configuration
credentials.js //Passwords/API keys for external services used by your app
config.js //Other environment variables
models/ //For mongoose schemas
users.js
things.js
routes/ //All routes for different entities in different files
users.js
things.js
views/
index.pug
404.pug
...
public/ //All static content being served
images/
css/
javascript/
app.js
routes.js //Require all routes in this and then require this file in
app.js
package.json
还有其他方法可以使用Express构建网站。 您可以使用MVC设计模式构建网站。 有关更多信息,请访问以下链接。
https://code.tutsplus.com/tutorials/build-a-complete-mvc-website-with-expressjs--net-34168
和,
https://www.terlici.com/2014/08/25/best-practices-express-structure.html 。
RESTful API
API更易于设计; 他们不需要public或views目录。 使用以下结构构建API -
test-project/
node_modules/
config/
db.js //Database connection and configuration
credentials.js //Passwords/API keys for external services used by your app
models/ //For mongoose schemas
users.js
things.js
routes/ //All routes for different entities in different files
users.js
things.js
app.js
routes.js //Require all routes in this and then require this file in
app.js
package.json
您还可以使用自动生成器来获得类似的结构。
ExpressJS - Resources
本章列出了我们在本教程中使用的各种资源。
最重要的链接当然是Express API文档 - https://expressjs.com/en/4x/api.html
快递网站上提供的不同方面的指南也很有帮助 -
有关Express的有用书籍和博客列表,请访问https://expressjs.com/en/resources/books-blogs.html
有关Express的大多数中间件列表,请访问https://expressjs.com/en/resources/middleware.html
这些带有Express提示和技巧的博客可能会有所帮助 -
申请结构 - https://www.terlici.com/2014/08/25/best-practices-express-structure.html
RESTful API -
https://www.thepolyglotdeveloper.com/2015/10/create-a-simple-restful-api-with-node-js/
https://scotch.io/tutorials/build-a-restful-api-using-node-and-express-4
https://pixelhandler.com/posts/develop-a-restful-api-using-nodejs-with-express-and-mongoose
http://cwbuecheler.com/web/tutorials/2014/restful-web-app-node-express-mongodb/
对于高级身份验证,请使用PassportJS - http://passportjs.org