Express
一、初识Express
1.1 Express 简介
1.1.1 什么是Express简介
Express是基于Node.js平台,快速、开放、极简的Web开发框架
Express的作用和Node.js内置 的http模块类似,是专门用来创建Web服务器的
本质:就是一个npm上的第三方包,提供快速创建Web服务器的便捷方法
官网地址 http://www.expressjs.com.cn/
1.1.2 进一步理解Express
使用Node.js提供的原生http模块也可以创建Web服务器
Express是基于内置的http模块进一步封装出来的,提高开发效率
http模块与Express的关系 类似于Web API与JQuery的关系
1.1.3 Express能做什么
使用Express能够方便快捷的创建Web网站的服务器 或 API接口的服务器
Web网站服务器:专门对外提供Web网页资源的服务器
API接口服务器:专门对外提供API接口的服务器
1.2 Express的基本使用
1.2.1 安装
在项目所处的目录中,运行
npm i express@4.17.1
1.2.2 创建基本的Web服务器
//1.导入express
const express=require("express")
//2.创建web服务器
const app=express()
//3.启动服务器
app.listen(80,()=>{
console.log('http://127.0.0.1:80')
})
1.2.3 监听GET请求
//参数1 客户端请求的URL地址
//参数2 请求对应的处理函数
//req:请求对象(包含了与请求相关的属性和方法)
//res:响应对象(包含了与响应相关的属性和方法)
app.get('请求的url',(req,res)=>{
})
1.2.4 监听POST请求
//参数1 客户端请求的URL地址
//参数2 请求对应的处理函数
//req:请求对象(包含了与请求相关的属性和方法)
//res:响应对象(包含了与响应相关的属性和方法)
app.post('请求的url',(req,res)=>{
})
1.2.5 把内容响应给客户端
res.send()方法
app.get('/user',(req,res)=>{
//向客户端 发送JSON对象
res.send({name:'张三'})
})
app.post('/user',(req,res)=>{
//向客户端发送文本内容
res.send('请求成功')
})
1.2.6 获取URL中携带的查询参数
通过req.query对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数
app.get('/',(req,res)=>{
//req.query 默认是一个空对象
//客户端使用?name=zs&age=20 查询字符串,发送到服务器的参数
//req.query.name req.query.age
console.log(req.query)
})
1.2.7 获取URL中的动态参数
通过req.params对象,访问URL中通过 : 匹配到的 动态参数
//注意:这里的:id 是一个动态的参数
app.get('/user/:id/:name', (req, res) => {
//req.params 是动态匹配到的url参数 默认是一个空对象
console.log('req', req.params);
res.send(req.params)
})
1.3 托管静态资源
1.3.1 express.static()
非常方便的创建一个静态资源服务器
express在指定的目录中查找文件,并对外提供资源的访问路径
存放静态文件的目录名不会出现在URL中
//将public目录下的图片,css文件 js文件对外开放访问
app.use(express.static('public'))
1.3.2 托管多个静态资源目录
//http://127.0.0.1:3030/index.html 访问地址
app.use( express.static(path.join(__dirname, './file')))
//http://127.0.0.1:3030/index.html 访问地址
app.use( express.static(path.join(__dirname, './clock')))
1.3.3 挂载路径前缀
在托管的静态资源访问路径之前,挂载路径前缀
//http://127.0.0.1:3030/abc/index.html 访问地址
app.use('/abc', express.static(path.join(__dirname, './file')))
//http://127.0.0.1:3030/clock/index.html 访问地址
app.use('/clock', express.static(path.join(__dirname, './clock')))
1.4 nodemon
1.4.1为什么使用nodemon
1.4.2 安装nodemon
npm install nodemon -g
1.4.3 使用nodemon
nodemon index.js
二、Express路由
2.1 路由的概念
2.1.1 什么是路由
广义来讲,路由就是 映射关系
2.1.2 Express 中的路由
在 Express 中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
2.1.3 Express中的路由例子
//挂载路由
app.get('/', (req, res) => {
res.send('hello word')
})
app.post('/', (req, res) => {
res.send('Post Request')
})
2.1.5 路由的匹配过程
2.2 路由的使用
2.2.1 最简单的用法
在express中,把路由挂载到app上
const express = require("express");
const app = express();
//挂载路由
app.get('/', (req, res) => {
res.send('hello word')
})
app.post('/', (req, res) => {
res.send('Post Request')
})
app.listen(3001, (req, res) => {
console.log('http://127.0.0.1:3001');
})
2.2.2 模块化路由
2.2.3 创建路由模块
//这是路由模块
//1.导入express模块
const express = require("express");
//2.创建路由对象
const router = express.Router();
//3.挂载具体的路由
router.get('/user/list', (req, res) => {
res.send('get user list')
})
router.post('/user/add', (req, res) => {
res.send('Add new user')
})
//4.向外导出路由对象
module.exports = router
2.2.4 注册路由模块
const express = require("express");
const app = express();
//1.导入路由模块
const router = require("./05-router")
//2.注册路由模块
//'/api' 挂载访问前缀
app.use('/api', router)
//注意点
//app.use==>用来注册全局中间件
app.listen(3003, () => {
console.log('http://127.0.0.1:3003');
})
2.2.5 为路由模块添加前缀
//'/api' 挂载访问前缀
app.use('/api', router)
三、Express中间件
3.1 中间件的概念
3.1.1 什么是中间件
中间件(Middleware),特指业务流程的中间处理环节
3.1.2 现实生活中的例子
3.1.3 Express中间件的调用流程
当一个请求到达 Express 的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。
3.1.4 Express中间件的格式
Express 的中间件,本质上就是一个 function 处理函数
中间件函数的形参列表中必须包含next参数,而路由处理列表中只包含req 和 res
3.1.5 next函数的作用
next 函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。
3.2 Express中间件的初体验
3.2.1 定义中间件函数
//定义一个最简单的中间件函数
const mw = function (req, res, next) {
console.log('这是最简单的中间件函数');
//把流转关系 转交给下一个中间件或路由
next()
}
//将mw 注册为全局生效的中间件
app.use(mw)
//定义全局中间件的简化形式
app.use((req, res, next) => {
console.log('这是最简单的中间件函数');
next()
})
3.2.2 全局生效的中间件
客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。
通过调用 app.use(中间件函数),即可定义一个全局生效的中间件
//将mw 注册为全局生效的中间件
app.use(mw)
3.2.3 定义全局中间件的简化形式
//定义一个最简单的中间件函数
const mw = function (req, res, next) {
console.log('这是最简单的中间件函数');
//把流转关系 转交给下一个中间件或路由
next()
}
3.2.4 中间件的作用
3.2.5 定义多个全局中间件
可以使用 app.use() 连续定义多个全局中间件。客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用
app.use((req, res, next) => {
console.log('定义第一个全局中间件');
next()
})
app.use((req, res, next) => {
console.log('定义第二个全局中间件');
next()
})
3.2.6 局部生效的中间件
不使用 app.use() 定义的中间件,叫做局部生效的中间件
/定义中间件函数
const mw1 = function (req, res, next) {
console.log('调用了局部生效中间件');
next()
}
//局部生效的中间件
app.get('/', mw1, (req, res) => {
console.log('请求了/路由');
res.send('///')
})
app.get('/user', (req, res) => {
console.log('请求了/user路由');
res.send('/user')
})
3.2.7 定义多个局部中间件
app.get('/', mw1, mw2, (req, res) => {
console.log('请求了/路由');
res.send('///')
})
3.2.8 了解中间件的5个使用注意事项
3.3 中间件的分类
3.3.1 应用级别的中间件
通过 app.use() 或 app.get() 或 app.post() ,绑定到 app 实例上的中间件,叫做应用级别的中间件
3.3.2 路由级别的中间件
绑定到 express.Router() 实例上的中间件,叫做路由级别的中间件。
Router 实例是一个完整的中间件和路由系统
3.3.3 错误级别的中间件
作用 专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
格式 错误级别中间件的 function 处理函数中,必须有 4 个形参,形参顺序从前到后,分别是 (err, req, res, next)。
错误级别的中间件必须注册在所有路由之后
const express = require("express");
const app = express();
//定义路由
app.get('/', (req, res) => {
//1.1 人为制造错误
throw new Error('服务器内部发生了错误')
res.send('Home page')
})
//定义错误级别的中间件 捕获整个项目的异常错误 从而防止程序的崩溃
app.use((err, req, res, next) => {
console.log('发生了错误', err.message);
res.send('Error:' + err.message)
})
app.listen(3002, () => {
console.log('http://127.0.0.1:3002');
})
3.3.4 Express内置的中间件
express.static 快速托管静态资源的内置中间件,例如: HTML 文件、图片、CSS 样式等(无兼容性)
express.json 解析 JSON 格式的请求体数据(有兼容性,仅在 4.16.0+ 版本中可用)
express.urlencoded 解析 URL-encoded 格式的请求体数据(有兼容性,仅在 4.16.0+ 版本中可用)
const express = require("express");
const app = express();
//注意:除了错误级别的中间件 其他的中间件 都必须在路由之前进行配置
//通过 express.json()中间件 解析表单中的JSON格式的数据
app.use(express.json())
//定义路由
app.post('/', (req, res) => {
//在服务器,可以使用req.body这个属性 来接收客户端发送过来的请求体数据
//默认情况下 如果不配置解析表单数据的中间件时,则req.body默认等于undefined
console.log(req.body);
res.send('Home page')
})
//通过express.urlencoded()来解析表单中url-encoded格式的数据
app.use(express.urlencoded({ extended: false }))
app.post('/book', (req, res) => {
//在服务器,可以使用req.body这个属性 获取json格式表单的数据和URL-encoded 格式的数据
console.log(req.body);
res.send('ok')
})
app.listen(3002, () => {
console.log('http://127.0.0.1:3002');
})
postm 下的json格式
postmen 下的URL-encoded 格式
3.3.5 第三方的中间件
非 Express 官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件
3.4 自定义中间件
3.4.1 需求描述与实现步骤
3.4.2 定义中间件
3.4.3 监听req的data事件
3.4.4 监听req的end事件
3.4.5 使用querystring模块解析请求体数据
3.4.6 将解析出来的数据对象挂载为req.body
3.4.7 将自定义中间件封装为模块
自定义中间件js部分
//1.导入express模块
const express = require("express");
//一、导入自定义封装的中间件模块
const customBodyParser = require("./15-custom-body-parser.js")
//导入处理 querystring 模块
const qs = require("querystring")
//2.创建express服务器实例
const app = express();
//使用中间件 解析表单数据的中间件
//二、将自定义的中间件函数 注册为全局可用的中间件
app.use(customBodyParser)
//路由
app.post('/user', (req, res) => {
res.send(req.body)
})
app.get('/user', (req, res) => {
res.send('ok')
})
//3.启动服务器
app.listen(3033, () => {
console.log('启动成功');
})
模块部分
//1.导入express模块
const express = require("express");
//导入处理 querystring 模块
const qs = require("querystring")
//2.创建express服务器实例
const app = express();
const bodyParser = (req, res, next) => {
//定义中间件的业务逻辑
//1.定义一个str字符串 专门用来存储客户端发送过来的请求体数据
let str = ''
//2.监听req的data事件
req.on('data', (chunk) => {
str += chunk
})
//3.监听req对象的end事件(请求体发送完毕后自动触发)
req.on('end', () => {
//打印完整的请求体数据
console.log(str)
//把字符串格式的请求体数据,解析成对象格式
const body = qs.parse(str)
console.log('body', body);
//挂载为req的属性
req.body = body
//调用next函数
next()
})
}
//路由
app.post('/user', (req, res) => {
res.send(req.body)
})
app.get('/user', (req, res) => {
res.send('ok')
})
//3.启动服务器
app.listen(3030, () => {
console.log('启动成功');
})
module.exports = bodyParser
四、使用Express写接口
4.1 创建基本的服务器
如果要获取 URL-encoded 格式的请求体数据,必须配置中间件 app.use(express.urlencoded({ extended: false }))
//1.1导入express 模块
const express = require("express");
//1.2创建express服务器实例
const app = express();
//3.1 配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))
//2.1 导入路由模块
const router = require("./17-apiRouter.js")
//2.2 把路由模块注册到app 上 /api为前缀(访问路径)
app.use('/api', router)
//1.3启动服务器
app.listen(3001, () => {
console.log('http://127.0.0.1:3001');
})
4.2 创建API路由模块
//导入express模块
const express = require("express");
const router = express.Router();
//挂载对应的路由
//编写GET接口
router.get('/get', (req, res) => {
//1.获取到客户端通过查询字符串,发送到服务器的数据
const query = req.query
//2.调用res.send()方法 把数据响应给客户端
res.send({
status: 0, //0成功 1失败
msg: 'GET请求成功', //状态描述
data: query //需要响应给客户端的具体数据
})
})
//挂载post接口
router.post('/post', (req, res) => {
//首先通过req.body 获取请求体中包含的url-encoded格式的数据
const body = req.body
//调用res.send方法 向客户端响应结果
res.send({
status: 0,
msg: 'POST请求成功',
data: body
})
})
//暴漏出去 供外界使用
module.exports = router;
4.5 CORS跨域资源共享
4.5.1 接口的跨域问题
解决接口跨域问题的方案主要有两种:
① CORS(主流的解决方案,推荐使用)
② JSONP(有缺陷的解决方案:只支持 GET 请求)
4.5.2 使用cors中间件解决跨域问题
4.5.3 什么是CORS
4.5.4 CORS的注意事项
① CORS 主要在服务器端进行配置。客户端浏览器无须做任何额外的配置,即可请求开启了 CORS 的接口。
② CORS 在浏览器中有兼容性。只有支持 XMLHttpRequest Level2 的浏览器,才能正常访问开启了 CORS 的服 务端接口(例如:IE10+、Chrome4+、FireFox3.5+)。
4.5.5 CORS响应头部
4.5.6 CORS请求的分类
根据请求方式和请求头的不同,可以将 CORS 的请求分为简单请求 预检请求
4.5.7 简单请求
同时满足以下两大条件的请求,就属于简单请求:
① 请求方式:GET、POST、HEAD 三者之一
② HTTP 头部信息不超过以下几种字段:无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、 Downlink、Save-Data、Viewport-Width、Width 、Content-Type(只有三个值application/x-www-formurlencoded、multipart/form-data、text/plain)
4.5.8 预检请求
只要符合以下任何一个条件的请求,都需要进行预检请求:
① 请求方式为 GET、POST、HEAD 之外的请求 Method 类型
② 请求头中包含自定义头部字段
③ 向服务器发送了 application/json 格式的数据
4.5.9 简单请求和预检请求的区别
简单请求的特点:客户端与服务器之间只会发生一次请求。
预检请求的特点:客户端与服务器之间会发生两次请求,OPTION 预检请求成功之后,才会发起真正的请求
4.6 JSONP接口
4.6.1 回顾JSONP的概念和特点
概念 浏览器端通过 script 标签的 src 属性,请求服务器上的数据,同时,服务器返回一个函数的调用。这种请求数据的方式叫做 JSONP。
特点 只支持get请求,不属于真正的ajax请求,没有使用XMLHttpRequest
4.6.2 创建JSONP接口的注意事项
4.6.3 实现JSONP接口的步骤
4.6.4 具体代码
//5 必须在配置cors 中间件之前 配置JSONP的接口
app.get('/api/jsonp', (req, res) => {
//定义JSONP具体的实现过程
//5.1 获取客户端发送过来的回调函数的名字
const funcName = req.query.callback
//5.2 得到要通过JSONP形式发送到客户端的数据
const data = { name: 'zs', age: 23 }
//5.3 根据前两步得到的数据 拼接出一个函数调用的字符串
const Str = `${funcName}${JSON.stringify(data)}`
//5.4 把上一步拼接得到的字符串 响应给客户端的<script>标签进行解析执行
res.send(Str)
})
4.6.5 在网页中使用jquery发起JSONP请求
//为jsonp绑定点击事件处理函数
$('#JSONP').on('click', () => {
$.ajax({
method: 'GET',
url: 'http://127.0.0.1/api/jsonp',
dataType: 'jsonp',
success: function (res) {
console.log('res', res);
}
})
})