写在前面
大家好,我是一溪风月⛽,一名前端工程师,在Node基础系列的上篇文章中,我们学习Web服务器的基本知识,我们学习和使用了http服务来搭建了一个Web服务器,但是在实际的开发中我们并不会这样做,我们一般都是基于框架去开发Web服务,Express就是其中比较受欢迎的一个,这篇文章我们将学习一个Express的基本用法,让你能够基于这个框架来搭建和开发自己的服务接口,好了,让我们开始吧!
一.认识Web框架
在前面的文章中我们已经了解了Web服务器的基础知识,那么为什么还要学习框架哪?原因有这些
- 原生http在进行很多处理的时候,会较为复杂。
- 有URL判断,Method判断,参数处理,逻辑代码处理等,这些都需要我们自己来处理。
- 并且所有的放在一起,会非常的混乱。
目前在Node中比较流行的轻量级框架有express框架和koa框架,express整个框架的核心就是中间件,了解了中间件一切都会变得非常的简单。
二.Express安装
Express框架的安装和使用有两种方式:
- 方式一:通过express提供的脚手架,直接创建一个应用的骨架。
js
// 安装脚手架
npm install -g express-generator
// 创建项目
express express-demo
// 安装依赖
npm install
// 启动项目
node app.js
- 方式二:从零自己来搭建自己express的应用结构。
js
// 初始化npm搭建项目
npm init -y
// 安装express
npm install express
// 在文件中导入express进行使用
const express = require("express");
const app = express();
三.Express的基本使用
我们创建第一个express项目之后,我们会发现,我们可以很方便的将我们的请求进行分离,无论是不同的URL,还是get,post等请求方式,这样的方式非常方便我们进行维护和扩展,比如路径中有这些参数/user/:userId
在request对象中我们就可以通过这种方式获取req.params.userId
来获取。
js
const express = require("express")
// 创建express服务器
const app = express()
// home的get请求
app.get("/home", (req, res) => {
res.end("返回首页轮播图数据!")
})
// login的post请求
app.post("/login", (req, res) => {
res.end("登录成功,欢迎回来!")
})
// 启动express服务器
app.listen(8000, () => {
console.log("服务启动🚀在了8000端口!")
})
// nodemon server.js
返回数据的时候我们可以非常方便的使用json
还有其他很多方式,我们在文章的后面会详细讲解。
四.认识中间件
Express是一个路由和中间件的Web框架,它本身的功能非常少,Express应用程序的本质是一系列中间件函数的调用,那么什么是中间件哪?
中间件的本质就是传递给Express的一个回调函数,这个回调函数接收三个参数
- 请求对象(request对象)
- 响应对象(response对象)
- next函数(在express中定义的用于执行下一个中间件的函数)
中间件可以执行哪些任务哪?
- 执行任何代码
- 更改请求(request)和响应对象(response)对象
- 结束请求-响应周期(返回数据)
- 调用栈中的下一个中间件
如果当前中间件功能没有结束请求-响应周期,则必须调用next()将控制权传递给下一个中间件功能,否则,请求将被挂起。

那么如何将中间件应用到我们的应用程序中哪?Express主要提供了两种方式
- app/router.use
- app/router.methods
可以是app也可以是router
我们会在文章的后边进行讲解,methods指的是app.get
和app.post
等等,我们首先来使用下最普通的中间件use的方式来写几个案例。
案例一: 最普通的中间件
js
// 通过use注册的中间件是最普通的中间件,无论通过什么方式都会匹配上。
app.use((req, res, next) => {
console.log("middleware!!!")
})
// middleware!!!
💡Tips:当express接收到了客户端发送的请求的时候,所有中间件都会开始匹配,当其中的一个中间件符合中间件的是时候那么就匹配这个中间件,当有多个中间件符合的时候会首先匹配第一个,后续中间件是否执行取决于这个首先匹配到的中间件是否执行了next。
案例二:path匹配中间件
js
app.use("/home", (req, res, next) => {
res.json({
"address": "南京东路"
})
})
// localhost:8000/home
案例三:path和methods匹配中间件
js
app.get("/home", (req, res, next) => {
res.json({
"address": "南京东路"
})
})
// get localhost:8000/home
案例四:匹配多个中间件
js
app.get("/home", (req, res, next) => {
console.log("001")
next()
}, (req, res, next) => {
console.log("002")
}, (req, res, next) => {
console.log("003")
}, (req, res, next) => {
console.log("004")
})
// 001
// 002
五.应用中间件body解析
在客户端发送POST请求的时候,会将数据放在body中,客户端可以通过JSON的方式进行传递,也可以通过form表单的方式进行传递。

在开发中我们会使用express内置中间件或者使用body-parse来进行解析。
js
const express = require("express")
const app = express()
// 使用中间件解析json
app.use(express.json())
app.post("/login", (req, res, next) => {
console.log(req.body)
})
app.listen(8000, () => {
console.log("服务启动在8000端口~")
})
// { username: 'zzz', password: '12345' }
在开发中除了会有解析json的需求之外还会有解析application/x-www-form-urlencoded
我们使用如下方式:
js
const express = require("express")
const app = express()
// 使用中间件解析urlencoded
app.use(express.urlencoded({ extended: true }))
app.post("/login", (req, res, next) => {
console.log(req.body)
})
app.listen(8000, () => {
console.log("服务启动在8000端口~")
})
六.应用中间件-第三方中间件
在服务端的开发中我们如果想要去排查问题,就需要查看服务端的日志,如果我们想要将请求的日志记录下来,那么我们可以使用Express官方开发的第三方库:morgan
js
npm install morgan
然后我们可以使用以下morgan,代码如下:
js
const express = require("express")
const morgan = require("morgan")
const fs = require("fs")
// 应用第三方中间件
const app = express()
const writeStream = fs.createWriteStream("./logs/access.log")
app.use(morgan('combined', { stream: writeStream }))
app.post('/login', (req, res) => {
res.end("登录成功,欢迎回来!")
})
app.listen(9000, () => {
console.log("服务启动在9000端口!")
})
然后我们在代码中新建一个名为logs
文件夹,然后新建一个access.log
通过apifox进行请求,然后我们会发现在我们新建的文件中增加了一条信息。

除了使用morgan进行日志的记录,我们还可以使用multer来进行文件的上传。
js
npm install multer
然后我们编写一下文件上传单个文件的逻辑
js
const express = require("express")
const multer = require("multer")
const app = express()
const upload = multer({
dest: './uploads'
})
//编写中间件
app.post('/avater', upload.single('avatar'), (req, res, next) => {
console.log(req.file)
res.end("文件上传成功!")
})
app.listen(9000, () => {
console.log("服务运行在9000端口")
})
然后我们通过apifox进行请求,我们就会发现文件被上传了,但是在window中我们是打不开的,因为我们并没有指定后缀名,但是其实这个文件没有任何问题的。

为了解决这种问题,我们可以自定义文件名,multer其实已经支持了这个功能
js
const upload = multer({
storage: multer.diskStorage({
destination (req, file, callback) {
callback(null, './uploads')
},
filename (req, file, callback) {
callback(null, Date.now() + '_' + file.originalname)
}
})
})
除了上传单个文件之外,我们使用multer还可以进行多文件的上传
js
app.post("/photos", upload.array('photos'), (req, res, next) => {
console.log(req.files)
res.end("上传多张照片成功!")
})
七.form-data的解析
multer除了可以上传文件之外,还可以帮助我们解析form-data格式的数据,因为在客户端有可能使用form-data的格式给我们传递普通的数据,如果要解析普通数据,我们可以使用any。
js
const express = require("express")
const multer = require("multer")
const app = express()
const upload = multer()
app.post("/login", upload.any(), (req, res, next) => {
console.log(req.body)
res.end("登录成功,欢迎回来")
})
app.listen(9000, () => {
console.log("服务运行在9000端口")
})
八.客户端发送请求的方式
客户端传递到服务器参数的方法常见的有五种:
- 方式一:通过get请求中的URL的params
- 方式二:通过get请求中的URL的query
- 方式三:通过post请求中的body的json格式(中间件中已经使用了)
- 方式四:通过post请求中的body的x-www-form-urlencoded格式(中间件使用过)
- 方式五:通过post请求中的form-data格式(中间件中使用过)
其中有三个我们已经使用过了,我们来尝试着使用下其他两个:
query参数的解析

js
app.get("/home/list", (req, res) => {
const queryInfo = req.query
console.log(queryInfo)
res.end("data list")
})
params参数的解析

js
app.get("/users/:id", (req, res) => {
const id = req.params.id
res.end(`获取到的数据id为${id}`)
})
九.响应数据
end方法 :类似于http中的response.end
方法,用法是一致的。
js
app.get("/login", (req, res) => {
res.end("登录成功,欢迎回来!")
})
json 方法:json方法可以传入很多类型:object,array,string,boolean,number,null等,它们会被转换为json格式返回,这个方式也是我们使用比较多的方式。
js
app.get("/login", (req, res) => {
res.json(666)
})
status方法:用于设置状态码,这是一个函数不是一个属性值。
js
app.get("/login", (req, res) => {
res.status(500)
res.json("创建用户失败!")
})
更多的响应方式可以参考官方文档:www.expressjs.com.cn/4x/api.html...
十.Express路由
如果我们将所有的代码逻辑都写在app中,那么app会变的越来越复杂,一方面完整的Web服务包含非常多的处理逻辑,另一方面有些处理逻辑其实是一个整体,我们应该将它们放在一起,比如user相关的处理,获取某个用户,创建一个新用户等等,我们可以使用express.Router
来创建一个路由处理程序,一个router拥有完整的中间件和路由系统,因此它也被称为迷你应用程序。
js
const express = require("express")
const userRouter = express.Router()
userRouter.get('/users', (req, res, next) => { })
userRouter.get('/users/:id', (req, res, next) => { })
userRouter.post('/users', (req, res, next) => { })
userRouter.delete('/users/:id', (req, res, next) => { })
userRouter.patch('/users/:id', (req, res, next) => { })
// 导出路由
module.exports = userRouter
导出之后我们仅仅需要在app.js
中导入使用app.use
来使用一下即可。
十一.静态资源服务器
部署静态资源我们可以选择很多方式,Node也可以作为静态资源服务器,并且express给我们提供了方便部署静态资源的方法。
js
const express = require("express")
const app = express()
app.use(express.static('./uploads'))
app.listen(9100, () => {
console.log("静态资源服务器启动成功~")
})
十二.服务端错误处理
我们在请求接口的时候如果是一个错误的请求,服务端需要响应给客户端一些错误的信息,所以我们需要一个服务端给客户端进行错误处理的方案。
方案一:返回http错误码
js
res.status(401)
res.json("未授权访问的信息")
方案二:http状态码 200 信息中会包含错误code/message
js
res.json({
code:'-1001',
message:"未授权访问的信息,请检查token",
})
方案一是早期使用比较多的方案,目前大多数公司使用的是第二种方案,在Express中我们还可以编写一个错误处理中间件,当某个接口中出现错误直接通过next(-1001)
这种方式来调用。
js
app.use((err,req,res,next)=>{
const code = err;
let message = "未知错误!"
switch(code){
case -1001:
message = "没有输入用户名和密码!"
break;
case -1002:
message = "输入用户名和密码错误!"
}
res.json({ code, message })
})
十三.总结
这篇文章到这里就结束了🌸,这篇文章我们学习了Node中的Express框架,我们分别介绍了Express核心概念中间件,包括Express内置的中间件和第三方中间件,我们使用了multer分别实现了单文件上传和多文件上传,并且在最后封装了Express全局的错误处理,相信在你学习了Express之后你会对服务端有更加深刻的理解,同时也是技术栈上一块坚实的拼图~