Nodejs 第二十九章 express基础使用
什么是 Express?
Express 是一个灵活的 Node.js Web 应用框架,提供了一套丰富的功能用于构建单页、多页以及混合网页应用。它被设计为使服务器端开发变得快捷而简单,是最流行的 Node.js 框架之一
,形成了庞大的生态系统。
为什么需要 Express?
- 简化路由和中间件的实现 :Node.js 的原生 HTTP 模块功能强大但不够直观,Express 提供了一个简洁的方式来处理路由,并
通过中间件来处理 HTTP 请求和响应
。这样开发者就可以方便地定义路由的处理逻辑,对于不同的URL和HTTP方法执行相应的代码。 - 提高开发效率:Express 提供了基础的Web开发框架结构,能够快速的起一个项目,避免从零开始编写重复的设置和配置代码。它还支持多种模板引擎,简化视图的生成过程。
- 便于集成各种中间件:Express 的设计允许开发者通过中间件来扩展其功能。例如,可以轻松添加体积较小的组件来处理静态文件、会话、用户认证、安全头部等。
- 强大的社区支持和资源:由于其广泛的使用,Express 拥有一个庞大的社区,提供大量的资源、文档、教程和第三方库,这对于解决开发中的问题非常有帮助。
Express为当前环境做出的改变
- 设置Web开发标准:Express 设定了Node.js Web开发的实践标准,许多其他框架和库也建立在Express的基础之上或受其启发。
- 促进了Node.js的普及:Express 作为 Node.js 的一部分,极大地推动了 Node.js 的普及。它的简洁和高效使得 Node.js 成为一个强大的解决方案,适合构建从小型项目到大型企业级应用。
- 微服务架构的支持:Express 的轻量级和模块化特性使其成为构建微服务架构应用的理想选择。开发者可以构建独立的服务,每个服务都使用 Express 快速开发,然后通过 RESTful API 或其他方式集成。
- 推动了全栈JavaScript的发展:Express 的普及加速了JavaScript全栈开发的趋势,即前后端都使用JavaScript语言。这种模式对于全栈开发者来说降低了学习曲线,项目管理变得更加高效。
初始化项目
-
在开始正式使用之前,我们需要
创建一点文件
,我会说明这些文件以及文件夹的作用的- middleware文件夹:用来写中间件的
- src文件夹:我们的路由模块源码,我们的主要编写代码就在这里,比如说用户接口的模块,用户登录模块,我们就拆成一个个模块化,结构更清晰
- app.js文件:主文件,也相当于入口文件。可以简单的理解为像vite或者webpack起一个项目中的index.html
- express.http文件:我们等下就在这里发送请求
- 解决好了这些之后,我们就可以开始准备使用
express
了,首先,我们先安装一下~
css
npm i express
- 然后我们进行简单的使用一下
javascript
import express from "express"
//由于express是一个函数,所以我们需要先进行调用一下再进行使用
const app = express()
//参数1:路由路径
//参数2:回调函数,处理客户端发送过来的请求(拿到发送的内容以及要返回的内容)
app.get('/get',(req,res)=>{
res.send('这是get请求')
})
//跟get方法一样的用法
app.post('/post',(req,res)=>{
res.send('这是post请求')
})
app.listen(3000,()=>{
console.log("3000端口成功启动");
})
- 通过测试模拟发送客户端请求,我们的两个demo案例接口也是成功的返回了内容,完成了最小案例闭环
动态参数定义
动态参数在路由定义中提供了显著的灵活性和扩展性。它们允许一个路由模式处理多种请求,极大减少了代码重复性,并简化了路由管理。这种方法能有效地降低路由数量,使得应用更易维护,同时提高开发效率。此外,动态参数支持 RESTful API 设计,使得每个URL可以精确地代表一个资源实例,增强了API的直观性和易用性。
- 如果你暂时不理解这句话什么意思的话,我们就从实践中出发来看看
javascript
// 动态参数
app.post('/user/:id', (req, res) => {
res.send('用户页面-动态参数')
})
-
动态参数在我们原有定义路由的基础上往后添加内容时,带上
冒号:
,而附在冒号后面的内容将不再固定(可以随意取名,因为此时起到的占位符的作用)- 通过下图,我们请求的是
/user/1
,这个内容不管是1还是2亦或者是其他内容都是能够触发我们/user
开头的接口的 - 这样的写法很容易进行复用,我们将基于user功能的其他内容继续封装起来,就可以少写很多的内容。这个思想其实是很类似于Class类的继承。是编程思想的一种体现
- 通过下图,我们请求的是
接收请求内容
-
在之前使用http模块的时候,我们直接
req.xxx
就能够拿到了内容。而在express中,也是这种的形式,但也有一点区别get请求
:req.querypost请求
:req.body动态参数
:req.params
-
也是非常的通俗易懂啊,那就让我们基于以上基础的代码来进行验证一下吧
javascript
app.get('/get', (req, res) => {
console.log(req.query);
res.send('这是get请求')
})
app.post('/post', (req, res) => {
console.log(req.body);
res.send('这是post请求')
})
// 动态参数
app.post('/user/:id', (req, res) => {
console.log(req.params);
res.send('用户页面-动态参数')
})
-
但这里需要注意一下POST请求有点不一样,当我们在进行POST请求,想要拿到JSON数据的时候,我们必须使用express的一个中间件来解析JSON数据。否则拿到的就是一个
undefined未定义
-
中间件是什么?
中间件我认为就是使用过程安装的插件,我们能够选择的插件是多种多样的。如果从一开始就直接全部默认加载上了,那会非常的臃肿,我们为了轻量化,在有用到的时候在进行挂载,利用效率就很高
记得把这个
app.use()
中间件放到前面,因为代码按顺序加载的,如果使用在前、挂载在后,那就是马后炮的作用
-
less
app.use(express.json())
模块拆分
- express的基础使用我们已经熟悉了,但在正常的开发中,我们是不可能这么写的,因为要编写的接口很多。如果所有的接口都挤在app.js这个文件的话,就算有添加详细的注释,也很难维护,没办法清晰的明白我们项目的结构
- 所以,我们现在要将一个个接口给拆分出去了,根据不同的功能进行分组
- 此时要用到的就是express中的Router模块了,我使用了两个案例接口来演示。一个是登录注册接口,一个是图片接口。现在让我们来试试吧!
javascript
//user.js文件(登录注册接口)
import express from "express"
const Router = express.Router()//路由模块
Router.post('/login', (req, res) => {
//我们返回的是json格式的数据,express路由中有提供对应的方法
res.json({
code:200,
msg:'登录成功'
})
})
Router.post('/register', (req, res) => {
res.json({
code:200,
msg:'注册成功'
})
})
export default Router
javascript
//picture.js文件(图片接口)
import express from "express"
const Router = express.Router()
Router.get('/jpg',(req,res)=>{
res.json({
code:200,
msg:'返回jpg图片列表',
data:[{
'落叶':'https://cdn.pixabay.com/photo/2024/03/04/16/44/barberry-8612696_1280.jpg',
'狗':'https://cdn.pixabay.com/photo/2024/01/07/11/17/welsh-corgi-8492879_1280.jpg',
'猫咪':'https://cdn.pixabay.com/photo/2023/12/08/23/46/cat-8438334_1280.jpg'
}]
})
})
Router.get('/png',(req,res)=>{
res.json({
code:200,
msg:'返回png图片列表',
data:[{
'代码':'http://tuchuang.xiaoyu2002.cn/picture/image-20240427191716914.png',
'二次元美少女':'https://p6.itc.cn/q_70/images01/20230426/aebe488ff7e14e538c464994efb35486.png'
}]
})
})
export default Router
-
然后我们在app.js文件中进行导入
- 通过这里,我们能够看到内容还是非常清爽的,一下子就简洁了很多
javascript
import express from "express"
import picture from "./src/picture.js"
import user from "./src/user.js"
const app = express()
app.use(express.json())
// 路由接口,前面加上前缀就是为了防止我们路由接口的重名问题,目录也更清晰
app.use('/picture',picture)
app.use('/user',user)
app.listen(3000, () => {
console.log("3000端口成功启动");
})
Picture接口返回内容测试(GET)
user接口返回内容测试(POST)
- 通过上面模块化的拆分测试,也是都明显成功了,每个文件里面都有两个接口,都能成功进行请求并返回正确内容。这是express的一个核心的功能点
中间件的编写
中间件是一个关键概念。中间件是处理HTTP请求和响应的函数,它位于请求和最终路由处理函数之间,可以对请求和响应进行修改、执行额外的逻辑或者执行其他任务。
中间件函数接收三个参数:
req
(请求对象)、res
(响应对象)和next
(下一个中间件函数)。通过调用next()
方法,中间件可以将控制权传递给下一个中间件函数。如果中间件不调用next()
方法,请求将被中止,不会继续传递给下一个中间件或路由处理函数
-
除了
Router
这个核心的模块化拆分功能之外,中间件
也是Express同等重要的核心功能- 在前面中,我们就有使用过来自express的json中间件。那现在就来让我们自己探究一下内部的奥秘吧!
- 还记得我们一开始创建的middleware(英译:中间件) 文件夹吗?我们在这里进行编写一个简单的中间件来走通流程
-
此时我们会用到
log4js
这个第三方库,所以记得npm安装一下安装命令:
npm i log4js
- 按照我们的习惯,我们在进行使用一个第三方库的时候,也要稍微了解一下这个库是用来做什么的
log4js日志第三方库
Log4js 是一个流行的 Node.js 日志库,它是基于 Java 的 Log4j 库的思想设计的。这个库提供了灵活的日志记录解决方案,适用于 Node.js 应用程序,支持多种日志记录方式和配置选项,让开发者能够有效地控制日志的格式、目的地和级别。
核心特性
-
多种日志级别:
- Log4js 支持标准的日志级别,如
TRACE
,DEBUG
,INFO
,WARN
,ERROR
, 和FATAL
。这样可以根据信息的重要程度来区分日志消息。
- Log4js 支持标准的日志级别,如
-
多种输出方式:
- 支持多种输出目的地(称为 appenders),如控制台、文件、远程HTTP服务器等。你可以配置多个输出目的地,根据不同的日志级别或者格式发送到不同的存储介质。
-
灵活的配置选项:
- Log4js 的配置非常灵活,可以通过 JSON 或 JavaScript 对象进行配置。这包括定义不同的日志级别,设置日志的格式,以及指定哪些记录器(logger)应该使用哪些输出器(appender)。
-
日志分类:
- 支持创建多个不同的记录器实例,每个记录器可以有其特定的配置。这对于大型应用来说非常有用,可以为不同的系统组件或模块创建专门的日志记录器。
-
日志滚动:
- 支持文件日志滚动,这意味着可以配置日志文件在达到特定大小或者时间后自动滚动(归档),避免单个日志文件过大。
应用场景
- 开发调试:使用较低级别的日志(如DEBUG)帮助开发者了解应用的运行流程。
- 错误跟踪:在生产环境中使用ERROR或FATAL级别记录关键错误,便于迅速定位问题。
- 性能监控:记录关键操作的处理时间,帮助分析和优化性能瓶颈。
- 用户行为跟踪:记录用户的关键操作,用于分析用户行为模式或进行审计。
- 接着具体如何使用,我们就在接下来的这个中间件案例进行尝试吧!
初始化中间件
-
中间件
是有他的使用方式的,也就是req,res,next三件套,组成了最基础的模板,通过文字可以不够直观,看通过代码我们一定能够一览无遗- 很明显中间件 是一个函数,我们在函数中调用
next()
来决定是否把控制权递交到下一个中间件的手里,并且可以传递一个错误参数给它,如果传递错误参数,则 Express 会跳过后续的所有非错误处理中间件,直接移交给错误处理中间件,也就是next(err)
- 很明显中间件 是一个函数,我们在函数中调用
javascript
import log4js from "log4js"
// req:接收前端发送过来的信息
// res:返回给前端的内容
// next:决定是否执行下一个中间件,如果不写就一直卡在这里
const loggerMiddleware = (req,res,next)=>{
// 中间件内容
next()
}
export default loggerMiddleware
-
接着我们通过log4js来实现日志效果
- 在 log4js 和其他许多日志系统中,appender 是一个非常重要的概念。Appender 是日志处理流程中的一个组件,它负责控制日志数据的输出目的地和输出方式。换句话说,appender 定义了日志消息如何被记录和存储。每个 appender 可以指定不同的输出目标,例如文件、数据库、控制台或者远程服务器等
- 我们在这里一共要实现两个功能,一个是控制台打印日志,另外一个则是写入日志文件。都是非常常见的功能。我们这两个功能都在
appenders
选项中进行配置,然后在categories
选项中放入defalut配置
中,这也是我们后面调用getLogger方法的由来,把我们配置两个功能设为默认功能,然后加载默认功能,这是一个不断集成的过程
go
// 配置 log4js 日志库
log4js.configure({
appenders: { // 定义日志输出的目的地,即日志的存储方式
out: { // 定义一个名为 'out' 的 appender
type: 'stdout', // 指定输出类型为标准输出(控制台)
layout: { // 定义日志输出的布局格式
type: 'colored' // 使用带颜色的布局,使控制台输出带有颜色区分,便于阅读
}
},
file: { // 定义一个名为 'file' 的 appender
type: 'file', // 指定输出类型为文件
filename: './logs/server.log', // 设置日志文件的存储路径和文件名
}
},
categories: { // 定义日志的分类
default: { // 配置默认类别的日志属性
appenders: ['out', 'file'], // 此类别的日志将使用名为 'out' 和 'file' 的 appender 输出
level: 'debug' // 设置此类别的日志记录级别为 debug(记录所有debug及以上级别的日志)
}
}
});
// 获取 logger
//在 log4js 中,getLogger 方法用于创建或获取一个日志记录器(logger)对象。这个对象提供了接口来记录日志消息,可以指定不同的日志级别,如 debug, info, warn, error, 等
const logger = log4js.getLogger('default');
- 通过这些配置然后放入我们的中间件就能进行使用了
javascript
// 日志中间件
const loggerMiddleware = (req, res, next) => {
logger.debug(`${req.method} ${req.url}`); // 记录请求方法和URL
next();
};
- 最终在app.js文件中进行挂载,让我们来看看效果
javascript
// 导入中间件
import loggerMiddleware from './middleware/logger.js'
// 使用我们自己写的中间件
app.use(loggerMiddleware)
- 能够看到,不仅在控制台成功输出相关的内容了,而且创建了日志文件并写入相关的内容
完整中间件代码
javascript
import log4js from "log4js"
// 配置 log4js 日志库
log4js.configure({
appenders: { // 定义日志输出的目的地,即日志的存储方式
out: { // 定义一个名为 'out' 的 appender
type: 'stdout', // 指定输出类型为标准输出(控制台)
layout: { // 定义日志输出的布局格式
type: 'colored' // 使用带颜色的布局,使控制台输出带有颜色区分,便于阅读
}
},
file: { // 定义一个名为 'file' 的 appender
type: 'file', // 指定输出类型为文件
filename: './logs/server.log', // 设置日志文件的存储路径和文件名
}
},
categories: { // 定义日志的分类
default: { // 配置默认类别的日志属性
appenders: ['out', 'file'], // 此类别的日志将使用名为 'out' 和 'file' 的 appender 输出
level: 'debug' // 设置此类别的日志记录级别为 debug(记录所有debug及以上级别的日志)
}
}
});
// 获取 logger
//在 log4js 中,getLogger 方法用于创建或获取一个日志记录器(logger)对象。这个对象提供了接口来记录日志消息,可以指定不同的日志级别,如 debug, info, warn, error, 等
const logger = log4js.getLogger('default');
// req:接收前端发送过来的信息
// res:返回给前端的内容
// next:决定是否执行下一个中间件,如果不写就一直卡在这里
const loggerMiddleware = (req,res,next)=>{
logger.debug(`${req.method} ${req.url}`); // 记录请求方法和URL
next()
}
export default loggerMiddleware
- 以及完整的测试发送请求代码
shell
# get请求
# GET http://localhost:3000/get?a=1&b=2 HTTP/1.1
# post请求
# POST http://localhost:3000/post HTTP/1.1
# Content-Type: application/json
# post请求(动态参数)
# POST http://localhost:3000/user/1 HTTP/1.1
# Content-Type: application/json
# post请求 带JSON数据
# POST http://localhost:3000/post HTTP/1.1
# Content-Type: application/json
# {
# "name":"小余"
# }
# picture接口请求
GET http://localhost:3000/picture/jpg HTTP/1.1
# GET http://localhost:3000/picture/png HTTP/1.1
# user接口请求
# POST http://localhost:3000/user/login HTTP/1.1
# POST http://localhost:3000/user/register HTTP/1.1
- 通过动手实践,我们亲自完成了express的基础使用、动态定义参数、模块化的使用路由以及完整的实现了一遍日志中间件的编写,我相信在学习的路上,我们会坚持到最后,看到山顶的风光