文章目录
- 一、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。










