官网传送门
目录基于 Node.js 平台,快速、开放、极简的 Web 开发框架
带你走进ExpressExpress 初体验
基本使用 托管静态资源Express 路由Express 中间件
全局中间件局部中间件中间件分类第三方中间件自定义中间件
带你走进ExpressExpress 是用于快速创建服务器的第三方模块。
进一步理解express
思考:不使用Express能否创建Web服务器?
答案:能,使用Node.js提供的原生http模块即可。
思考:既生瑜何生亮(有了http内置模块,为什么还有用Express)?
答案: http内置模块用起来很复杂,开发效率低;Express 是基于内置的 http模块进一步封装出来的,能够极大的提高开发效率。
思考: http内置模块与Express是什么关系?
答案:类似于浏览器中 Web API和jQuery的关系。后者是基于前者进一步封装出来的。
express能做什么
使用Express,我们可以方便、快速的创建Web网站的服务器或API接口的服务器。
对于前端程序员来说,最常见的两种服务器,分别是:
Web网站服务器:专门对外提供 Web 网页资源的服务器。API接口服务器:专门对外提供API接口的服务器。 Express 初体验 基本使用
安装 Express:
npm install express
创建服务器,监听客户端请求,并返回内容:
app.get(url,callback());app.post(url,callback());监听客户端发出的get和post请求
app.send() 响应内容到客户端的方法
通过req.query对象,可以访问到客户端通过查询字符串的形式发送到服务器的参数,(默认情况下,req.query是一个空对象),每一个参数都是req.query对象的一个属性,我们可以直接使用 req.query.参数名 获取参数值
通过 req.params 对象,可以访问到 URL 中通过:匹配的动态参数,默认是一个空对象
const express = require('express') // 创建 web 服务器 const app = express() // 监听客户端的 GET 和 POST 请求(第一参数是客户请求的url,第二参数是要执行的函数) // 并向客户端响应(send方法)具体的内容 app.get('/user', (req, res) => { res.send({ name: 'zs', age: 20, gender: '男' }) }) app.post('/user', (req, res) => { res.send('请求成功') }) app.get('/', (req, res) => { // 通过 req.query 可以获取到客户端发送过来的查询参数 console.log(req.query) res.send(req.query) }) // 这里的 :id 是一个动态的参数 app.get('/user/:ids/:username', (req, res) => { // req.params 是动态匹配到的 URL 参数,默认是一个空对象 console.log(req.params) res.send(req.params) }) app.listen(80, () => { console.log('express server running at http://127.0.0.1') })
补充:url中的动态匹配
动态匹配的时候的,出来前面的冒号是固定的,后面的名字都不是固定的,都是咱们自己起的,它对应的是咱们得到的动态匹配对象 req.params 中的属性名,比如
const express = require('express') const app = express() // 这里的 :id 是一个动态的参数 app.get('/user/:id', (req, res) => { // req.params 是动态匹配到的 URL 参数,默认是一个空对象 console.log(req.params) res.send(req.params) }) app.listen(80, () => { console.log('express server running at http://127.0.0.1') })
若请求发送的地址为http://127.0.0.1/user/2
则其中的req.params对象里面就包含一个属性 属性名为id,属性值为2
而且动态匹配的参数可以有多个(属性和值会一一对应),比如服务器端的监听请求的代码为
app.get('/user/:id/:username', (req, res) => { ... })
若请求发送的地址为http://127.0.0.1/user/2/admin
则其中的req.params对象里面就包含两个属性 属性1名为id,值为2;属性2名为username,值为admin
托管静态资源通过 express.static() 方法可创建静态资源服务器,向外开放访问静态资源。
Express 在指定的静态目录中查找文件,并对外提供资源的访问路径,存放静态文件的目录名不会出现在 URL 中
(由于目录名并不出现在url中,也就是说我们可以直接将指定的文件夹托管为静态资源,然后别人就可以直接通过网址再加上对应的文件名就可以访问对应的文件了,此时我们这个托管的文件夹也就相当于一个服务器的根目录了)
访问静态资源时,会根据托管顺序查找文件*(这种情况只会出现在托管多个静态资源的情况)*
可为静态资源访问路径添加前缀(称为 挂载前缀)
(我们使用的挂载前缀的前缀名也并不是固定的,使我们自己起的,而且也可以和托管的文件夹的名字不同------目的是:用户需要通过咱们的地址(如127.0.0.1)加上咱们指定的前缀,才能够访问到咱们的托管资源的,不加前缀就不可以!)
app.use(express.static('public')) app.use(express.static('files')) app.use('/bruce', express.static('bruce'))
小工具-nodemon
1.用处:
在编写调试Node.js项目的时候,如果修改了项目的代码,则需要频繁的手动close掉,然后再重新启动,非常繁琐.
现在,我们可以使用nodemon这个工具,它能够监听项目文件的变动,当代码被修改后,nodemon会自动帮我们重启项目,极大方便了开发和调试。
2.安装:
在终端执行命令:(将其安装为全局的工具)
npm i nodemon -g
2.使用:
用nodemon来代替node的使用
当基于Node.js编写了一个网站应用的时候,传统的方式,是运行node app.js 命令,来启动项目。这样做的坏处是:代码被修改之后,需要手动重启项目。
现在,我们可以将node命令替换为nodemon命令,使用nodemon app.js 来启动项目。这样做的好处是:代码被修改之后,会被nodemon监听到,从而实现自动重启项目的效果。
在Express 中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
Express 中的路由分为3部分,分别是 请求的类型(method)、请求的URL地址(path)、处理函数(handler),格式如下:
app.method(path,handler)
路由的匹配过程:
在匹配时,会按照路由的顺序(代码中的定义顺序)进行匹配,如果请求类型和请求的URL同时匹配成功,则 Express 会将这次请求,转交给对应的function函数进行处理。
每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。
路由最简单的用法:把路由挂载到app上面:
示例代码如下:
const express = require('express'); // 创建web服务器 const app = express(); //挂载路由 app.get('/',(req,res)=>{res.send('Hello World!')}) app.post('/',(req,res)=>{res.send('Hello World!')}) //启动并监听Web服务器 app.listen(80, () => { console.log('express server running at localhost!') })
模块化路由:为了方便路由进行模块化的管理,Express并不建议将路由直接挂载带app上面,而是推荐将路由抽离为单独的模块(其实就是将 根据用户请求地址,方式所执行的对应函数 给单独封装,以方便管理)。具体的步骤如下:
创建路由模块对应的js文件调用express.Router()函数创建路由对象向路由对象上面挂载具体的路由使用moudle.exports向外共享路由对象使用app.use()函数注册路由模块
示例代码如下:
1.路由模块对应的js文件 router.js
const express = require('express') // 创建路由对象 const router = express.Router() // 挂载具体路由 router.get('/user/list', (req, res) => { res.send('Get user list.') }) router.post('/user/add', (req, res) => { res.send('Add new user.') }) // 向外导出路由对象 module.exports = router
2.注册路由模块:
const express = require('express') const router = require('./router') const app = express() // 注册路由模块,添加访问前缀 app.use('/api', router) app.listen(80, () => { console.log('http://127.0.0.1') })
注意:app.use()函数的作用,就是注册全局中间件
Express 中间件中间件是指流程的中间处理环节
Express中间件的调用流程
服务器收到请求后,可先调用(可一个或多个)中间件进行预处理
Express的中间件,本质上是一个function处理函数,包含 req, res, next 三个参数,next()函数是实现多个中间件连续调用的关键,是连接本中间件和下一个中间件的桥梁(当一个中间件的相应的函数执行完之后,执行next()函数,来告诉人家该进行下一个中间件或者路由,到最后处理完毕在之后,响应这次请求),它表示把流转关系交给下一个中间件或路由
Express中间件的调用格式
中间件注意事项;
一点要在注册路由之前注册中间件(错误级别中间件除外)中间件可连续定义多个(客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用),与此同时,客户顿发送过来的请求,可以连续调用多个中间件进行处理。执行完业务代码之后,别忘记调用 next() 函数为了防止代码的逻辑混乱,调用next() 函数后就别写代码了多个中间件共享 req、 res对象(基于这一特性,我们可以在上游的中间件中,统一为req和res对象添加自定义的属性或方法,以供下游的中间件或者路由使用) 全局中间件
客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件,简称全局中间件。
通过 app.use(中间件函数) 定义的中间件,即为全局中间件,示例代码如下:
const express = require('express') const app = express() // 定义第一个全局中间件 app.use((req, res, next) => { console.log('调用了第1个全局中间件') next() }) // 定义第二个全局中间件 app.use((req, res, next) => { console.log('调用了第2个全局中间件') next() }) //定义一个路由函数,通过路由函数来测试中间件,也算是一个对请求的响应 app.get('/user', (req, res) => { res.send('User page.') }) app.listen(80, () => { console.log('http://127.0.0.1') })局部中间件
不使用app.use()定义的中间件,叫做局部生效的中间件,简称局部中间件。
const express = require('express') const app = express() // 定义中间件函数 const mw1 = (req, res, next) => { console.log('调用了第一个局部生效的中间件') next() } const mw2 = (req, res, next) => { console.log('调用了第二个局部生效的中间件') next() } // 两种定义局部中间件的方式(下面的前两中的定义中间件的函数的方式是等效的) app.get('/hello', mw2, mw1, (req, res) => res.send('hello page.')) app.get('/about', [mw1, mw2], (req, res) => res.send('about page.')) app.get('/user', (req, res) => res.send('User page.')) app.listen(80, function () { console.log('Express server running at http://127.0.0.1') })中间件分类
应用级别的中间件
通过 app.use() 或 app.get() 或 app.post() ,绑定到 app 实例上的中间件
路由级别的中间件
绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。
用法和应用级别中间件没有区别。应用级别中间件是绑定到 app 实例上,路由级别中间件绑定到 router 实例上。
代码示例如下:
const app = express() const router = express.Router() router.use(function (req, res, next) { console.log(1) next() }) app.use('/', router)
错误级别的中间件
用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题错误级别中间件的处理函数(有点相当于try-catch)中,必须有 4 个形参,形参顺序从前到后分别是 (err, req, res, next) 。错误级别的中间件必须注册在所有路由之后(一般的中间件都是需要在注册路由之前的)
代码示例如下:
const express = require('express') const app = express() //1.定义路由 //正常的话,本路由一经执行就会抛出错误,程序就没有办法正常的执行了, //但是错误级别中间件会捕获错误,并执行相应的内容,以及响应相关的错误信息来告诉开发人员:这里程序出错了,也防止了程序的崩溃 app.get('/', (req, res) => { //制造错误 throw new Error('服务器内部发生了错误!') res.send('Home page.') }) // 定义错误级别的中间件,**捕获整个项目的异常错误,从而防止程序的崩溃** app.use((err, req, res, next) => { //打印错误信息(err.message) console.log('发生了错误!' + err.message) //向客户端响应错误相关的内容 res.send('Error:' + err.message) }) app.listen(80, function () { console.log('Express server running at http://127.0.0.1') })
Express 内置中间件
自 Express 4.16.0 版本开始,Express 内置了 3 个常用的中间件,极大的提高了 Express 项目的开发效率和体验:
express.static 快速托管静态资源的内置中间件,例如: HTML 文件、图片、CSS 样式等(无兼容性)express.json 解析 JSON 格式的请求体数据(有兼容性,仅在 4.16.0+ 版本中可用)express.urlencoded 解析 URL-encoded 格式的请求体数据(有兼容性,仅在 4.16.0+ 版本中可用)在服务器端,可以使用req.body这个属性来接收客户端发送过来的json和urlencoded请求体数据默认情况下,如果不配置解析表单数据的中间件(就是下面代码示例的第二行代码)的话,则req.body默认等于undefined,配置之后默认就变成了空,才会对其进行赋值注意:除了错误级别的中间件,其他的中间件都要在路由之前进行配置
具体的使用代码写法如下
//配置解析 application/json 格式数据的内置中间件 app.use(express.json()) //配置解析 application/x-www-form-urlencoded 格式数据的内置中间件 app.use(express.urlencoded({ extended: false }))第三方中间件
非Express官方提供的,而是由第三方开发出来的中间件,叫做第三方中间件。在项目里面的话,我们可以按照我们的需要安装并且配置第三方中间件,从而提高项目的开发效率。
例如:在[email protected]之前的版本中,经常会使用body-parser这个第三方中间件,来解析请求体数据。使用步骤如下:
- 安装:运行 npm install body-parser导入:使用require()使用:调用app.use()注册并使用中间件
示例代码如下:
const parser =require('body-parser') app.use(parser.urlencoded({ extended: false }))自定义中间件
1.需求描述与实现步骤
自己动手模拟一个类似于express.urlencoded这样的中间件,来解析POST提交到服务器的表单数据。实验步骤:
定义中间件监听req的data事件(只要触发了data事件,就证明有数据发送到服务器了)监听req的end事件(只要触发了end事件,证明数据已经发送完毕了,服务器端也已经完整的额接收到了所有的数据)使用querystring模块解析请求体数据将解析出来的数据对象挂载为req.body将自定义中间件封装为模块
2.定义中间件
使用app.use()来定义全局生效的中间件,代码如下:
app.use(function(req,res,next)){ //中间件的业务逻辑 }
3.监听req的data事件
在中间件中,需要监听req对象的额data事件,来获取客户端发送到服务器的数据。
如果信息量比较大,无法一次性发送完毕,则客户端会把数据切断后,分批次发送到服务器。所以data事件可能会触发多次,每一次出发data事件,获取到的数据都只是整个数据的一部分,需要对手动接收到的数据进行拼接,从而得到最终的数据。
代码如下:
//定义变量,用来存储客户端发送过来的请求体数据
let str = ‘’
//监听req对象的data事件(客户端发送过来的新的请求体数据)
//使用on来绑定事件
req.on(‘data’, (chunk) => {
//拼接请求体数据,隐式转换为字符串
str += chunk
})
4.监听req的end事件
当请求体数据接收完毕之后,会自动触发req的end事件,因此,我们可以在req的end事件中,拿到并处理完整的的请求体数据。
示例代码如下:
req.on('end', () => { //打印完整的请求体数据 console.log(str) //TODO:把字符串格式的数据解析成对象格式的数据 })
5.使用querystring模块解析请求体数据
Node.js内置了一个querystring模块,专门用来处理查询字符串。通过这个模块提供的parse()函数,可以轻松把查询字符串,解析成对象的格式。
示例代码如下:
const qs = require('querystring'); //调用parse()方法,把查询的字符串解析为对象格式 const body = qs.parse(str);
6.将解析出来的数据对象挂载为req.body
上游的中间件和下游的中间件以及路由之间,共享着同一份req和res。所以,我们可以将解析出来的数据,挂载为req的自定义属性,命名为req.body,供下游使用。
示例代码:
req.on('end', () => { //调用parse()方法,把查询的字符串解析为对象格式 const body = qs.parse(str); //将解析出来的额请求体对象,挂载为req.body属性 req.body = body; //最后,一定要调用next()函数,执行后续的业务逻辑 next(); })
7.将自定义中间件封装为模块
为了优化代码的结构,我们可以把自定义的中间件函数,封装为独立的模块
示例代码如下:
//文件1---custom-body-parser.js const qs = require('querystring'); const bodyParser = function(req, res, next) {}; module.exports = bodyParser; //模块化拆分后的主体代码 // 1.导入自己的封装的中间件模块 const customBodyParser = require('./custom-body-parser'); // 2.将自定义的中间函数,注册为全局的中间件 app.use(customBodyParser);
具体的代码可点击 自定义解析表单数据的中间件案例代码 或 自定义解析表单数据的中间件案例文件 进行查看或者下载。
谢谢观看!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)