物联网服务NodeJs-5天学习第二天篇③ ——Express Web框架 和 中间件
Posted 单片机菜鸟哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了物联网服务NodeJs-5天学习第二天篇③ ——Express Web框架 和 中间件相关的知识,希望对你有一定的参考价值。
【NodeJs-5天学习】第二天篇③ ——Express Web框架 和 中间件
面向读者群体
- ❤️ 电子物联网专业同学,想针对硬件功能构造简单的服务器,不需要学习专业的服务器开发知识 ❤️
- ❤️ 业余爱好物联网开发者,有简单技术基础,想针对硬件功能构造简单的服务器❤️
- ❤️ 本篇创建记录 2023-03-12 ❤️
- ❤️ 本篇更新记录 2023-03-12 ❤️
技术要求
- 有HTML、CSS、JavaScript基础更好,当然也没事,就直接运行实例代码学习
专栏介绍
- 通过简短5天时间的渐进式学习NodeJs,可以了解到基本的服务开发概念,同时可以学习到npm、内置核心API(FS文件系统操作、HTTP服务器、Express框架等等),最终能够完成基本的物联网web开发,而且能够部署到公网访问。
🙏 此博客均由博主单独编写,不存在任何商业团队运营,如发现错误,请留言轰炸哦!及时修正!感谢支持!🎉 欢迎关注 🔎点赞 👍收藏 ⭐️留言📝
1. 前言
在前面一篇
我们讲解了HTTP服务器相关内容,但是你会发现我们需要关注非常多的细节(比如需要人工编码干预body的解析,需要分发请求方法等等),用起来有点复杂,开发效率低。那么有没有一些更加简单快捷的方式来创建web服务器?
当然有,这就是本篇要重点讲解的
Express
框架。那我们先看看它和HTTP模块的关系。
- 问题1:
不使用Express 能否创建 Web 服务器
?
能,使用 Node.js提供的原生http 模块即可。- 问题2:
有了http 内置模块,为什么还有用 Express
?
http内置模块用起来很复杂,开发效率低;Express是基于内置的http模块进一步封装出来的,能够极大的提高开发效率。- 问题3:
http 内置模块与Express是什么关系
?
后者是基于前者进一步封装出来的。
1.1 Express简介
官方给出的概念:Express
是基于 Node.js
平台,快速、开放、极简的Web 开发框架
。
通俗的理解:Express
的作用和 Node.js 内置的http
模块类似,是专门用来创建Web 服务器的。
Express 是npm上的一个第三方包,http模块是Node内置的模块,只不过Express基于http模块之上提供了更加简单快速创建web服务器的方法。
Express 中文官方网站:
https://www.expressjs.com.cn/
习惯性,我们都要点开一下官方说明看看:
- 官方定义
- 学习内容1:
快速入门
,如何快速搭建运行服务器,并且搭载静态文件资源(html、css等等)
- 学习内容2:
指南
,主要是学习中间件(MiddleWare
),包括了路由、错误、全局、局部等等
- 学习内容3:
API手册
,对各个api进行详细介绍,目前主要是4.x版本
- 学习内容4:
最佳实践
1.2 Express能做什么
对于大前端程序员来说,最常见的两种服务器,分别是:
Web 网站服务器
:专门对外提供Web 网页资源的服务器。
典型代表:我们经常使用浏览器看到的页面信息,基本上都是Web网页资源。
API 接口服务器
:专门对外提供API 接口的服务器。
典型代表:我们平常使用App看到的信息,基本上都是通过API接口返回给到app,app拿到数据之后进行渲染显示。
2. Express 快速入门
-
① 安装
express
、body-parser
、moment
模块 -
② 导入
express
、body-parser
模块 -
③ 创建 web 服务器
-
④ 注册
中间件
,处理业务逻辑 -
⑤ 调用
app.listen
(端口号, 启动成功后的回调函数) ,启动服务器
2.1 ① 安装 express
、body-parser
、moment
模块
分别执行:
npm install express --save
express主要是构建web服务器。
npm install body-parser --save
body-parser主要是用来解析post请求体,包括json
、urlencoded
表单
npm install moment --save
moment主要是用来处理时间
。
2.2 ② 导入 express
、body-parser
模块
创建一个 web 服务器,对外提供 web 服务,需要导入 express
模块:
// 1. 导入 express
const express = require('express')
const getIPAdress = require('./utils/utils.js')
const time = require('./utils/time.js')
const bodyParser = require('body-parser')
- express 用来构建web服务器
- bodyParser 用来解析post请求体,包括
json
、urlencoded
表单 - time 主要是使用特定格式显示时间
const moment = require('moment');
// 获取当前时间 2022-07-31
function getCurrentDate()
return moment().format("YYYY-MM-DD");
// 获取当前时间 2022-07-31 11:30:30
function getCurrentDateTime()
return moment().format("YYYY-MM-DD HH:MM:SS");
// 获取当前时间
function getCurrentDateFormat(format)
return moment().format(format);
// 向外导出方法
module.exports =
getCurrentDate,
getCurrentDateFormat,
getCurrentDateTime
- getIPAdress用来获取本机IP地址,后面浏览器用来访问服务
const os = require('os');
// 获取本机ip
function getIPAdress()
var interfaces = os.networkInterfaces();
for (var devName in interfaces)
var iface = interfaces[devName];
for (var i = 0; i < iface.length; i++)
var alias = iface[i];
if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal)
return alias.address;
// 向外导出方法
module.exports =
getIPAdress
2.3 ③ 创建 web 服务器
// 2. 创建 web 服务器
const app = express()
const port = 8266 // 端口号
const myHost = getIPAdress(); // 获取本机IP地址
这里定义了服务器的IP和端口号,后面监听客户端请求会用到。
2.4 ④ 注册中间件
,处理业务逻辑
// 3.注册中间件,处理业务逻辑
// 注意:中间件注入顺序,必须严格区分
// - 1、预处理中间件(排在最前面)
// - 2、路由中间件(中间位置,路由分为API路由和静态文件路由)
// - 3、错误处理中间件(兜底,专门用于捕获整个项目发生的异常错误,防止项目奔溃,必须注册在所有路由之后)
/*********************** 预处理中间件 *************************/
// 注入一些自定义中间件
// 定义一个最简单的中间件函数
// 常量 mw1 所指向的,就是一个中间件,这里打印请求进来的时间
const mw1 = function(req , res , next)
console.log('这是第一个中间件函数')
var date = time.getCurrentDateTime()
console.log('请求时间:%s', date)
// 注意:在当前中间件的业务处理完毕后, 必须调用next()函数
// 表示把流转关系转交给下一个中间件或路由
next()
app.use(mw1)
// 解析JSON格式的请求体数据 (post请求:application/json)
app.use(bodyParser.json());
// 解析 URL-encoded 格式的请求体数据(表单 application/x-www-form-urlencoded)
app.use(bodyParser.urlencoded( extended: true ));
/*********************** 预处理中间件 *************************/
/*********************** 路由中间件 *************************/
// 创建路由对象
const router = express.Router();
router.get('/api/test1', (req, res) =>
console.log("请求:GET /api/test1")
// 获取 URL 中携带的查询参数
console.log(req.query)
res.send("/api/test1 get OK")
)
router.post('/api/test1', (req, res) =>
console.log("请求:POST /api/test1")
// 获取 请求体 中携带的内容
console.log(req.body)
res.send("/api/test1 Post OK")
)
router.get('/api/test2', (req, res) =>
console.log("请求:GET /api/test2")
// 获取 URL 中携带的查询参数
console.log(req.query)
res.send("/api/test2 get OK")
)
router.post('/api/test2', (req, res) =>
console.log("请求:POST /api/test2")
// 获取 请求体 中携带的内容
console.log(req.body)
res.send("/api/test2 Post OK")
)
// 注入API路由中间件
app.use(router);
// app.use('/api', router) // 添加/api 访问前缀
// 注入静态路由中间件,快速托管静态资源的中间件,比如 HTML文件、图片、CSS等
app.use(express.static('web'))
// all可以匹配任何提交方式 兜底方案
app.all('*',(req,res)=>
// 做一个其它比较友好界面 响应给浏览器
console.log('页面还没完成,请等待...')
res.send('页面还没完成,请等待...')
)
/*********************** 路由中间件 *************************/
/*********************** 错误处理中间件 *************************/
app.use((err, req, res, next) =>
console.error('出现异常:' + err.message)
res.send('Error: 服务器异常,请耐心等待!')
)
/*********************** 错误处理中间件 *************************/
这里的中间件分了几类:
- 预处理中间件(排在最前面)
一般这种中间件主要是在处理业务逻辑之前对
req
或者res
做处理,比如打印请求到来的时间、解析post请求带过来的数据。
- 路由中间件(中间位置,路由分为API路由和静态文件路由)
一般我们会在这里
处理对应请求URL
,比如路由映射以及html链接访问等等
- 错误处理中间件(兜底,专门用于捕获整个项目发生的异常错误,防止项目奔溃,必须注册在所有路由之后)
一般这里就是错误兜底,当其他中间件出现错误问题时,会在这里捕获到。
注意:
中间件一定是从上到下依序执行
,它们之间通过next
方法进行流转,部分3会详细讲解中间件。
接下来介绍一下用到的中间件。
2.4.1 预处理中间件
第一个
执行的中间件是自定义的预处理中间件,主要是打印请求进来的时间
,然后调用next方法将操作权流转给下一个中间件
// 注入一些自定义中间件
// 定义一个最简单的中间件函数
// 常量 mw1 所指向的,就是一个中间件,这里打印请求进来的时间
const mw1 = function(req , res , next)
console.log('这是第一个中间件函数')
var date = time.getCurrentDateTime()
console.log('请求时间:%s', date)
// 注意:在当前中间件的业务处理完毕后, 必须调用next()函数
// 表示把流转关系转交给下一个中间件或路由
next()
app.use(mw1)
第二个
执行的中间件是第三方编写的预处理中间件,主要是解析JSON格式的请求体数据 (post请求:application/json
),内部会调用next方法将操作权流转给下一个中间件
// 解析JSON格式的请求体数据 (post请求:application/json)
app.use(bodyParser.json());
第三个
执行的中间件是第三方编写的预处理中间件,主要是解析 URL-encoded 格式的请求体数据(表单 application/x-www-form-urlencoded),内部会调用next方法将操作权流转给下一个中间件
// 解析 URL-encoded 格式的请求体数据(表单 application/x-www-form-urlencoded)
app.use(bodyParser.urlencoded( extended: true ));
2.4.2 路由中间件
在Express中,路由
指的是客户端的请求与服务器处理函数之间的映射关系
。
Express中的路由分3 部分组成,分别是请求的类型
、请求的URL
地址、处理函数
,格式如下:
app.METHOD(PATH , HANDLER)
// METHOD 请求的类型 可以是get / post
// PATH 请求的URL地址
// HANDOD 处理函数
https://www.expressjs.com.cn/guide/routing.html
第四个
执行的中间件是API路由中间件
,主要是解析各个请求方法以及对应URL,然后响应具体对应的操作处理,内部会调用next方法将操作权流转给下一个中间件
// 创建路由对象
const router = express.Router();
router.get('/api/test1', (req, res) =>
console.log("请求:GET /api/test1")
// 获取 URL 中携带的查询参数
console.log(req.query)
res.send("/api/test1 get OK")
)
router.post('/api/test1', (req, res) =>
console.log("请求:POST /api/test1")
// 获取 请求体 中携带的内容
console.log(req.body)
res.send("/api/test1 Post OK")
)
router.get('/api/test2', (req, res) =>
console.log("请求:GET /api/test2")
// 获取 URL 中携带的查询参数
console.log(req.query)
res.send("/api/test2 get OK")
)
router.post('/api/test2', (req, res) =>
console.log("请求:POST /api/test2")
// 获取 请求体 中携带的内容
console.log(req.body)
res.send("/api/test2 Post OK")
)
// 注入API路由中间件
app.use(router);
// app.use('/api', router) // 添加/api 访问前缀
第五个
执行的中间件是静态文件路由中间件
,主要是提供外界可以访问本地文件服务器(需要指定一个文件夹目录,可以用于存放web项目),一般都是用于html、css、js等等文件组成的web页面。内部会调用next方法将操作权流转给下一个中间件
// 注入静态路由中间件,快速托管静态资源的中间件,比如 HTML文件、图片、CSS等
app.use(express.static('web'))
- 假设上面两个路由中间件都没有命中,会继续执行第六个
API路由中间件
。这里我们作为兜底处理,所以无法匹配的请求方法和请求URL都执行这个,一般多是用于提供一个友好页面给到用户。
// all可以匹配任何提交方式 兜底方案
app.all('*',(req,res)=>
// 做一个其它比较友好界面 响应给浏览器
console.log('页面还没完成,请等待...')
res.send('页面还没完成,请等待...')
)
注意:
- 路由中间件只会命中其中一个,不会依序执行。
在路由中间件中,我们需要注意一些知识点。
2.4.2.1 监听GET 请求 app.get()
通过 app.get()
方法,可以监听客户端的 GET
请求,具体的语法格式如下:
// 参数1: 客户端请求的 URL 地址
// 参数2: 请求对应的处理函数
// req:请求对象(包含了与请求相关的属性与方法)
// res:响应对象(包含了与响应相关的属性与方法)
app.get('请求路径URL' , function(req , res) /*处理函数*/)
app.get('请求路径URL' , (req , res) => /*处理函数*/) // 利用箭头函数
2.4.2.2 监听 POST 请求 app.post()
通过 app.post()
方法,可以监听客户端的POST
请求,具体的语法格式如下:
// 参数1: 客户端请求的 URL 地址
// 参数2: 请求对应的处理函数
// req:请求对象(包含了与请求相关的属性与方法)
// res:响应对象(包含了与响应相关的属性与方法)
app.post('请求路径URL' , function(req , res) /*处理函数*/)
app.post('请求路径URL' , (req , res) => /*处理函数*/) // 利用箭头函数
2.4.2.3 监听 所有 请求 app.all()
通过 app.all() 方法,可以监听客户端的任意请求,具体的语法格式如下:
// 参数1: 客户端请求的 URL 地址
// 参数2: 请求对应的处理函数
// req:请求对象(包含了与请求相关的属性与方法)
// res:响应对象(包含了与响应相关的属性与方法)
app.post('*' , function(req , res) /*处理函数*/)
app.post('*' , (req , res) => /*处理函数*/) // 利用箭头函数
这里使用到了正则匹配表达式。
注意:
不管是GET、POST、PUT、DELETE还是all,它们的路径均可以使用正则匹配表达式。比如:
'/ab?cd'
会匹配到acd
和abcd
'/ab+cd'
会匹配到abcd
,abbcd
,abbbcd
'/ab*cd'
会匹配到abcd
,abxcd
,abRANDOMcd
,ab123cd
'/ab(cd)?e'
会匹配到/abe
and/abcde
.
2.4.2.4 把内容响应给客户端 res.send()
通过 res.send()
方法,可以把处理好的内容,发送给客户端:
app.get('/user' , (req , res) =>
// 调用express 提供的 res.send() 方法 , 向客户端响应(发送)一个 JSON 对象
res.send(name: 'zs' , age : 20 , gender: '男')
)
app.post('/user' , (req , res) =>
// 调用express 提供的 res.send() 方法 , 向客户端响应(发送)一个 文本字符串
res.send('请求成功')
)
2.4.2.5 获取 URL 中携带的查询参数 req.query
通过 req.query
对象,可以访问到客户端通过查询字符串
(queryString HTTP)的形式,发送到服务器的参数:
app.get('/' , (req, res) =>
// req.query 默认是一个空对象
// 客户端使用 ?name=zs&age=20 这种查询字符串形式 , 发送到服务器的参数,
// 可以通过req.query 对象访问到,例如:
// req.query.name req.query.age
console.log(req.query)
)
2.4.2.6 获取 URL 中的动态参数 req.params
通过 req.params
对象,可以访问到 URL 中,通过 : 匹配到的动态参数:
// URL 地址中,可以通过 :参数名 的形式 , 匹配动态参数值
// 注意 : 这里的 :id 是一个动态的参数
app.get('/user/:id' , (req , res) =>
// req.params 默认是一个空对象
// 里面存放着通过 : 动态匹配到的参数值
console.log(req.params)
)
具体案例:
// 动态参数可以是多个 例如:/user/:id/:name
//: 后面的值可以随便写(只要合理) 例如: /user/:ids
// 动态参数的个数要保持一致 ,不然会报错
http://127.0.0.1/user/1/zs/20 // 这时会报错
app.get('/user/:id/name' , (req , res) =>
console.log(req.params)
)
// 动态参数的顺序可以调换,对应的参数也会改变
// 第一次 http://127.0.0.1/user/1/zs
app.get('/user/:id/name' , (req , res) =>
console.log(req.params) // id:1 , name:zs
)
// 第二次 http://127.0.0.1/user/zs/1
app.get('/user/:id/name' , (req , res) =>
console.log(req.params) // id:zs , name:1
)
2.4.2.7 req.body、req.query、req.params区别
- req.query:
// 注册中间件
app.use(express.urlencoede(extended:false))
// 以?传递的参数都是get形式 接收的时候使用req.query
app.post('/login' ,(req , res) =>
console.log(req.query)
)
- req.body
app.use物联网服务NodeJs-5天学习第二天篇④ ——项目模块化
物联网服务NodeJs-5天学习第二天篇② —— 网络编程(TCPHTTPWeb应用服务)
物联网服务NodeJs-5天学习第三天实战篇③ ——基于MQTT的环境温度检测
物联网服务NodeJs-5天学习第四天存储篇③ ——基于物联网的WiFi自动打卡考勤系统,升级存储为mysql,提醒功能改为QQ
物联网服务NodeJs-5天学习第一天篇③ —— VsCode上运行第一个NodeJs 程序,配置自动重启插件 nodemon