打开vscode,创建node项目,直接进入一个干净的文件夹,打开控制台
一 项目初始化
1. 初始化包管理
npm init -y
2. 安装express
npm install express@4.17.1
3. 根目录下创建app.js,引入express
// 引入expree
const express = require('express')// 创建实例
const app = express()app.listen(3007, function(){console.log('api serve 启动')
})
4. 安装cors插件解决跨域
npm install cors@2.8.5
5. app.js中导入配置
// 引入
const cors = require('cors')
// 注册为全局中间件
app.use(cors())
6. 配置解析表单中间件
// 配置解析表单数据的中间件,解析 application/x-www-form-urlencode格式的表单
app.use(express.urlencoded({extended: false}))
7. 修改package.json启动方式
下载nodemon npm install nodemon
修改启动方式是
控制台启动项目 - npm run dev
二 配置路由
1.根目录下创建文件夹router和router_handler
router: 具体的接口请求(和前端封装request的请求类似,将请求处理和接口分开管理)
router_handler: 接口请求对应的处理函数
2. router文件下创建user.js
const express = require('express')const router = express.Router()const userHandler = require('../router_handler/user')// 注册新用户
router.post('/register', userHandler.regUser)// 登录
router.post('/login', userHandler.login)// 暴露出router
module.exports = router
3. 在router_handler创建user.js
exports.regUser = (req,res) => {res.send('register:ok')
}exports.login = (req, res) => {res.send('login:ok')
}
3. 在app.js中引入
const useRouter = require('./router/user')
app.use('/api', useRouter)
4. 打开网页版本
Postman: The World's Leading API Platform | Sign Up for FreeAccelerate API development with Postman's all-in-one platform. Streamline collaboration and simplify the API lifecycle for faster, better results. Learn more.
https://www.postman.com/
4.1 登录注册后先设置代理,请求接口
测试发起请求,有返回,证明就apiOK了
三 创建表,连接数据库
1. 下载mySQL Workbench
2. 打开创建数据表
3. 安装配置mysql
3.1 安装sql
npm install mysql@02.18.1
3.2 根目录下创建db文件夹,在创建index.js
const mysql = require('mysql')const db = mysql.createPool({host: '127.0.0.1', // 本机user: 'root', // 用户名password: 'admin123', // 密码database: 'my_db_01' // 数据库
})module.exports = db
四 功能
4.1 定义处理错误中间件
index.js 中统一定义错误中间件
4.2 新增用户注册校验
安装数据校验插件
npm install @hapi/joi@17.1.0
npm install @escook/express-joi
schema下新建user.js ,利用插件处理入参
// 校验参数
const joi = require('@hapi/joi')/*** string() 值必须是字符串* alphanum() 值只能包含a-z A-Z 0-9字符串* min(len) 最小长度* max(len) 最大长度* required() 值为必填项,不能为undefined* pattern(正则表达式) 值必须符合正则表达式规则*/const username = joi.string().alphanum().min(1).max(10).required()
const password = joi.string().pattern(/^[\s]{6,12}$/).required()exports.reg_login_schema = {body: {username,password}
}
router_handler 下,写接口注册的具体逻辑
const db = require('../db/index')
// 需要安装 npm install bcryptjs@2.4.3, 加密插件
const bcryptjs = require('bcryptjs')exports.regUser = (req, res) => {const userInfo = req.bodyconsole.log(userInfo.username, userInfo.password)// 已使用 中间件校验,可取消// if (!userInfo.username || !userInfo.password) {// // 使用注册的中间件// return res.cc('用户名或密码不能为空!')// }const sql = 'select * from my_users where username=?'db.query(sql, [userInfo.username], function (err, results) {if (err) {return res.cc(err, 2)}if (results.length) {return res.cc('用户名被占用')}// 加密后数值 = 带加密密码, 数值(提交安全性)const password = bcryptjs.hashSync(userInfo.password, 10)// 用户名可以用,新增数据到数据库const sql = 'insert into my_users set?'db.query(sql, { username: userInfo.username, password }, function (err, results) {if (err) {return res.cc(err)}if (results.affectedRows !== 1) {return res.cc('注册用户失败,请后再试')}res.cc('注册成功', 0)})})
}exports.login = (req, res) => {res.send('login:ok')
}
路由中使用校验插件
const express = require('express')const router = express.Router()const userHandler = require('../router_handler/user')// 导入验证表单数据的中间件
const expressJoi = require('@escook/express-joi')
// 导入验证规则对象
const { reg_login_schema } = require('../schema/user')// 注册新用户,
// 验证数据的正确性
router.post('/register', expressJoi(reg_login_schema), userHandler.regUser)// 登录
router.post('/login', expressJoi(reg_login_schema), userHandler.login)// 暴露出router
module.exports = router
app.js中进行统一捕获参数错误校验
4.3 生成token 和 验证token (login接口处理)
4.3.1 校验用户的参数
4.3.2 login接口处理,生成token
安装插件
npm install jsonwebtoken@8.5.1// 校验登录
// 检测表单数据是否合法
// 根据用户名查询用户数据
// 输入密码查询
// 生成JWT的Token的字符串
exports.login = (req, res) => {const userInfo = req.body// res.send('login:ok')const sql = 'select * from my_users where username=?'db.query(sql, userInfo.username, function (err, results) {if (err) return res.cc(err)if (results.length !== 1) return res.cc('登录失败!')const compareResult = bcryptjs.compareSync(userInfo.password, results[0].password)if (!compareResult) return res.cc('登录失败!')const user = { ...results[0], password: '', user_pic: '' }// token - 生成tokenconst tokenStr = jwt.sign(user, config.jwtSecreKey, {expiresIn: '10h' // token有效期})// 生成token 返回res.send({token: 'Bearer ' + tokenStr,status: 0,message: "登录成功!"})})}
4.3.3 app.js 配置需校验token的路由
const expressJwt = require('express-jwt')// 配置需解析token的身份认证的路由 ,unless - 配置哪些接口不需要校验的路由, 路由非/api,不进行校验
app.use(expressJwt({ secret: config.jwtSecreKey }).unless({ path: [/^\/api\//] }))
4.4 用户基本信息接口开发
4.4.1 校验更新用户接口参数
4.4.2 创建对应的路由文件 和handler 文件
router - >userinfo.js
const express = require('express')const router = express.Router()const userInfoHandler = require('../router_handler/userInfo')// 导入验证表单数据的中间件
const expressJoi = require('@escook/express-joi')
// 导入验证规则对象
const { update_userinfo_schema } = require('../schema/user')// userInfo
router.get('/userInfo', userInfoHandler.userInfo)// 更新用户信息
router.post('/updateInfo', expressJoi(update_userinfo_schema), userInfoHandler.updateInfo)// 暴露出router
module.exports = router
router_handler -> userInfo.js
const db = require('../db/index')
const config = require('../config')// 获取用户基本信息
exports.userInfo = (req, res) => {const userInfo = req.bodyconsole.log(userInfo.username, userInfo.password)// 已使用 中间件校验,可取消// if (!userInfo.username || !userInfo.password) {// // 使用注册的中间件// return res.cc('用户名或密码不能为空!')// }console.log(req, '==>')const sql = 'select id, username, email, user_pic from my_users where id=?'db.query(sql, req.user.id, function (err, results) {if (err) {return res.cc(err, 2)}if (!results.length) {return res.cc('获取用户信息失败')}res.send({status: 0,message: "查询成功",data: results[0]})})
}exports.updateInfo = (req, res) => {const userInfo = req.bodyconst sql = 'UPDATE my_users SET username = ?, email = ? WHERE id = ?'db.query(sql, [userInfo.username, userInfo.email, userInfo.id], (err, results) => {if (err) {return res.cc(err)}if (results.affectedRows === 0) {return res.cc('用户不存在或未更新任何数据');}res.send({status: 0,message: "更新成功",});})}
4.4.3 app.js中引入
const userInfoRouter = require('./router/userInfo')
app.use('/my', userInfoRouter)
4.5 重置密码接口
4.5.1 新增校验密码
schema - user.js
// 新密码保持密码规则 且不能与旧密码相同
exports.update_pwd_schema = {body: {oldPwd: password,newPwd: joi.not(joi.ref('oldPwd')).concat(password)}
}
4.5.2 定义接口
rouer - userInfo.js
router.post('/updatePwd', expressJoi(update_pwd_schema), userInfoHandler.updatePwd)
router_handler - userInfo.js
exports.updatePwd = (req, res) => {const bodyInfo = req.bodyconst sql = 'select * from my_users where id=?'db.query(sql, req.user.id, (err, results) => {if (err) return res.cc(err)if (results.length !== 1) return res.cc('用户不存在')const compareResult = bcryptjs.compareSync(bodyInfo.oldPwd, results[0].password)if (!compareResult) return res.cc('旧密码不正确')// 更新数据表const password = bcryptjs.hashSync(bodyInfo.newPwd, 10)const sql1 = 'update my_users set password =? where id =?'db.query(sql1, [password, req.user.id], (err, results) => {if (err) return res.cc(err)if (results.affectedRows === 1) {return res.send({status: 0,message: '更新成功'})}res.send('更新失败')})})}
4.6 更新头像接口
4.6.1 定义校验params
schema - user.js
exports.update_avatar_schema = {body: {// base64avatar: joi.string().dataUri().required()}
}
4.6.2 定义接口
router - userInfo.js
// 更换头像
router.post('/update/avatar', expressJoi(update_avatar_schema), userInfoHandler.updataAvatar)
router_handler - userInfo.js
exports.updataAvatar = (req, res) => {// if (err) return res.cc(err)const sql = 'update my_users set user_pic=? where id=?'db.query(sql, [req.body.avatar, req.user.id], (err, results) => {if (err) return res.cc(err)if (results.affectedRows == 1) {return res.send({status: 0,message: '上传成功'})}res.cc('上传失败')})
}