Express
1. 初识Express
1.1 Expres简介
-
什么是 Express
- 概念:Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架。
- 本质:第三方包,提供了快速创建 Web 服务器的便捷方法。
-
Express 的作用
对于前端来说,最常见的两种服务器,分别是:- Web 网站服务器:专门对外提供web网专资源的服务器。
- API接口服务器:专门对外提供API接口的服务器。
使用 Express,可以方便、快速创建Web网站的服务器或API接口的服务器。
1.2 Express的基本使用
-
运行
npm i express@4.17.1
命令安装 express -
创建基本的Web服务器
// 1. 导入 express const express = require('express'); // 2. 创建 Web 服务器实例对象 const app = express(); // 3. 定义端口号 const port = 3000; // 4. 调用app.listen(端口号,启动成功后的回调函数) 方法,启动服务器 app.listen(port, () => { console.log('Express server running at http://127.0.0.1:3000'); })
-
监听GET请求语法格式
/* * @alias app.get(url, callback) * @param {string} 客户端请求的URL地址 * @param {callback} 响应客户端请求的处理函数 */ app.get('url', (req, res) => { // req: 请求对象(包含了与请求相关的属性与方法) // res: 相应对象(包含了与响应相关的属性与方法) // 处理函数体 })
-
监听POST请求语法格式
/* * @alias app.post(url, callback) * @param {string} 客户端请求的URL地址 * @param {callback} 响应客户端请求的处理函数 */ app.post('url', (req, res) => { // req: 请求对象(包含了与请求相关的属性与方法) // res: 相应对象(包含了与响应相关的属性与方法) // 处理函数体 })
-
获取URL中携带的查询参数
通过req.query
对象,可以访问到客户端通过查询字符串的形式发送到服务器的参数:app.get('/search', (req, res) => { // req.query 默认是一个空对象 // 当客户端的请求URL为:/search?q=taobao console.log(req.query); // 输出: { q: 'taobao' } console.log(req.query.q); // 输出: taobao })
-
获取表单中的数据
通过req.body
对象,可以获取表单提交过来的数据:// 如果需要使用 req.body , 需要在使用前使用解析表单的中间件(下面会详细说) app.use(express.urlencoded({ extended: true })); // 如果使用上述代码,req.body 的值为 undefined app.post('/reg', (req, res) => { console.log(req.body); })
-
获取URL中的动态参数
通过req.params
对象,可以访问到URL中通过:
匹配到的动态参数:// :id 表示 id 这个参数是动态参数 app.get('/user/:id', (req, res) => { // req.params 默认是一个空对象 // 客户端请求URL为: /user/34 console.log(req.params); // 输出: { id: '34' } })
-
响应客户端请求
通过res.send()
方法,可以把数据和信息发送给客户端。app.get('/', (req, res) => { // 向客户端发送 JSON 对象 res.send({ name: 'xao', age: 18, sex: '男', }); }) app.post('/user', (req, res) => { // 向客户端发送文本内容 res.send('POST请求成功'); })
1.3 托管静态资源
- 静态资源目录树结构
|--public |--|--index.html |--|--login.html |--|--register.html
-
express.static()
通过使用express.static()
方法,可以快速创建一个静态资源服务器。app.use(express.static('./public')); // 通过上述代码,客户端就可以访问public目录下的静态资源 // htpp://127.0.0.1:3000/index.html // htpp://127.0.0.1:3000/login.html // htpp://127.0.0.1:3000/register.html
-
托管多个静态资源目录
如果要托管多个静态资源目录,请多次调用express.static() 方法app.use(express.static('./public')); app.use(express.static('./files'));
客户端访问静态资源时,express.static() 函数会根据目录的添加顺序查找所需的文件。
-
挂载路径前缀
如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可以使用如下的方式:app.use('/public',express.static('./public')); // 添加上述代码后,客户端访问资源时必须要带有 /public 前缀地址来访问public目录中的静态资源 // htpp://127.0.0.1:3000/public/index.html // htpp://127.0.0.1:3000/public/login.html // htpp://127.0.0.1:3000/public/register.html
2. Express 路由
2.1 路由的概念
-
从广义上来讲,路由就是映射关系。
-
express 中的路由
- 在 express 中,路由指的是 客户端的请求 与 服务器处理函数 之间的映射关系。
- express 中的路由分 3 部分组成,分别是 请求的类型、 请求的 URL 地址、 处理函数 。
-
具体代码
// 匹配GET请求,且请求 URL 地址为 /index.html app.get('/index.html', (req, res) => { res.send('hello world !'); }) // 匹配POST请求,且请求 URL 地址为 /login.html app.post('/login.html', (req, res) => { res.send('登录成功'); })
-
express路由匹配过程
- 每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。
- 在匹配时,会按照路由定义的顺序进行匹配,如果请求类型和请求的 URL 同时匹配成功,则 Express 会将这次请求,转交给对应的 function 函数进行处理。
2.2 路由的使用
-
简单使用方法
将路由挂载到服务器实例对象上// 导入 express const express = require('express') // 创建 web 服务器实例对象 const app = express() // 定义端口号 const port = 3000 // 挂载路由 app.get('/', (req, res) => { res.send('GET request to the homepage') }) app.post('/', function (req, res) { res.send('POST request to the homepage') }) // 启动服务器 app.listen(port, () => { console.log(`Express server running at http://127.0.0.1:3000`) })
-
模块化路由
为了方便对路由进行模块化的管理,Express 不建议将路由直接挂载到 app 上,而是推荐将路由抽离为单独的模块。-
将路由抽离为单独模块的步骤如下:
- 创建路由模块对应js文件
- 调用 express.Router() 函数创建路由对象
- 定义路由的具体映射关系
- 使用 module.exports 向外共享路由对象
- 使用 app.use() 函数注册路由模块
-
代码如下
- 创建路由模块
// 导入 express const express = require('express'); //创建路由对象 const router = express.Router(); // 定义路由的映射关系 router.get('/', (req, res) => { res.send('GET request to the homepage') }) router.post('/', function (req, res) { res.send('POST request to the homepage') }) // 向外共享路由对象 module.exports = router;
- 注册路由模块
// 导入路由模块 const router = require('./router.js'); // 使用 app.use() 注册路由模块 app.use(router);
- 为路由模块添加路径前缀
// 使用 app.use() 注册路由模块,并统一添加访问路径前缀 app.use('/index', router);
- 创建路由模块
-
3. Express 中间件
3.1 中间件的概念
-
中间件(Middleware ),特指业务流程的 中间处理环节。
-
express 中间件的调用流程
- 当一个请求到达服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。
- 多个中间件之间共享 req 和 res 对象。
- 当处理完毕后,响应客户端的请求。
3.2 中间件的类型
-
应用层中间件
通过 app.use() 和 app.METHOD() 函数,绑定到 express() 实例对象上的中间件,叫做应用层中间件。const app = express(); // 每次收到请求,都会执行该中间件(全局中间件) app.use((req, res, next) => { console.log('hello express !'); next(); // next() 方法必须被调用,以便进入下一个中间件。否则,请求将被挂起。 // 调用 next() 方法之后,不要再书写任何代码。(一般最后调用 next() 方法) }) // 该中间件只会在客户端请求 /api 路径时,被执行 app.use('/api', (req, res, next) => { console.log('api'); next(); }) // 该中间件只会在客户端发起 GET 请求,且请求 /public 路径时被执行 app.get('/public', (req, res, next) => { console.log('public'); next(); })
-
路由级中间件
路由器级中间件与应用层中间件的工作方式相同,只不过它绑定到的实例 express.Router() 上。const userRouter = express.Router(); // 该中间件只会在客户端请求 userRouter 模块时被执行 userRouter.use((req, res, next) => { console.log('success'); next(); }) // 该中间件只会在客户端请求 userRouter 模块,且请求路径为 /userinfo 时被执行 userRouter.use('/userinfo', (req, res, next) => { console.log('userinfo'); next(); }) // 该中间件只会在客户端请求 userRouter 模块,发起 POST 请求,且路径为 /userinfo/update 时被执行 userRouter.post('/userinfo/update', (req, res, next) => { console.log('update userinfo'); next(); })
-
错误处理中间件
- 错误中间件必须带有4个参数,(err, req, res, next)
- 错误中间件无需调用 next() 方法,但参数必须传;否则,会被当做其他类型的中间件执行。
- 错误中间件必须在所有路由之后调用
app.use((err, req, res, next) => { if(req.url == '/api/info') { return res.status(404).send('404 页面不存在!'); } res.status(500).send('服务器内部错误'); })
-
内置中间件
express.static
快速托管静态资源的内置中间件,例如: HTML 文件、图片、CSS 样式等(无兼容性)。express.json
解析 JSON 格式的请求体数据(有兼容性,4.16.0+ 中可用)。express.urlencoded
解析 URL-encoded 格式的请求体数据(有兼容性,4.16.0+ 中可用)。
-
第三方中间件(与包类似)
- 非express官方内置的中间件,而是有第三方开发出来的express中间件,叫做第三方中间件。
- 使用第三方中间件时,需要使用
npm i 名称
下载第三方中间件,使用require()
导入,并调用app.use()
挂载第三方中间件。
3.3 自定义中间件
// 新建一个js文件
// 自定义模块可以使用 node.js 内置模块 或 第三方模块
// 定义中间件
const myMiddle = (req, res, next) => {
// 要执行的代码...
next();
}
// 向外共享myMiddle
module.exports = myMiddle;
4. 使用过 express 搭建API接口服务器
4.1 使用过 express 搭建API接口
-
搭建基本服务器
// 导入 express const express = require('express'); // 创建服务器实例对象 const app = express(); // 定义端口号 const port = 3000; // 导入路由模块 const apiRouter = require('./apiRouter'); // 将路由模块注册到服务器上 app.use('/api', apiRouter); app.listen(port, () => console.log(`Example server running at http://127.0.0.1:3000`));
-
apiRouter 模块
// 导入 express const express = require('express'); // 创建一个新的路由对象 const router = express.Router(); // 实现路由的映射 router.get('/get', (req, res) => { const query = req.query; res.send({ status: 200, // 响应状态码:200 msg: 'GET请求成功', // 响应状态描述 data: query, // 响应给客户端的数据 }); }); router.post('/post', (req, res) => { const query = req.query; res.send({ status: 200, // 响应状态码:200 msg: 'POST请求成功', // 响应状态描述 data: query, // 响应给客户端的数据 }) }) // 向外共享路由模块 module.exports = router;
4.2 跨域资源共享
-
CORS(Cross-Origin Resource Sharing)跨域资源共享
- 同源:客户端请求的URL地址与服务器的ULR地址的协议、域名、端口号都相同即为同源,三者有一者不同为跨域。
- 由于浏览器同源策略的存在,默认阻止跨域获取资源;但服务器可以通过设置响应头,允许跨域请求访问资源。
- 但CORS 在浏览器中有兼容性,只支持 XMLHttpRequest Level2 的浏览器,才能正常开启 CORS (例如:IE10+、Chrome4+、FireFox3.5+)。
-
CORS 跨域资源实现方式
通过指定响应头Access-Control-Allow-Origin
字段对应的值来指定允许访问资源的外域 URL。// 只允许来自 http://127.0.0.1:5500 的跨域访问资源请求 res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500'); // 值为 '*' : 表示允许来自任何域的请求 res.setHeader('Access-Control-Allow-Origin', '*');
-
CORS 默认支持9个请求头
Accept Accept-Language Content-Language DPR Downlink Save-Data Viewport-Width Width Content-Type (值仅限于 text/plain、multipart/form-data、application/x-www-form-urlencoded 三者之一)
如果需要支持其他的类型的请求头,可以通过
Access-Control-Allow-Headers
字段对额外的请求进行声明。// 允许客户端额外向服务器发送 X-Custom-Header 请求头 res.setHeader('Access-Control-Allow-Headers', 'X-Custom-Header'); // 可以同时设置多个请求头,多个请求头之间用英文逗号分隔 res.setHeader('Access-Control-Allow-Headers','Content-Type,X-Custom-Header');
-
CORS 默认情况下仅支持客户端发起的简单请求(GET、POST、HEAD)。
如果服务器想要允许客户端通过非简单请求(预检请求)来请求服务端的资源,可以通过Access-Control-Alow-Methods
字段来设置客户端可以使用的请求方式。// 允许客户端使用 GET, POST, HEAD, DELETE 方式请求资源 res.setHeader('Access-Control-Alow-Methods', 'GET, POST, HEAD, DELETE'); // 允许客户端使用所有的HTTP请求方式 res.setHeader('Access-Control-Alow-Methods', '*');
-
简单请求
- 请求方式为 GET、POST、HEAD 三者之一。
- 客户端请求头部无自定义头部字段。
- 客户端只会向服务端发送一次请求。
-
预检请求
- 请求方式为 GET、POST、HEAD 之外的请求类型。
- 请求头部包含自定义头部字段。
- 向服务器发送
application/json
格式的数据。 - 客户端会向服务器发送两次请求。
- 在浏览器与服务器正式通信之前,浏览器会先发送 OPTION 请求进行预检,以获知服务器是否允许该实际请求。
- 服务器成功响应OPTION请求(预检请求)后,才会发送真正的请求,并且携带真实数据。
4.3 跨域解决方案
- CORS(主流方案,只需要在后端配置,推荐使用)
- JSONP(兼容性好,需要前后端配合,但只支持GET请求)
4.4 使用 cors 中间件解决跨域问题
cors 是 express 的一个第三方中间件,通过安装和配置中间件,可以方便解决跨域问题。
- 安装:
npm i cors
- 导入:
const cors = require('cors')
- 全局配置:
app.use(cors())
4.5 使用 jsonp 解决跨域问题
- 服务器端代码
app.get('/api/jsonp', (req, res) => { // 1. 得到函数的名称 const funName = req.query.callback // 2. 定义要发送到客户端的数据对象 const data = { name: 'zs', age: 22 } // 3. 拼接出一个函数的调用 const scriptStr = `${funName}(${JSON.stringify(data)})` // 4. 把拼接的字符串,响应给客户端 res.send(scriptStr) })
- 客户端代码
$.ajax({ type: 'GET', url: 'http://127.0.0.1/api/jsonp', dataType: 'jsonp', success: function (res) { console.log(res); } })
PS: 如果本文对你有所帮助,请点个赞吧!