定义:Node.js
是一个跨平台 JavaScript 运行环境
,使开发者可以搭建服务器端的 JavaScript 应用程序
为什么 Node.js 能执行 JS 代码:
Node.js 中根据模块来源不同,将模块分为了3大类,分别是:
使用强大的 require()
方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用(注意:使用 require() 方法加载其他模块时,会执行被加载模块中的代码)
// 加载内置模块
const fs = require('fs')
// 加载用户自定义的模块
const myModule = require('./myModule.js')
// 加载第三方模块
const moment = require('moment')
每个自定义的模块中都有一个 module 对象,它里面存储了和当前模块有关的信息(打印结果,如下所示)
Module {
id: '.',
path: 'E:\\Desktop\\jsProject',
exports: {},
filename: 'E:\\Desktop\\jsProject\\test.js',
loaded: false,
children: [],
paths: [
'E:\\Desktop\\jsProject\\node_modules',
'E:\\Desktop\\node_modules',
'E:\\node_modules'
]
}
在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,供外界使用
外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的对象
【myModule.js文件】
module.exports.username = 'Jack'
module.exports.sayHello = () => { console.log('Hello') }
【text.js文件】
const m = require('./myModule')
console.log(m) // { username: 'Jack', sayHello: [Function (anonymous)] }
Node.js 遵循了 CommonJS 模块化规范,CommonJS 规定了模块的特性和各模块之间如何相互依赖
CommonJS 规定:
npm -v
命令,来查看自己电脑上所安装的 npm 包管理工具的版本号目标:通过npm包管理工具,下载 moment 包,并输出格式化时间
npm install moment
npm i moment
npm i [email protected]
// 1. 导入 moment 包
const moment = require('moment')
// 2. 使用包里面的方法
const dt = moment.format('YYYY-MM-DD HH:mm:ss')
console.log(dt) // 输出格式化时间
初次装包完成后,在项目文件夹下多一个叫做 node_modules 的文件夹和 package-lock.json 的配置文件
node_modules 文件夹用来存放所有已安装到项目中的包。require() 导入第三方包时,就是从这个目录中查找并加载包
package-lock.json 配置文件用来记录 node_modules 目录下的每一个包的下载信息,例如包的名字、版本号、下载地址
注意:不要手动修改 node_modules 或 package-lock.json 文件中的任何代码,npm 包管理工具会自动维护它们
npm i [email protected]
中的版本号由三位数字组成
每一位数字所代表的含义:
第一位数字:大版本(例如:底层重构)
第二位数字:功能版本(例如:新功能的推出)
第三位数字:Bug修复版本(例如:修复某个Bug)
版本号提升的规则:
只要前面的版本号增长了,后面的版本号"归零"
npm 规定,在项目根目录中,必须提供一个叫做 package.json 的包管理配置文件。用来记录与项目有关的一些配置信息,例如:
- 项目的名称、版本号、描述等
- 项目中都用到了哪些包
- 哪些包只在开发期间会用到
- 哪些包在开发和部署时都需要用到
问题:
在多人协作时,第三方包的体积过大,不方便团队成员之间共享项目源代码
解决方案:
共享时剔除 node_modules 目录,让其他成员根据 package.json 配置文件,自行下载第三方模块(注意:今后在项目开发中,一定要把 node_modules 文件夹,添加到 .gitignore 忽略文件中)
快速创建 package.json 配置文件
npm init -y
npm install
npm uninstall 包的名称
npm i 包的名称 -D
或者
npm i 包的名称 --save-dev
# 查看当前的下包镜像源
npm config get registry
# 将下包的镜像源切换成淘宝镜像源
npm config set registry https://registry.npmmirror.com
# 检查镜像源是否下载成功
npm config get registry
// 1. 导入 fs 模块对象
const fs = require('fs')
// 2. 写入文件
// 参数1:要写入内容的文件的存放路径
// 参数2:表示要写入的内容
// 参数3:写入的格式(默认utf8)
// 参数3:回调函数
const info = 'Hello World'
fs.writeFile('./test.txt', info, 'utf8', function (err) {
if (err) console.log(`错误信息${err}`)
else console.log(`写入数据成功(数据信息:${info})`)
// 3. 读取文件
// 参数1:读取文件的存放路径
// 参数2:读取文件时候采用的编码格式(默认utf8)
// 参数3:回调函数,拿到读取失败和成功的结果
fs.readFile('./test.txt', 'utf8', (err, data) => {
if (err) console.log(`错误信息${err}`)
else console.log(`读取数据成功(数据信息:${data.toString()})`) // data 是 buffer 16 进制数据流对象,需要转换为字符串格式
})
})
// __dirname 表示获取当前文件所处的目录(文件夹)
console.log(__dirname)
// 导入 path 模块
const path = require('path')
// 注意: ../会抵消前面的路径
const pathStr = path.join('/a', '/b/c', '../', '/d', '/e')
console.log(pathStr) // 输出路径为:\a\b\d\e
// 导入 path 模块
const path = require('path')
// 定义文件的"存放路径"
const filePath = '/a/b/c/index.html'
// 从"存放路径"中获取文件名(包含扩展名部分)
const fileName1 = path.basename(filePath)
console.log(fileName1) // 输出结果:index.html
// 从"存放路径"中获取文件名(不包含扩展名部分)
const fileName2 = path.basename(filePath, '.html')
console.log(fileName2) // 输出结果:index
// 从"存放路径"中获取文件扩展名
const extName = path.extname(filePath)
console.log(extName) // 输出结果:.html
// 1. 导入 http 模块
const http = require('http')
const { resolve } = require('path')
// 2. 创建 web 服务器实例
const server = http.createServer()
// 3. 为服务器实例绑定 request 事件
// req 是请求对象,包含了与客户端相关的数据和属性
// res 是响应对象
// res.end() 方法用于向客户端发送指定的内容,并结束本次请求的处理过程
server.on('request', (req, res) => {
// req.url 是客户端请求的 URL 地址
const url = req.url
// req.method 是客户端请求的 method 类型
const method = req.method
// 为了防止中文乱码的问题,需要设置响应头
res.setHeader('Content-Type', 'text/html; charset=utf-8')
// res.end() 方法
res.end(`您的请求方式为:
${method} 请求`)
})
// 4. 启动服务器
server.listen(80, () => {
console.log('server running at http://127.0.0.1:80')
})
npm i [email protected]
// 1. 导入 express
const express = require('express')
// 2. 创建 web 服务器
const app = express()
// 4. 监听客户端的 GET 和 POST 请求
// GET app.get('请求URL', (请求对象, 响应对象) => { 处理函数 })
// POST app.post('请求URL', (请求对象, 响应对象) => { 处理函数 })
app.get('/user', (req, res) => {
res.send({ name: 'Jack', age: 20, gender: '男' }) // 响应一个 JSON 对象
})
app.post('/user', (req, res) => {
res.send('订单提交成功,请等待商家发货') // 响应
})
// 5. 获取URL携带的查询参数
app.get('/', (req, res) => {
// 默认情况下,req.query 是一个空对象
// 查询参数 ?name=Jack&age=20
// 可通过 req.query.name 与 req.query.age 获得其值
console.log(req.query)
res.send(req.query)
})
// 6. 获取URL中的动态参数
app.get('/user/:id/:name', (req, res) => {
// req.params 默认是一个空对象,里面存放着,通过冒号识别出来的参数值
res.send(req.params)
})
// 3. 调用 app.listen(端口号, 启动成功后的回调函数)
app.listen(80, () => {
console.log('Server started successfully at http://127.0.0.1/')
})
介绍:
express 提供了一个非常好用的函数,叫做 express.static()
通过它,我们可以非常方便地创建一个静态资源服务器
例如:
项目目录下有一个 public 文件夹,里面存放了图片、html、css、js文件,通过下面的代码就可以将它们对外开放访问了
注意:
express 在指定的静态文件中查找文件,并对外提供资源的访问路径,因此,存放静态文件的目录名(本例中为public)是不会出现在URL中的
http://127.0.0.1/images/img0.jpg
http://127.0.0.1/html/index.html
http://127.0.0.1/css/index.css
http://127.0.0.1/js/index.js
app.use(express.static('public'))
app.use(express.static('public'))
app.use(express.static('files'))
......
如果希望在托管的静态资源访问路径之前,挂载路径前缀,可以使用下面的代码
http://127.0.0.1/public/images/img0.jpg
http://127.0.0.1/public/html/index.html
http://127.0.0.1/public/css/index.css
http://127.0.0.1/public/js/index.js
app.use('public', express.static('public'))
在编写调试 Node.js 项目的时候,如果修改了项目的代码,则需要频繁的手动 close 掉,然后再重新启动,非常繁琐
为了解决这个问题,我们可以使用 nodemon 这个工具,它能够监听项目文件的变动,当代码修改后,nodemon 会自动帮我们重启项目,极大方便了开发和调试
在终端中运行下面命令,即可将 nodemon 安装为全局可用的工具
npm install -g nodemon
nodemon 要执行的js文件的路径
例如:nodemon E:\Desktop\jsProject\test.js
直接挂载路由
const express = require('express')
const app = express()
// 直接在app上挂载路由
app.get('/', (req, res) => { res.send('this is get') })
app.post('/', (req, res) => { res.send('this is post') })
app.listen(80, () => {
console.log('Server is running at http://127.0.0.1/')
})
模块化的方式,挂载路由,实现解耦合
// 这是路由模块
// 1. 导入 express 模块
const express = require('express')
// 2. 创建路由对象
const router = express.Router()
// 3. 挂载具体的路由
router.get('/list', (req, res) => { res.send('Get user list.') })
router.post('/add', (req, res) => { res.send('Add new user.') })
// 4. 向外导出路由对象
module.exports = router
// 这是 app (application-应用)
// 1. 导入 express 模块
const express = require('express')
// 2. 创建应用
const app = express()
// 4. 导入路由模块
const router = require('./routerModule')
// 5. "注册"路由模块
app.use('/user', router)
// app.use(express.static('public'))
// 注意:app.use() 函数的作用,就是用来"注册"全局中间件
// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
// 1. 导入 express 模块
const express = require('express')
// 2. 创建应用
const app = express()
// 4. 定义一个中间件函数
const mw = (req, res, next) => {
console.log('这是最简单的中间件函数')
next() // 把流转关系,转交给下一个中间件或路由
}
// 5. 将 mw 注册为全局生效的中间件
// 当注册成功后,用户每次请求服务时,都会先执行中间键函数里面的内容,再执行路由函数里面的内容
app.use(mw)
// 6. 创建路由
app.get('/', (req, res) => { res.send('home page') })
app.get('/user', (req, res) => { res.send('user page') })
// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
// 1. 导入 express 模块
const express = require('express')
// 2. 创建应用
const app = express()
// 4. 注册全局中间件
app.use((req, res, next) => {
// 获取请求到达服务器的时间
const time = Date.now()
// 为req对象,挂载自定义属性,从而把时间共享给后面的所有路由
req.startTime = time
next()
})
// 5. 创建路由
app.get('/', (req, res) => { res.send('home page ' + req.startTime) })
app.get('/user', (req, res) => { res.send('user page ' + req.startTime) })
// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
// 1. 导入 express 模块
const express = require('express')
// 2. 创建应用
const app = express()
// 4. 注册全局中间件
// 4.1 注册第一个中间件
app.use((req, res, next) => {
console.log('调用了第1个中间件')
next()
})
// 4.2 注册第二个中间件
app.use((req, res, next) => {
console.log('调用了第2个中间件')
next()
})
// 4.3 注册第三个中间件
app.use((req, res, next) => {
console.log('调用了第3个中间件')
next()
})
// 5. 创建路由
app.get('/user', (req, res) => { res.send('user page') })
// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
// 1. 导入 express 模块
const express = require('express')
// 2. 创建应用
const app = express()
// 4. 定义一个中间件函数
const mw = (req, res, next) => {
console.log('调用了局部生效的中间件')
next()
}
// 5. 创建路由
app.get('/', mw, (req, res) => { res.send('home page') }) // 使用了局部中间件
app.get('/user', (req, res) => { res.send('user page') }) // 没有使用局部中间件
// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
// 以下两种写法是等效的
app.get('/', mw1, mw2, (req, res)=>{ res.send('home page') })
app.get('/', [mw1, mw2], (req, res)=>{ res.send('home page') })
// 1. 导入 express 模块
const express = require('express')
// 2. 创建应用
const app = express()
// 4. 定义路由
app.get('/', (req, res) => {
throw new Error('The Error made by People.') // 人为制造错误
res.send('home page') // 此处无法执行
})
// 5. 定义错误级别的中间件,捕获整个项目的异常错误,从而防止程序的崩溃
app.use((err, req, res, next) => {
console.log('警告!服务器发生了错误。\n错误信息:' + err.message)
res.send('尊敬的用户,很抱歉,服务器出错了~')
}) // 错误级别的中间件一定要放在所有路由之后
// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
自从 Express 4.16.0 版本开始,Express 内置了3个常用的中间件,极大的提高了 Express 项目的开发效率和体验
1. express.static 快速托管静态资源的内置中间件,例如:HTML文件、CSS样式、图片等
2. express.json 解析 JSON 格式的请求体数据 (4.16.0+ 版本可用)
3. express.urlencoded 解析 URL-encoded 格式的请求体数据 (4.16.0+ 版本可用)
// 1. 导入 express 模块
const express = require('express')
// 2. 创建应用
const app = express()
// 4.1 通过express.json()这个中间件,解析表单中的JSON
app.use(express.json())
// 4.2 通过express.urlencoded()这个中间件,来解析表单中的url-encoded格式的数据
app.use(express.urlencoded({ extended: false }))
// 5. 创建路由
app.post('/user', (req, res) => {
// 使用 req.body 属性,可用接收客户端发送过来的请求体数据
// 默认情况下,如果不配置解析表单数据的中间件,则 req.body 默认等于 undefined
console.log(req.body)
res.send('ok')
})
app.post('/book', (req, res) => {
// 在服务器端,可用通过 req.body 来获取 JSON 格式的表单数据和 url-encoded 格式的数据
console.log(req.body)
res.send('ok')
})
// 3. 运行服务器,监听应用的80端口
app.listen(80, () => { console.log('Server is running at http://127.0.0.1/') })
npm i cors@2.8.5
// 导入 cors 中间件
const cors = require('cors')
// 将 cors 注册为全局中间件
app.use(cors())
点击链接:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
npm install mysql
// 1. 导入 mysql 模块
const mysql = require('mysql')
// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({
host: '127.0.0.1', // 数据库的IP地址
user: 'root', // 登录数据库的账号
password: 'admin123', // 登录数据库的密码
database: 'myDatabase' // 指定要操作哪个数据库
})
// 检测 mysql 模块能否正常工作
db.query('select 1', (err, results) => {
if (err) return console.log(err.message) // mysql 模块工作期间报错了
console.log(results) // 能够成功的执行SQL语句
})
// 1. 导入 mysql 模块
const mysql = require('mysql')
// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({
host: '127.0.0.1', // 数据库的IP地址
user: 'root', // 登录数据库的账号
password: 'admin123', // 登录数据库的密码
database: 'myDatabase' // 指定要操作哪个数据库
})
// 3. 查询 users 表中所有的数据
const sqlStr = 'select * from users'
db.query(sqlStr, (err, results) => {
// 查询数据失败
if (err) return console.log(err.message)
// 查询数据成功
console.log(results)
})
// 1. 导入 mysql 模块
const mysql = require('mysql')
// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({
host: '127.0.0.1', // 数据库的IP地址
user: 'root', // 登录数据库的账号
password: 'admin123', // 登录数据库的密码
database: 'myDatabase' // 指定要操作哪个数据库
})
// 3. 向 users 表中插入数据
const user = { username: 'Jack', password: '123456' }
const sqlStr = 'insert into users set ?'
db.query(sqlStr, user, (err, results) => {
if (err) return console.log(err.message) // 执行SQL语句失败了
if (results.affectedRows === 1) { console.log('插入数据成功!') }
})
// 1. 导入 mysql 模块
const mysql = require('mysql')
// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({
host: '127.0.0.1', // 数据库的IP地址
user: 'root', // 登录数据库的账号
password: 'admin123', // 登录数据库的密码
database: 'myDatabase' // 指定要操作哪个数据库
})
// 3. 更新 users 表中数据
const user = { id: 6, username: 'Jack', password: '123456' }
const sqlStr = 'update users set ? where id=?'
db.query(sqlStr, [user, user.id], (err, results) => {
if (err) return console.log(err.message) // 执行SQL语句失败了
if (results.affectedRows === 1) { console.log('更新数据成功!') }
})
// 1. 导入 mysql 模块
const mysql = require('mysql')
// 2. 建立与 MySQL 数据库的连接
const db = mysql.createPool({
host: '127.0.0.1', // 数据库的IP地址
user: 'root', // 登录数据库的账号
password: 'admin123', // 登录数据库的密码
database: 'myDatabase' // 指定要操作哪个数据库
})
// 3. 删除 users 表中的数据(删除id=5的用户数据)
const sqlStr = 'delete from users where id=?'
db.query(sqlStr, 5, (err, results) => {
if (err) return console.log(err.message) // 执行SQL语句失败了
if (results.affectedRows === 1) { console.log('删除数据成功!') }
})
标记删除法:(扩展)
使用 delete 语句,是真的会把数据从表中抹除掉。为了保险起见,推荐使用标记删除法,来模拟删除的动作。
什么是标记删除法呢?就是在创建表的时候,提前建立好一个 status 状态字段,用来表示当前这条数据是否被删除掉,当我们使用标记删除法后,哪怕用户删除数据了,有一天用户需要找回来,也能找到这条数据。
目前主流的开发模式有两种
服务端发送给客户端的HTML页面,是在服务器通过字符串的拼接,动态生成的。因此,客户端不需要使用Ajax这样的技术额外请求页面的数据。
前后端分离的开发模式,依赖于Ajax技术的广泛应用。简而言之,前后端分离的Web开发模式,就是后端只负责提供API接口,前端使用Ajax调用接口的开发模式。
解决方案:
利用 Vue、React 等前端框架的 SSR (server side render) 技术能够很好的解决 SEO 问题
介绍:
身份认证(Authoritarian)又称"身份验证"、"鉴权",它是指通过一定的手段,完成对用户身份的确认
例如:
手机验证码登录、邮箱密码登录、二维码登录等
不同开发模式下的身份认证
1.服务端渲染推荐使用 Session 认证机制
2.前后端分离推荐使用 JWT 认证机制
HTTP 协议的无状态性质,指的是客户端每次的 HTTP 请求都是独立的,连续多个请求之前没有直接的关系,服务器不会主动保留每次 HTTP 请求的状态
使用 Cookie 可以弥补 HTTP 无法记录状态的问题
Cookie 是存储在用户浏览器中的一段不超过 4KB 的字符串
它由一个名称(Name)、一个值(value)和其他几个用于控罪 Cookie 有效期、安全性、使用范围的可选属性组成
不同域名下的 Cookie 各自独立,每当客户端发起请求时,会自动把当前域名下所有未过期的 Cookie 一同发送到服务器
1.自动发送
2.域名独立
3.过期时限
4.4KB限制
客户端第一次请求服务器的时候,服务器通过响应头的形式,向客户端发送一个身份认证的 Cookie,客户端会自动将 Cookie 保存在浏览器中
随后,当客户端浏览器每次请求服务器的时候,浏览器会自动将身份认证相关的 Cookie,通过请求头的形式发送给服务器,服务器即可验证客户端的身份
由于 Cookie 是存储在浏览器中的,而且浏览器也提供了读写 Cookie 的 API,因此 Cookie 很容易被伪造,不具有安全性。因此不建议服务器将重要的隐私数据,通过 Cookie 的形式发送给浏览器
npm install express-session
// 1. 导入 session 中间件
const session = require('express-session')
// 2. 配置 Session 中间件
app.use(session({
secret: 'keyboard cat', // secret 属性的值可以为任意字符串
resave: false, // 固定写法
saveUninitialized: true // 固定写法
}))
当 express-session 中间件配置成功后,即可通过 req.session 来访问和使用 session 对象,从而存储用户的关键信息
// 1. 创建服务器实例
const express = require('express')
const app = express()
// 3. 配置 express-session 中间件
const session = require('express-session')
app.use(session({
secret: 'keyboard cat', // secret 属性的值可以为任意字符串
resave: false, // 固定写法
saveUninitialized: true // 固定写法
}))
// 4. 创建路由
// 4.1 登录接口
app.post('api/login', (req, res) => {
// 判断用户提交的登录信息是否正确
if (req.body.username !== 'admin' || req.body.password !== '123456') {
return res.send({ status: 1, msg: '登录失败' })
}
req.session.user = req.body // 将用户的信息,存储到 Session 中
req.session.isLogin = true // 将用户的登录状态,存储到 Session 中
req.send({ status: 0, msg: '登录成功' })
})
// 4.2 获取用户姓名的接口
app.get('/api/username', (req, res) => {
// 从 Session 中获取用户的名称,响应给客户端
if (!req.session.isLogin) {
return res.send({ status: 1, msg: 'fail' })
}
res.send({
status: 0,
msg: 'success',
username: req.session.user.username
})
})
// 4.3 退出登录的接口
app.post('/api/logout', (req, res) => {
// 清空当前客户端对应的 Session 信息
req.session.destory()
res.send({
status: 0,
msg: '退出登录成功'
})
})
// 2. 启动服务器
app.listen(80, (req, res) => {
console.log('Server is running at http://127.0.0.1/')
})
Session 认证机制需要配合 Cookie 才能实现。
由于 Cookie 默认不支持跨域访问,所以,当涉及到前端跨域请求后端接口时,需要做很多额外配置,才能实现跨域 Session 认证
当前端请求后端接口不存在跨域问题的时候,推荐使用 Session 身份认证机制
当前端需要跨域请求后端接口的时候,不推荐使用 Session 身份认证机制,推荐使用 JWT 认证机制
Header (头部)
Payload (有效载荷)
Signature (签名)
三者之间使用英文的"."进行分隔
Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串
Header 和 Signature 是安全性相关的部分,只是为了保证 Token 的安全性
客户端收到服务器返回的 JWT 之后,通常会将它存储在 localStorage 或 sessionStorage 中
此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串,从而进行身份认证
推荐的做法是把 JWT 放在 HTTP 请求头的 Authoritarian 字段中
格式如下:
Authorization: Bearer
jsonwebtoken 用于生成 JWT 字符串
express-jwt 用于将 JWT 字符串解析还原成 JSON 对象
npm install jsonwebtoken express-jwt
// 1. 创建服务器实例
const express = require('express')
const app = express()
// 3. 使用 JWT
// 3.1 安装并导入 JWT 相关的两个包
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
// 3.2 设置 secret 密钥,保证 JWT 字符串的安全
const secretKey = 'text use ^_^'
// 3.4 将 JWT 字符串解析还原成 JSON 对象
// 注意:只要配置成功了 express-jwt 这个中间件,就可以把解析出来的用户信息,挂载到 req.user 属性上
const mw = expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] })
app.use(mw) // 将这个中间件注册为全局中间件
// 获取用户信息的接口
app.get('/admin/getInfo', (req, res) => {
// 3.5 使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端
console.log(req.user)
res.send({
status: 200,
message: "获取用户信息成功",
data: req.user
})
})
// 登录接口
app.post('api/login', (req, res) => {
const userinfo = req.body
// 登录失败
if (userinfo.username !== 'admin' || userinfo.password !== '123456') {
return res.send({ status: 400, message: '登录失败' })
}
// 登录成功
// 3.3 登录成功后,生成 JWT 字符串,并通过 token 属性发送给客户端
// 参数1:用户的信息对象
// 参数2:加密的密钥
// 参数3:配置对象,可以配置当前的 token 的有效期
const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '60s' })
req.send({ status: 200, message: '登录成功', token: tokenStr })
})
// 3.6 使用全局错误处理中间件,捕获解析 JWT 失败后产生的错误
app.use((err, req, res) => {
// 判断错误是否为 token 解析失败导致
if (err.name === 'UnauthorizedError') return res.send({ status: 401, message: '无效的token' })
res.send({ status: 500, message: '未知的错误' })
})
// 2. 启动服务器
app.listen(80, (req, res) => {
console.log('Server is running at http://127.0.0.1/')
})