文章目录
- 一、Express介绍
- 二、Express的基本使用
- 三、Express路由
- 四、托管静态资源
- 五、nodemon
- 六、Express中间件
- 七、Webstorm中创建Express项目
一、Express介绍
1、Express是什么
Express是基于Node.js平台,快速、开放、极简的Web开发框架。
ExpressExpress的作用和Node.js内置的http模块类似,它可以帮助开发人员快速创建Web服务器。
ExpressExpress是目前流行的基于Node.js运行环境的Web应用程序开发框架,它简洁且灵活,为Web应用程序提供了强大的功能。Express提供了一个轻量级模块,类似于jQuery(封装的工具库),它把Node.js的HTTP模块的功能封装在一个简单易用的接口中,用于扩展HTTP模块的功能,能够轻松地处理服务器的路由、响应、Cookie和HTTP请求的状态。
优势:
本质:是npm上的第三方包,提供了快速创建Web服务器的便捷方法。
https://www.expressjs.com.cn/
这是express中文官网,可以在上面学习关于express的知识。
2、为什么要使用Express
当我们想创建服务器时,由于Node.js提供的http内置模块使用起来很复杂,开发效率低,所以基于http模块,封装出了Express,极大的提高了开发效率。
3、express的作用
使用Express,可以方便、快速的创建Web网站的服务器或API接口的服务器。
Web网站服务器
:专门对外提供Web网页资源的服务器。
API接口服务器
:专门对外提供API接口的服务器。
二、Express的基本使用
1、安装
创建目录,并在该目录下新建目录,如server作为项目的根目录。
server目录下执行如下命令。
npm init(项目初始化,生成package.json文件)
npm install express(安装express)
安装完成后,在当前目录下打开命令行工具执行“npm list express”命令,可以查看Express版本。
2、创建基本的Web服务器
//1、导入express模块
const express = require('express');
//2、创建web服务器
const app = express();
//3、监听端口80,启动web服务器
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1');
});
3、监听GET请求
通过app.get()方法,可以监听客户端的GET请求,具体的语法格式如下:
//监听GET请求
//参数1:客户端请求的URL地址、
//参数2:请求对应的处理函数
// req:请求对象(包含了与请求相关的属性和方法)
// res:响应对象(包含了与响应相关的属性和方法)
app.get('请求URL',function(req,res){
//处理函数
})
4、监听POST请求
通过app.post()方法,可以监听客户端的POST请求,具体语法格式如下:
//参数1:客户端请求的URL地址
//参数2:请求对应的处理函数
// req:请求对象(包含了与请求相关的属性和方法)
// res:响应对象(包含了与响应相关的属性和方法)
app.post('请求URL',function(req,res){
//处理函数
})
5、把内容响应给客户端
下表中响应对象(res)上的方法可以向客户端发送响应,并终止
请求-响应循环。如果没有从路由处理程序调用这些方法,客户端请求将保持挂起
状态。
方法 | 说明 |
---|---|
res.download() | 提示要下载的文件。 |
res.end() | 结束响应过程。 |
res.json() | 发送一个JSON响应。 |
res.jsonp() | 发送JSON响应与JSONP支持。 |
res.redirect() | 重定向请求。 |
res.render() | 渲染视图模板。 |
res.send() | 发送各种类型的响应。 |
res.sendFile() | 以八位元流的形式发送文件。 |
res.sendStatus() | 设置响应状态码,并将其字符串表示作为响应体发送。 |
如通过res.send()方法,可以把处理好的内容,发送给客户端:
app.get('/user',(req,res)=>{
//向客户端发送JSON对象
res.send({
name:'haha',
age:90,
gender:'男'
})
})
app.post('/user',(req,res)=>{
//向客户端发送文本内容
res.send('请求成功')
})
6、举例
//1、导入express
const express = require('express');
//2、创建web服务器
const app = express();
//3、监听GET和POST请求,并向客户端响应具体的内容
app.get('/user',(req,res)=>{
//向客户端发送JSON对象
res.send({
name:'haha',
age:90,
gender:'男'
})
})
app.post('/user',(req,res)=>{
//向客户端发送文本内容
res.send('请求成功')
})
//4、启动web服务器
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1');
});
首先我们启动服务器
然后我们可以打开postman测试一下,我们的请求是否生效,首先测试GET请求,输入网址后点击send,就会出现以下结果,则响应成功。
然后测试POST请求,同样输入网址,选择POST,再点击send,出现以下结果,响应成功。
7、获取URL中携带的查询参数
通过req.query
对象,用于获取GET请求参数,即可以访问到客户端通过查询字符串的形式,发送到服务器的参数,会将GET参数转换为对象并返回。
app.get('/',(req,res)=>{
//req.query默认是一个空对象
//客户端使用 ?name=ahah&age=20 这种查询字符串形式,发送到服务器的参数,
//可以通过 req.query 对象访问到,例如:
//req.query.name req.query.age
console.log(req.query);
})
举例如下:
//1、导入express
const express = require('express');
//2、创建web服务器
const app = express();
//3、监听GET和POST请求,并向客户端响应具体的内容
app.get('/user',(req,res)=>{
//向客户端发送JSON对象
res.send({
name:'haha',
age:90,
gender:'男'
})
})
app.post('/user',(req,res)=>{
//向客户端发送文本内容
res.send('请求成功')
})
app.get('/',(req,res)=>{
//通过req.query可以获取到客户端发送过来的查询参数
//req.query默认是一个空对象
console.log(req.query);
res.send(req.query);
})
//4、启动web服务器
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1');
});
启动服务器后我们可以发现,终端里打印的为空对象,用postman测试时响应的也是空对象,这是因为req.query默认情况下为空对象。
接下来,我们重新测试,在postman输入的网址里输入查询字符串 ?name=haha&age=90,结果如下:
查询字符串成功显示。
8、获取URL中的动态参数
通过req.params
对象,可以访问到URL中,通过:
匹配到的动态参数,即获取路由传递的数据:
URL地址中,可以通过 :参数名
的形式,匹配动态参数值,此时传递的各种数据,id都会接受。
app.get('/user/:id',(req,res)=>{
//req.params默认是一个空对象
//里面存放着通过 : 动态匹配到的参数值
console.log(req.params)
})
举例如下:
//1、导入express
const express = require('express');
//2、创建web服务器
const app = express();
//注意:这里的 :id 是一个动态的参数
app.get('/user/:id/:age',(req,res)=>{
//req.params默认是一个空对象
//里面存放着通过 : 动态匹配到的URL参数值
console.log(req.params)
res.send(req.params)
})
//3、启动web服务器
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1');
});
我们可以通过postman测试一下:
9、获取post请求数据
(1)通过事件获取数据
req.on('data',function(mydata){})
首先我们先写一个post.html文件,里面使用form表单,使它可以传递数据给服务器端。
method:
设置请求方法,如post;
action:
设置请求url
name:
用于接收表单输入的数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>post</title>
</head>
<body>
<form action="/mylogin" method="post">
<label>
用户:
<input type="text" name="uname">
</label>
<br/>
<label>
密码:
<input type="password" name="upwd">
</label>
<br/>
<input type="submit">
</form>
</body>
</html>
其次创建一个服务器,根据提交按钮的请求,创建相应的路由。
const express = require('express');
const querystring = require('querystring')
const app = express();
app.get('/login',(req,res)=>{
//响应文件
res.sendFile(__dirname+'/post.html')
})
app.post('/mylogin',(req,res)=>{
//获取post请求数据
//事件
req.on('data',(mydata)=>{
//mydata数据是buffer数据,所以要转换为字符串,再解析为对象
var str = mydata.toString();
var obj = querystring.parse(str);
console.log(obj);
})
res.send('登录成功')
})
app.listen(80,()=>{
console.log('成功运行在http://127.0.0.1');
})
(2)body-parser模块
Express中的req.body用于获取POST请求参数,需要借助第三方body-parser模块
将POST参数转换为对象形式。利用req获取POST请求参数的示例代码如下。
app.post('url地址', (req, res) => {
res.send(req.body);
});
body-parser是一个解析HTTP请求体的模块,使用这个模块可以处理POST请求参数,使用起来非常方便。使用body-parser模块处理表单数据的示例代码如下。
app.use(bodyParser.urlencoded({ extended: false }))
{ extended: false}表示在方法的内部使用querystring系统模块来处理POST请求参数;
{ extended: true}表示使用qs第三方模块进行处理。
一般我们选择false
。
在处理POST请求参数之前,首先要完成body-parser模块的安装。
npm install body-parser
首先在index.js下写入如下代码:
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.post('/body', (req, res) => {
res.send(req.body);
});
app.listen(3000);
console.log('服务器启动成功');
再写一个body.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://localhost:3000/body" method="post">
用户名:
<input type="text" name="username" />
<br><br>
密码:
<input type="password" name="password" />
<br><br>
<input type="submit" value="提交">
</form>
</body>
</html>
首先启动服务器,在浏览器中打开body.html文件,输入用户名和密码,单击“提交”按钮,结果如图所示。
三、Express路由
1、路由的概念
(1)Express中的路由
在Express中,路由指的是客户端的请求与服务器处理函数之间的映射关系,即接收请求并作出响应,且一个路由只能处理一个请求,注意:每个路由可以有一个或多个处理函数,当路由匹配时执行。
Express中的路由分3部分组成,分别是请求的类型
、请求的URL地址
、处理函数
,格式如下:
app.METHOD(PATH,HANDLER)
app
:是express的一个实例。
METHOD
:是HTTP请求方法,小写,如get、post。
PATH
:是请求URL地址,即路由路径,它可以是字符串、字符串模式或正则表达式,一个路由会匹配它的路径后面紧跟的任何路径,然后再加上一个“/”。
HANDLER
:是路由匹配时执行的函数。
注意:在浏览器地址栏直接输入url,默认是get请求,传递数据使用查询字符串,浏览记录可能被浏览器缓存。
post请求需要使用表单提交来完成(现阶段),获取数据使用事件。
(2)简单路由举例
//匹配GET请求,且请求URL为 /
app.get('/',function(req,res){
res.send('Hello World!')
})
//匹配POST请求,且请求URL为 /
app.post('/',function(req,res){
res.send('Got a POST request')
})
练习一:创建路由,请求方法:get,请求url:/haha,响应多行内容。
假设我们创建一个这样的路由:
const express = require('express');
const app = express();
app.get('/haha',(req,res)=>{
res.send('第一行')
res.send('第二行')
})
app.listen(80,()=>{
console.log('成功运行在http://127.0.0.1');
})
启动服务器后打开浏览器,可以看到,只要‘第一行’被显示出来,第二行并没有,终端也显示出了如下错误:
所以注意:send只能使用一次
,不能多次使用。当我们想响应多行内容时,可以写成如下形式:
const express = require('express');
const app = express();
app.get('/haha',(req,res)=>{
res.send('第一行<br/>第二行')
})
app.listen(80,()=>{
console.log('成功运行在http://127.0.0.1');
})
练习二:创建路由,使用get请求方法,请求url:/heihei,响应并发送一个文件。
使用sendFile()方法响应文件,要求使用绝对路径,可以使用__dirname。
const express = require('express');
const app = express();
app.get('/heihei',(req,res)=>{
res.sendFile(__dirname+'/heihei.text')
})
app.listen(80,()=>{
console.log('成功运行在http://127.0.0.1');
})
heihei.text文件与index.js文件处于同一目录下,所以__dirname后加’/heihei.text’就可以:
练习三:get请求方法,请求url:/wuwu,想请求后跳转到网页https://www.baidu.com。
可以使用res.redirect()方法,它是响应的重定向,可跳转到另一个url。
const express = require('express');
const app = express();
app.get('/wuwu',(req,res)=>{
res.redirect('https://www.baidu.com')
})
app.listen(80,()=>{
console.log('成功运行在http://127.0.0.1');
})
(3)路由的匹配过程
每当一个请求到达服务器后,需要先经过路由的匹配,只有匹配成功之后,才会调用相应的处理函数。
在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的URL同时匹配成功,则Express会将这次请求,转交给对应的function函数进行处理。
注意:
①按照定义的先后顺序匹配
②请求类型和请求的URL同时匹配成功,才会调用相应的处理函数。
2、路由的使用
(1)简单用法
const express = require('express');
//创建web服务器,命名为app
const app = express();
//挂载路由
app.get('/',(req,res)=>{
res.send('Hello World!');
})
app.post('/',(req,res)=>{
res.send('Post Request');
})
//启动web服务器
app.listen(80,()=>{
console.log('server running at http://127.0.0.1');
})
启动服务器后可打开postman进行测试,结果如下:
(2)模块化路由
为了方便对路由进行模块化的管理,Express不建议将路由直接挂载到express实例上,而是推荐将路由抽离为单独的模块,即通过express.Router()实现模块化路由管理。也叫作路由器
,把所有的路由放入其中。
将路由抽离为单独模块的步骤如下:
注意:app.use()函数的作用就是用来注册全局中间件。将指定的中间件函数挂载在指定的路径上:当请求的路径基匹配path时,中间件函数将被执行。
app.use([path,] callback [, callback...])
path:
中间件函数被调用的路径,可以是:
表示路径的字符串;
一个路径模式;
匹配路径的正则表达式模式;
以上任何一种组合的数组。
因为path默认为" / ",所以没有路径的中间件将执行到应用程序的每一个请求。
callback:
回调函数,可以是:
一个中间件的功能;
一系列中间件函数(用逗号分隔);
中间件函数数组;
以上几点的结合。
可以提供多个像中间件一样的回调函数
(3)创建路由模块
express.Router()
方法用于创建路由对象route,然后使用route.get()和route.post()来注册当前模块路由对象下的二级路由
,这就是一个简单的模块化路由。
route对象下可以定义二级路由,示例代码如下:
route.get('请求路径', '请求处理函数'); // 接收并处理route 下的GET 请求
route.post('请求路径', '请求处理函数'); // 接收并处理route 下的POST 请求
举例如下:
var express = require('express');//导入express
var router = express.Router();//创建路由对象
router.get('/user/list',function(req,res){//挂载获取用户列表的路由
res.send('Get user list');
})
router.post('/user/add',function(req,res){//挂载添加用户的路由
res.send('Add new user');
})
module.exports = router;//向外导出路由对象
(4)注册路由模块
格式如下:
我们首先创建一个route.js作为路由模块,里面创建了两个二级路由。
var express = require('express');//导入express
var router = express.Router();//创建路由对象
router.get('/list',function(req,res){//挂载获取用户列表的路由
res.send('Get user list');
})
router.post('/add',function(req,res){//挂载添加用户的路由
res.send('Add new user');
})
module.exports = router;//向外导出路由对象
然后在index.js里创建服务器并注册路由模块,
使用app.use()注册route模块化路由。
const express = require('express');
const app = express()
//导入路由模块
const router = require('./route');
//注册路由模块
app.use(router);
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1')
})
启动服务器,然后可以在postman软件里验证是否成功:
(5)为路由模块添加前缀
格式如下:
//导入路由模块
const userRouter = require('./router');
//使用app.use()注册路由模块,并添加统一的访问前缀 /api
app.use('/api',userRouter)
四、托管静态资源
1、express.static()
express.static(root, [options])
根参数root
指定提供静态资产的根目录。这个函数通过组合req来确定要服务的文件。提供的根目录的Url。当文件未找到时,它不是发送404响应,而是调用next()转移到下一个中间件,允许堆叠和回退。
选项对象
的属性我们可以如下网址里查看:
https://www.expressjs.com.cn/4x/api.html#express.static
通过express.static(),我们可以方便地创建一个静态资源服务器,把所有的静态资源托管到某个目录,如果浏览器请求静态资源,就会自动到该目录下寻找,无需创建路由。如:通过如下代码就可以将public目录下的图片、CSS文件、JavaScript文件对外开放:
app.use(express.static('public'))
这样,当浏览器请求静态资源,会自动到public目录下查找,不需要再创建单独的路由响应文件。
注意:Express在指定的静态目录中查找文件,并对外提供资源的访问路径。因此,存放静态文件的目录名不会出现在URL中。
举例如下:
const express = require('express');
const app = express()
//调用express.static()方法,快速的对外提供静态资源
app.use(express.static('test'))
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1')
})
我们再在test文件夹下写两个html文件:
001.html代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>test1</title>
</head>
<style>
div{
width: 200px;
height: 150px;
font-size: 30px;
color: purple;
background-color: yellow;
}
</style>
<body>
<div>sunny</div>
</body>
</html>
002.html代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>test2</title>
</head>
<style>
div{
width: 200px;
height: 150px;
font-size: 30px;
color: yellow;
background-color: purple;
}
</style>
<body>
<div>funny</div>
</body>
</html>
启动服务器后,在浏览器中输入相应URL,结果如下:
2、托管多个静态资源目录
要托管多个静态资源目录,就要多次调用express.static()函数:
app.use(express.static('test'))
app.use(express.static('test1'))
注意:访问静态资源文件时,express.static()函数会根据目录的添加顺序查找所需的文件,找到后不会再向下查找。
举例如下:
const express = require('express');
const app = express()
//调用express.static()方法,快速的对外提供静态资源
app.use(express.static('test'))
app.use(express.static('test1'))
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1')
})
我们再建一个test2文件夹,里面写入003.css和004.html两个文件。
003.css:
div{
width: 500px;
height: 200px;
color: red;
background-color: wheat;
text-align: center;
line-height: 200px;
}
004.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./003.css">
</head>
<body>
<div>
好好学习,天天向上
</div>
</body>
</html>
现在启动服务器,在浏览器中输入相应URL,结果如下:
3、挂载路径前缀
如果希望在托管的静态资源访问路径之前,挂载路径前缀,可以使用下列方法:
app.use('/public',express.static('test'))
这样,就可以通过带有/public前缀地址来访问test目录中的文件了:
http://localhost:80/public/001.html
http://localhost:80/public/002.html
举例如下:
const express = require('express');
const app = express()
//调用express.static()方法,快速的对外提供静态资源
app.use('/public',express.static('test'))
app.use('/haha',express.static('test1'))
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1')
})
五、nodemon
1、为什么要使用nodemon
在编写调试Node.js项目时,如果修改了项目的代码,则需要频繁的手动关掉,然后再重新启动,非常繁琐。现在,我们可以使用nodemon
(https://www.npmjs.com/package/nodemon)这个工具,它能够监听项目文件的变动,当代码被修改后,nodemon会自动帮我们重启项目,极大方便了开发和调试。
2、安装nodemon
在终端中运行如下命令,即可将nodemon安装成全局可用的工具:
npm install -g nodemon
3、使用nodemon
当基于Node.js编写了一个网站应用的时候,传统的方式,是运行node app.js命令,来启动项目,这样做的缺点是:代码被修改之后,需要手动重启项目。
使用nodemon aoo.js来启动项目的优点是:代码被修改之后,会被nodemon监听到,从而实现自动重启项目的效果。
举例如下:
const express = require('express');
const app = express()
//调用express.static()方法,快速的对外提供静态资源
app.use(express.static('./test'))
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1')
})
我们首先使用nodemon index.js启动服务器:
现在修改代码:
const express = require(‘express’);
const app = express()
//调用express.static()方法,快速的对外提供静态资源
app.use(’/public’,express.static(’./test’))
app.listen(80,()=>{
console.log(‘express server running at http://127.0.0.1’)
})
从终端中可以看到,项目自动重启了。
六、Express中间件
1、中间件的概念
(1)什么是中间件
中间件(Middleware)
:官方解释为中间件函数是能够访问请求对象(req)、响应对象(res)和应用程序的请求-响应循环中的下一个函数的函数。下一个函数是Express路由器中的一个函数,当它被调用时,将执行接替当前中间件的中间件。
特指业务流程的中间处理环节,可以把中间件理解为处理客户端请求的一系列方法。如果把请求比作水流,那么中间件就是阀门,阀门可以控制水流是否继续向下流动,也可以在当前阀门处对水流进行排污处理,处理完成后再继续向下流动。下一个中间件函数通常由一个名为next的变量表示。
①中间件功能可以执行的任务
执行任何代码;
对请求和响应对象进行更改;
结束请求-响应周期;
调用栈中的下一个中间件。
②中间件机制可以实现的应用
路由保护
:当客户端访问登录页面时,可以先使用中间件判断用户的登录状态,如果用户未登录,则拦截请求,直接响应提示信息,并禁止用户跳转到登录页面。
网站维护公告
:在所有路由的最上面定义接收所有请求的中间件,直接为客户端做出响应,并提示网站正在维护中。
自定义404页面
:在所有路由的最上面定义接收所有请求的中间件,直接为客户端做出响应,并提示404页面错误信息。
(2)Express中间件的调用流程
当一个请求到达Express的服务器后,可以连续调用多个中间件,从而对这次请求进行预处理。
如果当前中间件函数没有结束请求-响应循环,它必须调用next()将控制权传递给下一个中间件函数。否则,请求将被搁置。
(3)Express中间件的格式
中间件主要由中间件方法
和请求处理函数
这两个部分构成。
中间件方法由Express 提供,负责拦截请求。
请求处理函数由开发人员编写,负责处理请求。
常用的中间件方法有app.get()、
app.post()、app.use(),其基本语法形式如下。
app.get('请求路径', '请求处理函数'); // 接收并处理GET 请求
app.post('请求路径', '请求处理函数'); // 接收并处理POST 请求
app.use('请求路径', '请求处理函数'); // 接收并处理所有请求,调用其他模块
举例:Express的中间件,本质上就是一个function处理函数,格式如下:
const express = require('express');
const app = express();
app.get('/',function(req,res,next){
next();
})
app.listen(3000);
注意:中间件函数的形参列表中,必须包含next参数,而路由处理函数中只包含req和res。
(4)next函数的作用
next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。
2、Express中间件的简单用法
(1)定义中间件函数
可以通过如下方式,定义一个最简单的中间件函数:
//常量bear所指向的,就是一个中间件函数
const bear = function(req,res,next){
console.log('中间件函数');
//注意:在当前中间件的业务处理完毕后,必须调用next()函数
//表示把流转关系转交给下一个中间件或路由
next()
}
(2)全局生效的中间件
app.use()方法的请求路径参数可以省略,省略时表示不指定路径,所有的请求都会被处理,即全局生效的中间件。
客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。
通过调用app.use(中间件函数),即可定义一个全局生效的中间件。
现在我们举一个例子来看一下,首先写一个index.js文件:
const express = require('express');
const app = express();
//常量bear所指向的,就是一个中间件函数
const bear = function(req,res,next){
console.log('中间件函数');
next()
}
//全局生效的中间件
app.use(bear)
//创建路由
app.get('/haha',(req,res)=>{
res.send('兴高采烈')
})
app.post('/wuwu',(req,res)=>{
res.send('喜极而泣')
})
app.listen(3000,()=>{
console.log('express server running at http://127.0.0.1')
});
启动服务器后,我们通过postman测试可以看出,每次请求时,全局生效的中间件bear都会被触发,打印出‘中间件函数’,然后通过next()函数,将流转关系转交给请求的下一个路由。
(3)定义全局中间件的简化形式
格式如下:
app.use(function(req,res,next){
console.log('中间件函数');
next()
})
(4)中间件的作用
多个中间件之间,共享同一份req和res,所以我们可以在上游的中间件中,统一为req或res对象添加自定义的属性和方法,供下游的中间件或路由进行使用。
举例如下:
const express = require('express');
const app = express();
app.use(function(req,res,next){
//获取到请求到达服务器的时间
const time = Date.now()
//为req对象,挂载自定义属性,从而把时间共享给后面的所有路由
req.gettime = time;
next()
})
app.get('/haha',(req,res)=>{
res.send('兴高采烈'+req.gettime)
})
app.post('/wuwu',(req,res)=>{
res.send('喜极而泣'+req.gettime)
})
app.listen(3000,()=>{
console.log('express server running at http://127.0.0.1')
});
启动服务器,利用postman进行测试:
可以看到,由于中间件使用next()函数将定义的req的属性gettime传给了下面的路由,所以请求时都可以使用到req.gettime属性值。
(5)定义多个全局中间件
可以使用app.use()连续定义多个全局中间件,表示对同一个路径的请求进行多次处理,客户端请求到达服务器之后,默认情况下Express会从上到下依次匹配中间件。举例如下:
const express = require('express');
const app = express();
app.use(function(req,res,next){
console.log('第一个中间件');
next()
})
app.use(function(req,res,next){
console.log('第二个中间件');
next()
})
app.get('/haha',(req,res)=>{
res.send('兴高采烈')
})
app.post('/wuwu',(req,res)=>{
res.send('喜极而泣')
})
app.listen(3000,()=>{
console.log('express server running at http://127.0.0.1')
});
(6)局部生效的中间件
不使用app.use()定义的中间件,叫做局部生效的中间件,举例如下:
const express = require('express');
const app = express();
//常量bear所指向的,就是一个中间件函数
const bear = function(req,res,next){
console.log('中间件函数');
next()
}
//bear这个中间件只在当前路由中生效,这种用法属于局部生效的中间件
app.get('/haha',bear,(req,res)=>{
res.send('兴高采烈')
})
//bear这个中间件不会影响下面这个路由
app.post('/wuwu',(req,res)=>{
res.send('喜极而泣')
})
app.listen(3000,()=>{
console.log('express server running at http://127.0.0.1')
});
(7)定义多个局部中间件
可以在路由中,通过如下两种等价的方式,使用多个局部中间件:
app.get('/',middle1,middle2,(req,res)=>{})
app.get('/',[middle1,middle2],(req,res)=>{})
举例如下:
const express = require('express');
const app = express();
const bear = function(req,res,next){
console.log('第一个中间件函数');
next()
}
const peach = function(req,res,next){
console.log('第二个中间件函数');
next()
}
app.get('/haha',(req,res)=>{
res.send('兴高采烈')
})
app.post('/wuwu',bear,peach,(req,res)=>{
res.send('喜极而泣')
})
app.listen(3000,()=>{
console.log('express server running at http://127.0.0.1')
});
(8)注意事项
①一定要在路由之前注册中间件,因为一个请求到达服务器后,是按顺序从前往后进行匹配的,一旦路由匹配成功就会直接响应,如果中间件放在路由后面就无法被调用;
②客户端发送过来的请求,可以连续调用多个中间件进行处理;
③执行完中间件的业务代码之后,不要忘记调用next()函数;
④为了防止代码逻辑混乱,调用next()函数后不要再写额外的代码;
⑤连续调用多个中间件时,多个中间件之间,共享req和res对象。
3、中间件的分类
Express官方把常见的中间件用法,分成了5类,分别是:
应用级别的中间件
、路由级别的中间件
、错误级别的中间件
、Express内置的中间件
、第三方的中间件
。
(1)应用级别的中间件
通过app.use()或app.get()或app.post(),绑定到app实例上的中间件,叫做应用级别的中间件,如下列代码:
//应用级别的中间件(全局中间件)
app.use((req,res,next)=>{
next()
})
//应用级别的中间件(局部中间件)
const middle = (req,res,next)=>{
next()
}
app.get('/',middle,(req,res)=>{})
(2)路由级别的中间件
绑定到express.Router()实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别,只不过,应用级别中间件是绑定到app实例上,路由级别中间件绑定到router实例上。举例如下:
var express = require('express');//导入express
var router = express.Router();//创建路由对象
//路由级别的中间件
router.use(function(req,res,next){
console.log('haha');
next()
})
app.use('/',router);
(3)错误级别的中间件
错误级别中间件的作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
错误处理中间件总是接受四个参数。必须提供四个参数来将其标识为一个错误处理中间件函数。即使不需要使用下一个对象,也必须指定它来维护签名。否则,下一个对象将被解释为常规中间件,并将无法处理错误。
格式:错误级别中间件的function处理函数中,4个形参顺序从前到后,分别是(err,req,res,next)。
举例如下:
我们在路由内部人为抛出了一个错误,这样程序就会停止,res.send()不能运行,此时,我们又定义了一个错误级别的中间件,它可以捕获错误,并在服务器中打印错误信息,向客户端响应错误信息,这样就防止了程序崩溃。
const express = require('express')
const app = express()
//定义路由
app.get('/',(req,res)=>{
//抛出一个自定义错误
throw new Error('这是一个错误')
res.send('haha')
})
//错误级别的中间件,捕获整个项目的异常错误,从而防止程序的崩溃
app.use((err,req,resnext)=>{
//在服务器中打印错误信息
console.log('错误是:'+err.message);
//向客户端响应错误相关的内容
res.send('Error!'+err.message);
})
app.listen(80,()=>{
console.log('express server running at http://127.0.0.1');
})
注意:错误件别的中间件,必须注册在所有路由之后,这样才能捕获到错误信息。
(4)Express内置的中间件
自Express4.16.0版本开始,Express内置了3个常用的中间件,极大的提高了Express项目的开发效率和体验:
①express.static
快速托管静态资源的内置中间件,例如:HTML文件、图片、CSS样式等(无兼容性)
②express.json
解析JSON格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)
③express.urlencoded
解析URL-encoded格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)
格式如下:
//配置解析application/json格式数据的内置中间件
app.use(express.json())
//配置解析application/x-www-form-urlencoded格式数据的内置中间件
app.use(express.urlencoded({
extended:false
}))
express.json的使用,举例如下:
const express = require("express")
//创建express的服务器实例
const app = express()
//注意:除了错误级别的中间件,其他中间件,必须在路由之前进行配置
//配置解析application/json格式数据的内置中间件
app.use(express.json())
app.post('/user',(req,res)=>{
//在服务器,可以使用req.body这个属性,接收客户端发送过来的请求体数据
//默认情况下,如果不配置解析表单数据的中间件,则req.body默认为undefined
console.log(req.body);
res.send('ok')
})
app.listen(80,function(){
console.log('express server running at http://127.0.0.1');
})
首先我们创建好一个服务器,并定义post路由,启动服务器后,可以通过postman向服务器发送一个JSON格式的请求体数据,注意:请求体数据要在Body面板中发送,所以我们将postman里的Params面板切换为Body面板,再点击raw,选择JSON,向服务器发送以下数据:
{
"username":"汪汪",
"age":"1"
}
点击send,结果如下,服务器成功接收到。
express.urlencoded的使用,举例如下:
const express = require("express")
//创建express的服务器实例
const app = express()
//注意:除了错误级别的中间件,其他中间件,必须在路由之前进行配置
//配置解析application/x-www-form-urlencoded格式数据的内置中间件
app.use(express.urlencoded({
extended:false
}))
app.post('/user',(req,res)=>{
//在服务器端,可以通过req.body来获取JSON格式的表单数据和url-encoded格式的数据
console.log(req.body);
res.send('ok')
})
app.listen(80,function(){
console.log('express server running at http://127.0.0.1');
})
首先我们创建好一个服务器,并定义post路由,启动服务器后,可以通过postman向服务器发送一个x-www-form-urlencoded格式的请求体数据,它为键值对格式,我们将postman里的Params面板切换为Body面板,再点击x-www-form-urlencoded,向服务器发送以下数据,点击send,从结果可以看到服务器成功接收到请求体数据。
(5)第三方的中间件
非Express官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件。在项目中,可以按需下载并配置第三方中间件,从而提高项目的开发效率。
例如:在express@4.16.0之前的版本中,经常使用body-parser这个第三方中间件,来解析请求体数据,使用步骤如下:
①运行npm install body-parser安装中间件;
②使用require导入中间件;
③调用app.use()注册并使用中间件;
④在post请求的路由中使用req.body返回对象。
举例如下:
const express = require("express")
//创建express的服务器实例
const app = express()
//导入解析表单数据的中间件body-parser
const parser = require('body-parser')
//使用app.use()注册body-parser中间件,将post请求的数据解析为对象。
app.use(parser.urlencoded({
//表示不使用第三方的qs模块,而是使用核心模块querystring来解析查询字符串为对象
extended:false
}))
app.post('/user',(req,res)=>{
//如果没有配置任何解析表单数据的中间件,则req.body默认等于undefined
console.log(req.body);
res.send('ok')
})
app.listen(80,function(){
console.log('express server running at http://127.0.0.1');
})
注意:Express内置的express.urlencoded中间件,就是基于body-parser这个第三方中间件进一步封装出来的。
4、自定义中间件
(1)需求描述与实现步骤
自己手动模拟一个类似于express.urlencoded这样的中间件,来解析POST提交到服务器的表单数据。
实现步骤:
①定义中间件
②监听req的data事件
③监听res的end事件
④使用querystring模块解析请求体数据
⑤将解析出来的数据对象挂载为req.body
⑥将自定义中间件封装为模块
(2)定义中间件
使用app.use()来定义全局生效的中间件,代码如下:
//使用app.use()注册解析表单数据的中间件
app.use(function(req,res,next){
//定义中间件具体的业务逻辑
})
(3)监听req的data事件
在中间件中,需要监听req对象的data事件,来获取客户端发送到服务器的数据。
如果数据量比较大,无法一次性发送完毕,则客户端会把数据切割后,分批发送到服务器。所以data事件可能会触发多次,每一次触发data事件时,获取到数据只是完整数据的一部分,需要手动对接收到的数据进行拼接。
代码如下:
//定义变量,用来存储客户端发送过来的请求体数据
let str = ''
//监听req对象的data事件(客户端发送过来的新的请求体数据)
req.on('data',(chunk)=>{
//拼接请求体数据,隐式转换为字符串
str += chunk
})
(4)监听req的end事件
当请求体数据接收完毕之后,会自动触发req的end事件。因此,我们可以在req的end事件中,拿到并处理完整的请求体数据。代码如下:
//监听req对象的end事件(请求体发送完毕后自动触发)
req.on('end',()=>{
//打印str中完整的请求体数据
console.log(str);
})
(5)使用querystring模块解析请求体数据
Node.js内置了一个querystring模块,专门用来处理查询字符串。通过这个模块提供的parse()函数,可以轻松把查询字符串,解析为对象的格式,示例代码如下:
//导入处理querystring的Node.js内置模块
const qs = require('querystring')
...
//调用qs.parse()方法,把字符串格式的请求体数据,解析为对象格式
const body = qs.parse(str);
(6)将解析出来的数据对象挂载为req.body
上游的中间件和下游的中间件及路由之间,共享同一份req和res。因此,我们可以将解析出来的数据,挂载为req的自定义属性,命名为req.body,共下游使用。代码如下:
req.on('end',()=>{
//调用qs.parse()方法,把字符串格式的请求体数据,解析为对象格式
const body = qs.parse(str);
//将解析出来的请求体对象,挂载为req.body属性
req.body = body;
next()
})
(7)全部代码
全部代码如下:
const express = require("express")
//创建express的服务器实例
const app = express()
//导入处理querystring的Node.js内置模块
const qs = require('querystring')
//使用app.use()注册解析表单数据的中间件
app.use(function(req,res,next){
//定义中间件具体的业务逻辑
//定义变量,用来存储客户端发送过来的请求体数据
let str = ''
//监听req对象的data事件(客户端发送过来的新的请求体数据)
req.on('data',(chunk)=>{
//拼接请求体数据,隐式转换为字符串
str += chunk
})
//监听req对象的end事件(请求体发送完毕后自动触发)
req.on('end',()=>{
//调用qs.parse()方法,把字符串格式的请求体数据,解析为对象格式
const body = qs.parse(str);
//将解析出来的请求体对象,挂载为req.body属性
req.body = body;
next()
})
})
app.post('/user',(req,res)=>{
res.send(req.body)
})
app.listen(80,function(){
console.log('express server running at http://127.0.0.1');
})
我们可以打开postman做一下测试:
(8)将自定义中间件封装为模块
为了优化代码的结果,我们可以把自定义的中间件函数,封装为独立的模块,实例代码如下:
首先我们创建一个route.js文件,里面含有自定义的中间件函数,然后把它导出:
const qs = require('querystring')
function bodyParser(req,res,next){
//定义中间件具体的业务逻辑
//定义变量,用来存储客户端发送过来的请求体数据
let str = ''
//监听req对象的data事件(客户端发送过来的新的请求体数据)
req.on('data',(chunk)=>{
//拼接请求体数据,隐式转换为字符串
str += chunk
})
//监听req对象的end事件(请求体发送完毕后自动触发)
req.on('end',()=>{
//调用qs.parse()方法,把字符串格式的请求体数据,解析为对象格式
const body = qs.parse(str);
//将解析出来的请求体对象,挂载为req.body属性
req.body = body;
next()
})
}
//向外导出解析请求体数据的中间件函数
module.exports = bodyParser
其次我们创建index.js文件,里面导入自定义的中间件模块,并使用它解析请求体数据:
const express = require("express")
//创建express的服务器实例
const app = express()
//导入自己封装的自定义模块
const customBodeParser = require('./route')
//将自定义的中间件函数,注册为全局可用的中间件
app.use(customBodeParser)
app.post('/user',(req,res)=>{
res.send(req.body)
})
app.listen(80,function(){
console.log('express server running at http://127.0.0.1');
})
使用postman测试结果如下:
七、Webstorm中创建Express项目
选择新项目,创建一个express项目,名为express-demo,WebStorm会直接创建好。
1、修改项目使用的端口号:bin->www
2、项目的静态资源目录:public
3、路由文件夹:routes
4、视图文件夹(页面文件):views
5、项目的启动文件(入口文件):app.js
附:
(1)moment模块,监控源代码的变化,若源代码发生改变会自动重新运行程序;
(2)在cmd中运行express项目的命令:npm start。