前言
🫥大家好我是一溪风月
一名程序员界搬砖工 今天来分享下Express相关的内容。
🤗话接上回~前面我们已经学习了使用HTTP模块来创建和搭建Web服务器,为什么还要学习Web框架?因为使用原生HTTP模块进行处理的时候比较复杂,比如在文件的上传等等,需要做非常多的处理,常见的处理有URL
的处理,Methods方法的判断,参数的处理,逻辑代码的处理,都需要我们自己来处理和封装,并且所有的内容都需要放在一起,会非常的混乱。
🤡那么既然要学习框架为什么要学习Express和Koa这两个框架,因为Express框架和Koa是比较受欢迎的,并且很多框架都是基于Express的,比如Nest框架,底层就是基于Express框架的。
🥱Express的核心就是中间件,如果你想把Express掌握好,其实就是理解中间件。
一.Express的安装与基本使用
🥺使用Express有两种方式:首先我们可以像Vue和React的脚手架一样来使用它,也可以自己去搭建。
- 基于脚手架来直接搭建使用Express。
js
// 全局安装
npm install -g express-generator
// 创建Express应用
express express-demo
// 安装依赖
npm i
// 启动Express
node bin/www
- 自己搭建自己的Express框架。
js
npm init -y
// 安装express
npm install express
// 下载依赖
npm install
😎既然我们安装了Express项目现在我们就来简单的使用以下Express,我们依然在Apifox中cesium返回结果,
当然我们平时在开发的时候会有很多返回json
的需求,在Express中我们可以直接返回json
通过res.json
js
const express = require("express")
// 创建express的服务器
const app = express()
// 客户端访问URL:/login和/home
app.post('./login', (req, res, next) => {
res.end("登录成功!")
})
app.get('/home', (req, res, next) => {
res.end("进入home页")
})
// 启动服务器,并且监听端口
app.listen(8000, () => {
console.log("Express服务器启动成功")
})
二.认识中间件
🫥Express是一个路由和中间件的Web框架,它的本身功能特别的少,Express的核心就是中间件,也就可以理解为学会了
- Express应用程序本质上是一系列中间件函数的调用。
- Express主要提供了两种中间件的方式
app/router.use
app/router.methods
.
🤗基本使用方式如下:
- 最普通的中间件的用法,所有的请求都会经过。
js
const express = require("express")
// 创建express的服务器
const app = express()
app.use((req, res, next) => {
res.end("HelloWorld")
})
// 启动服务器,并且监听端口
app.listen(8000, () => {
console.log("Express服务器启动成功")
})
- 路径匹配中间件,会匹配固定的路径。
js
const express = require("express")
// 创建express的服务器
const app = express()
app.use('/user', (req, res, next) => {
res.end("HelloWorld")
})
// 启动服务器,并且监听端口
app.listen(8000, () => {
console.log("Express服务器启动成功")
})
- 路径和方法匹配中间件,匹配对应的方法和路径时候。
js
const express = require("express")
// 创建express的服务器
const app = express()
app.use('/user', (req, res, next) => {
res.end("HelloWorld")
next()
})
app.get('/login', (req, res, next) => {
res.end("登录成功")
next()
})
// 启动服务器,并且监听端口
app.listen(8000, () => {
console.log("Express服务器启动成功")
})
- 在一个请求中使用多个中间件
js
const express = require("express")
// 创建express的服务器
const app = express()
app.post('/user', (req, res, next) => {
console.log("匹配到了user")
next()
}, (req, res, next) => {
console.log("中间件执行")
next()
})
// 启动服务器,并且监听端口
app.listen(8000, () => {
console.log("Express服务器启动成功")
})
🤡哈哈哈,既然基本知识学过了,那么看下下面这个问题,让我们更深层次的理解中间件,我们可以看到在我们的代码中有对应匹配的user
的代码,你觉得它是直接去匹配这个中间件哪?还是从其他匹配方式?
js
const express = require("express")
// 创建express的服务器
const app = express()
// example 01
app.use((req, res, next) => {
console.log("答案一")
next()
})
app.post('/user', (req, res, next) => {
console.log("答案二")
next()
}, (req, res, next) => {
console.log("答案三")
next()
})
// 启动服务器,并且监听端口
app.listen(8000, () => {
console.log("Express服务器启动成功")
})
执行结果:答案一,答案二,答案三
三.编写解析request body中间件
🤡业务流程:当客户端上传的内容是用户名为zpj
密码为123456
就返回登陆成功,并且限制客户传递的数据通过json
的方式进行传递,服务端编写解析body
的中间件进行验证。
js
const express = require("express")
const app = express()
app.use((req, res, next) => {
if (req.headers['content-type'] === 'application/json') {
req.on('data', (data) => {
const userInfo = JSON.parse(data.toString())
req.body = userInfo
})
req.on('end', () => {
next()
})
} else {
next()
}
})
app.post('/login', (req, res, next) => {
if (req.body.username === 'zpj' && req.body.password === '123456') {
res.json({
code: 200,
message: '登录成功'
})
} else {
res.json({
code: 400,
message: '用户名或密码错误'
})
}
})
app.listen(9000, () => {
console.log("服务器启动成功")
})
🤡请求结果如下:
四.应用级中间件-body解析
🫥如果我们所有的功能中间件都要自己进行编写往往会出现很多问题,因为一方面因为某些中间件比较复杂,另外一方面假如我们每次使用都自己进行编写会比较繁琐,其实有很多中间件都是早就已经编写好的,比如解析body的中间件express.json
还有前边我们涉及的文件上传的中间件multer
都是框架中早就帮我们编写好的,我们直接拿来用就好了。
😎使用express.json
中间件来进行body
的解析
js
const express = require('express')
const app = express()
app.use(express.json()) // 全局中间件使用解析 body-json
app.post('/login', (req, res, next) => {
if (req.body.username === '王鹏' && req.body.password === '123456') {
res.json({
code: 0,
msg: '登录成功'
})
} else {
res.json({
code: 1,
msg: '用户名或密码错误'
})
}
})
app.listen(9000, () => {
console.log("服务启动成功")
})
👹请求结果如下:
🥺但是当有另外一种可能我们上传的格式是application/x-www-form-urlencoded
的话我们需要添加另外一个中间应用级别的中间件,这样就能够解析这种类型的数据了。
js
app.use(express.urlencoded({extended:true}));
🥱表单形式也是日常文件上传的时候经常使用的,我们使用multer
来兼容表单formdata
形式,结合下方文件上传的代码,我们只需要做如下操作即可。
js
app.use(upload.any())
五.应用级中间件-文件上传
🫥使用express.multer
来实现文件的上传
- 首先由于
multer
并不是express中内置的,它属于一个第三方的中间件,如果我们想要使用首先要进行安装
js
npm install multer
- 使用
multer
进行文件的上传,需要注意的是upload.single(file)
需要和formdata
中的字段名称保持一致。
js
const express = require('express')
const multer = require('multer')
const app = express()
// 配置multer
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'upload/') // 指定文件上传的目录
},
filename: function (req, file, cb) {
cb(null, file.originalname) // 保持文件上传的名称
}
})
// 使用multer
const upload = multer({
storage: storage
})
app.post('/upload', upload.single('file'), (req, res, next) => {
res.end("上传成功")
})
app.listen(9000, () => {
console.log("服务启动成功")
})
🥱内容上传成功:
🫥以上我们看到的是单文件上传的案例,但是在实际开发中我们经常进行的多文件的上传,那么我们该如何实现多文件的上传哪?其实很简单在multer
模块中也提供了多文件上传的方法,我们只需要进行如下的修改即可。
js
// 单文件上传
app.post('/upload', upload.single('file'), (req, res, next) => {
res.end("上传成功")
})
// 多文件上传
app.post('/upload', upload.array('photos'), (req, res, next) => {
res.end("多文件上传成功")
})
六.应用级中间件-日志记录
🫥在进行前端开发的时候我们看内容基本靠的是打印,但是在服务端我们有的时候需要看客户端的请求记录,包括请求时间,接口状态等等内容,在服务端我们一般会使用日志库来记录服务端的日志,常见的日志库包括Wiston
,morgan
等等,在这里我们首先来使用下morgan
来记录日志信息
首先第一步我们需要单独安装morgan
npm install morgan
然后我们进行如下的编码来使用日志库的相关内容。
js
const express = require('express')
const morgan = require('morgan')
const fs = require('fs')
const app = express()
const logFileWriter = fs.createWriteStream('./log/access.log', {
flags: 'a+'
})
// 使用中间件
app.use(morgan('combined', {
stream: logFileWriter
}))
app.get('/login/:id/:name', (req, res, next) => {
console.log(req.params)
res.json('params成功')
})
app.get('/login', (req, res, next) => {
console.log(res.query)
res.json('query成功')
})
const port = 9000
app.listen(port, () => {
console.log("服务启动在9000端口")
})
👹日志打印结果,我们可以通过看日志来排查问题。
七.常见HTTP请求格式
🤡经常进行前端开发,在工作过程中我们可能大多数时候使用的是JSON
格式进行参数上传的,偶尔涉及到文件的上传我们会用到form-data
的格式进行文件的上传,如果你使用过postman
或者apifox
你可能会发现上传的方式很多,其实这些不同的方式在服务端是都支持的,所以我们很必要一起来了解下所有的上传方式。
- 通过
GET
请求的URL的params - 通过
GET
请求的URL的query - 通过
POST
请求的URL的json
- 通过
POST
请求中的body
的x-www-form-urlencoded
格式。 - 通过
POST
请求的formdata
格式
🥺我们来一个个的尝试着使用一下前端使用过的三个POST我们就不看了,首先实我们来看下params
和query
两种方式。
params
:路径类似于这种方式http://localhost:8080/login/coder/why
我们在Express中的方式。
js
app.get('/login/:id/:name', (req, res, next) => {
console.log(req.params)
res.json('请求成功')
})
query
:GET
请求的query的方式,这种请求方式就是我们日常进行GET
请求的query
方式。
js
app.get('/login', (req, res, next) => {
console.log(res.query)
res.json('query成功')
})
八.Express数据响应
🤡请求数据的目的是为了获取到数据,在前边我们一般使用的是res.end
的方式来响应客户端的数据,但是其实在现实的开发过程中一般前端接受的数据格式是JSON
数据所以我们使用的一般是res.json
,下面是我们最常用的三个响应方式
end
:与HTTP模块中的end用法一致。json
方法:json方法中可以传入很多的类型:object、array、string、boolean、number、null等,它们会被转换成json格式返回。status
方法:用来设置HTTP响应码。
九.为什么要使用路由
🤡试想一下如果我们把全部的内容都存放在App中,那么App会变得非常的复杂,没有对用的层次和架构导致我们开发的东西难以维护,一方面完整的Web服务器包含非常多的处理逻辑,另一方面有些处理逻辑其实是一个整体,我们应该把他们放在一起,比如对user
相关的处理。
- 获取用户列表
- 获取某一个用户的信息
- 创建一个新的用户
- 删除一个用户
- 更新一个用户
十.路由的相关使用
🦝我们知道在之前我们写相关代码都是基于app
这个内容的,我们的代码难以组织,app会越来越繁重,不利于我们对整个项目进行管理,所以为了解决这个问题我们引入了后端路由的概念,我们可以使用express.Router
来创建一个路由处理程序。
- 一个Router实例拥有完整的中间件和路由系统。
- 因此,也被称为迷你应用程序(mini-app)
js
const express = require('express')
const userRouter = express.Router()
userRouter.get('/', (req, res, next) => {
res.json('用户列表数据')
})
userRouter.get('/:id', (req, res, next) => {
const id = req.params.id
res.json('某个用户的数据id' + id)
})
userRouter.post('/', (req, res, next) => {
res.json("创建用户成功")
})
userRouter.delete('/:id', (req, res, next) => {
const id = req.params.id
res.json("删除某一个用户的id" + id)
})
userRouter.patch('/:id', (req, res, next) => {
const id = req.params.id
res.json("修改某一个用户的数据" + id)
})
module.exports = userRouter
js
const express = require('express')
const userRouter = require('./router/userRouter.js')
const app = express()
// 使用路由
app.use('/users', userRouter)
app.listen(9000, () => {
console.log("服务启动成功")
})
十一.如何在Express中部署静态资源
🫥在Express中进行非常的简单,直接使用已经提供的中间件就可以了,部署的内容可以是图片等静态文件也可以是前端打包好的静态资源。
php
app.use(express.static(./upload))
十二.使用Nginx部署前端资源原理
🥺首先我们先来了解下使用静态资源服务器部署静态资源的基本原理是什么
- 开发者将静态资源上传到服务器的某个文件夹中。
- 客户通过浏览器在地址中输入URL,Nginx从文件夹中查找静态资源的内容。
- 将查找到的内容返回给浏览器,浏览器根据文件的依赖进行下载展示。
十三.使用Express代替Nginx部署静态资源
🤡首先我们可以将开发好的前端项目在Express中进行部署,我们拷贝一下dist
包,然后使用如下的方式进行引入就可以了。
js
const express = require('express')
const userRouter = require('./router/userRouter.js')
const app = express()
app.use(express.static('./dist'))
// 使用路由
app.use('/users', userRouter)
app.listen(9000, () => {
console.log("服务启动成功")
})
然后我们直接在浏览器上访问9000
端口。
🫥疑问解答,为什么直接访问900端口就可以进行部署了?因为无论在Nginx还是在Express
中当你直接访问对应端口的时候静态资源服务器会自动去查找index.html
然后将它加载到浏览器中,之后会依次加载他们所依赖的对应的样式文件和JS文件等等。
十四.Express中的错误处理
🫥我们在开发中难免会发生错误,可能是客户端的问题,有可能是服务器端的问题,遇到问题我们需要将错误信息进行显示出来,方便我们进行错误的排查,我们首先使用比较普通的思路来处理一下,代码如下:
js
const express = require('express')
const app = express()
app.use(express.json()) // 解析json格式数据
app.post('/login', (req, res,next) => {
// 获取登录的用户名和密码
const { username, password } = req.body
// 对用户名和密码进行判断
if (!username || !password) {
next(-1001)
} else if (username !== 'zpj' || password !== 123456) {
next(-1001)
} else {
next(-1002)
}
})
app.listen(9000, () => {
console.log("服务运行9000端口")
})
😎当我们进行不正确的密码账户输入的时候就会报错,其实是可以满足我们对错误处理的要求的,但是在项目中其实这种方式不是特别的优雅,所以我们还有其他的更加优秀的方案,我们可以使用Express
提供的中间件来实现对错误的处理。
js
const express = require('express')
const app = express()
app.use(express.json()) // 解析json格式数据
app.post('/login', (req, res) => {
// 获取登录的用户名和密码
const { username, password } = req.body
// 对用户名和密码进行判断
if (!username || !password) {
res.json({
code: -1001,
message: "没有输入用户名和密码"
})
} else if (username !== 'zpj' || password !== 123456) {
res.json({
code: -1002,
message: "用户名或者密码错误"
})
} else {
res.json({
code: 0,
message: "登陆成功",
token: 'abcdefg'
})
}
})
// 错误处理中间件
app.use((err, req, res, next) => {
const code = err
let message = '未知的错误信息'
switch (code) {
case -1001:
message = '没有输出用户名和密码'
break
case -1002:
message = '输入用户名和密码错误'
break
}
res.json({
code, message
})
})
app.listen(9000, () => {
console.log("服务运行9000端口")
})
十五.总结
🤡Express的核心就是中间件,我们本章主要介绍的内容就是中间件,通过学习了Express相关中间件可以更加深入的理解了Express的相关内容,同时也为我们后期学习其他框架和技术打下了基础。