【综合项目】api系统——基于Node.js、express、mysql等技术

目录

[0 前言](#0 前言)

[1 初始化](#1 初始化)

[2 注册登录](#2 注册登录)

[2.1 注册](#2.1 注册)

[2.1.1 功能:密码加密(2.3.3)](#2.1.1 功能:密码加密(2.3.3))

[2.1.1.1 操作](#2.1.1.1 操作)

[2.1.1.2 bcryptjs详解](#2.1.1.2 bcryptjs详解)

[2.1.2 插入新用户(2.3.4)](#2.1.2 插入新用户(2.3.4))

[2.1.3 优化:表单数据验证(2.5)](#2.1.3 优化:表单数据验证(2.5))

[2.1.3.1 过时代码修正](#2.1.3.1 过时代码修正)

[2.1.3.2 关键操作](#2.1.3.2 关键操作)

[2.2 登录](#2.2 登录)

[2.2.1 判断密码是否正确(2.6.3)](#2.2.1 判断密码是否正确(2.6.3))

[2.2.2 生成 JWT 的 Token 字符串的注意点(2.6.4)](#2.2.2 生成 JWT 的 Token 字符串的注意点(2.6.4))

[3 个人中心](#3 个人中心)

[3.1 更新用户基本信息](#3.1 更新用户基本信息)

[3.1.1 验证表单数据(3.2.2)](#3.1.1 验证表单数据(3.2.2))

[3.2 重置密码](#3.2 重置密码)

[3.2.1 验证表单数据(3.3.2)](#3.2.1 验证表单数据(3.3.2))

[3.3 更新头像](#3.3 更新头像)

[3.3.1 验证表单数据(3.4.2)](#3.3.1 验证表单数据(3.4.2))

[4 文章分类管理](#4 文章分类管理)

[4.1 根据Id更新文章分类数据](#4.1 根据Id更新文章分类数据)

[4.1.1 查询分类名称与别名是否被占用(4.5.4)](#4.1.1 查询分类名称与别名是否被占用(4.5.4))

[5 文章管理](#5 文章管理)

[5.1 发布新文章](#5.1 发布新文章)

[5.1.1 使用 multer 解析表单数据(5.2.3)](#5.1.1 使用 multer 解析表单数据(5.2.3))

[5.1.2 验证表单数据(5.2.4)](#5.1.2 验证表单数据(5.2.4))

[5.1.3 新建数据对象(5.2.5)](#5.1.3 新建数据对象(5.2.5))


0 前言

本章仅记录部分功能代码(以前文章中未涉及的新内容)以及所遇到的问题

详细内容见 项目首页 - api系统 - GitCode 中的指导文档

❗❗❗最终完整代码也会上传至GitCode中,收藏方便查找

注:部分标题后面跟了原文档中的序号,方便查看


1 初始化

此部分暂无内容


2 注册登录

2.1 注册

2.1.1 功能:密码加密(2.3.3)

2.1.1.1 操作

使用 bcryptjs 对用户密码进行加密

安装指定版本的 bcryptjs :

CoffeeScript 复制代码
npm i bcryptjs@2.4.3

导入 bcryptjs :

javascript 复制代码
const bcrypt = require('bcryptjs')

在注册用户的处理函数中,确认用户名可用之后,调用 bcrypt.hashSync(明文密码, 随机盐的长度) 方法,对用户的密码进行加密处理:

javascript 复制代码
// 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串
userinfo.password = bcrypt.hashSync(userinfo.password, 10)

2.1.1.2 bcryptjs详解

bcrypt.hashSync 是 `bcrypt` 库中用于同步生成哈希值的函数,在 Node.js 中经常使用该库来对密码等敏感信息进行哈希处理。以下详细介绍 bcrypt.hashSync 函数的参数:

函数

javascript 复制代码
bcrypt.hashSync(data, saltOrRounds);

参数说明

  1. data

类型:string

描述:需要进行哈希处理的数据,通常是用户的密码。这是一个必需的参数,代表你要加密的原始文本。
2. saltOrRounds

类型:string 或 number

描述:该参数可以是一个盐值(string 类型),也可以是生成盐的轮数(number 类型)。
当 saltOrRounds 为 number 类型时

它代表生成盐的轮数,也称为成本因子(cost factor)。这个值越大,生成盐和哈希值所花费的时间就越长,安全性也相对更高。推荐的值通常在 10 - 12 之间。

bcrypt 会根据这个轮数自动生成一个随机的盐值,然后使用这个盐值对 data 进行哈希处理。

当 saltOrRounds 为 string 类型时

它代表一个预先定义好的盐值。使用自定义盐值时,每次使用相同的盐和数据进行哈希处理,会得到相同的哈希结果。

一般情况下,不建议手动指定盐值,因为 bcrypt 自动生成的随机盐值可以更好地保证安全性。

返回值

bcrypt.hashSync 函数会返回一个包含盐值和哈希值的字符串,这个字符串可以安全地存储在数据库中,用于后续的密码验证。


2.1.2 插入新用户(2.3.4)

判断插入是否成功

javascript 复制代码
if (results.affectedRows !== 1) {
return res.send({ status: 1, message: '注册用户失败,请稍后再试!' })
}

即判断影响数据行数是否为1


2.1.3 优化:表单数据验证(2.5)

2.1.3.1 过时代码修正

const joi = require('@hapi/joi') 的写法现在已经失效,

应该这样导入:const joi = require('joi')


2.1.3.2 关键操作

安装 @hapi/joi 包,为表单中携带的每个数据项,定义验证规则:

CoffeeScript 复制代码
npm install @hapi/joi@17.1.0

安装 @escook/express-joi 中间件,来实现自动对表单数据进行验证的功能:

CoffeeScript 复制代码
 npm i @escook/express-joi

常用验证规则:

除此之外,本文下方的验证表单数据中还包含一些其他常用规则

javascript 复制代码
 /**
 * string() 值必须是字符串
 * alphanum() 值只能是包含 a-zA-Z0-9 的字符串
 * min(length) 最小长度
 * max(length) 最大长度
 * required() 值是必填项,不能为 undefined
 * pattern(正则表达式) 值必须符合正则表达式的规则
 */

注意:一定要先指定一种数据类型string()或者其他(包括any()),然后才能执行后续操作


导出规则

javascript 复制代码
// 注册和登录表单的验证规则对象
exports.reg_login_schema = {
// 表示需要对 req.body 中的数据进行验证
  body: {
    username,
    password,
  },
 }

使用规则

javascript 复制代码
 // 1. 导入验证表单数据的中间件
const expressJoi = require('@escook/express-joi')
 // 2. 导入需要的验证规则对象
const { reg_login_schema } = require('../schema/user')


//...


router.post('/reguser', expressJoi(reg_login_schema), userHandler.regUser)

2.2 登录

2.2.1 判断密码是否正确(2.6.3)

使用加密密码的包bcrypt

返回值是布尔值(true 一致、false 不一致)

javascript 复制代码
bcrypt.compareSync(用户提交的密码, 数据库中的密码)

示例:

javascript 复制代码
// 拿着用户输入的密码,和数据库中存储的密码进行对比
const compareResult = bcrypt.compareSync(userinfo.password, results[0].password)
// 如果对比的结果等于 false, 则证明用户输入的密码错误
if (!compareResult) {
	return res.cc('登录失败!')
}
// TODO:登录成功,生成 Token 字符串

2.2.2 生成 JWT 的 Token 字符串的注意点(2.6.4)

核心注意点:在生成 Token 字符串的时候,一定要剔除 密码 和 头像 的值

通过 ES6 的高级语法,快速剔除 密码 和 头像 的值:

javascript 复制代码
// 剔除完毕之后,user 中只保留了用户的 id, username, nickname, email 这四个属性的值
const user = { ...results[0], password: '', user_pic: '' }

3 个人中心

3.1 更新用户基本信息

3.1.1 验证表单数据(3.2.2)

记录新属性

javascript 复制代码
 const id = joi.number().integer().min(1).required()
 const nickname = joi.string().required()
 const email = joi.string().email().required()

integer() 方法用于验证一个值是否为整数


3.2 重置密码

3.2.1 验证表单数据(3.3.2)

javascript 复制代码
body: {
      // 使用 password 这个规则,验证 req.body.oldPwd 的值
    oldPwd: password,
    newPwd: joi.not(joi.ref('oldPwd')).concat(password),
  },

使用 joi.not(joi.ref('oldPwd')).concat(password) 规则,验证 req.body.newPwd 的值

解读:

  1. joi.ref('oldPwd') 表示 newPwd 的值必须和 oldPwd 的值保持一致

  2. joi.not(joi.ref('oldPwd')) 表示 newPwd 的值不能等于 oldPwd 的值

  3. .concat() 用于合并 joi.not(joi.ref('oldPwd')) 和 password 这两条验证规则


3.3 更新头像

3.3.1 验证表单数据(3.4.2)

javascript 复制代码
// dataUri() 指的是如下格式的字符串数据:
// data:image/png;base64,VE9PTUFOWVNFQ1JFVFM=
 const avatar = joi.string().dataUri().required()

dataUrl() 方法用于验证一个字符串是否符合数据 URL 的格式


4 文章分类管理

4.1 根据Id更新文章分类数据

4.1.1 查询分类名称与别名是否被占用(4.5.4)

javascript 复制代码
SELECT * FROM ev_article_cate WHERE Id <> ? AND (name = ? or alias = ?)

注意:已有数据的情况下进行更新内容,需要排除当前数据的内容,否做无法做到部分内容修改!


5 文章管理

5.1 发布新文章

5.1.1 使用 multer 解析表单数据(5.2.3)

URL-encoded:适用于传输简单的文本数据,例如表单中的文本字段

multipart/form-data:适用于上传文件或包含二进制数据的表单

注意:使用 express.urlencoded() 中间件无法解析 multipart/form-data 格式的请求体数据

当前项目,推荐使用 multer 来解析 multipart/form-data 格式的表单数据

安装:

CoffeeScript 复制代码
 npm i multer@1.4.2

创建与使用:

javascript 复制代码
// 导入解析 formdata 格式表单数据的包
const multer = require('multer')
 // 导入处理路径的核心模块
const path = require('path')
// 创建 multer 的实例对象,通过 dest 属性指定文件的存放路径
const upload = multer({ dest: path.join(__dirname, '../uploads') })



// 发布新文章的路由
// upload.single() 是一个局部生效的中间件,用来解析 FormData 格式的表单数据
// 将文件类型的数据,解析并挂载到 req.file 属性中
// 将文本类型的数据,解析并挂载到 req.body 属性中
router.post('/add', upload.single('cover_img'), article_handler.addArticle)

之后文本类型的数据,即字段会通过joi来进行规则验证,但是文件类型的数据不行,得额外用if判断


5.1.2 验证表单数据(5.2.4)

注意:先后顺序一定不能变,因为multer会将其他字段挂载到req.body上,如果在joi之后,会导致部分字段不会被joi检测

javascript 复制代码
// 导入验证数据的中间件
const expressJoi = require('@escook/express-joi')
 // 导入文章的验证模块
const { add_article_schema } = require('../schema/article')
 // 发布新文章的路由
// 注意:在当前的路由中,先后使用了两个中间件:
//       先使用 multer 解析表单数据
//       再使用 expressJoi 对解析的表单数据进行验证
router.post('/add', upload.single('cover_img'), expressJoi(add_article_schema), 
article_handler.addArticle)

验证文件类型:

javascript 复制代码
 // 发布新文章的处理函数
exports.addArticle = (req, res) => {
    // 手动判断是否上传了文章封面
  if (!req.file || req.file.fieldname !== 'cover_img') return res.cc('文章封面是必选
参数!')
  // TODO:表单数据合法,继续后面的处理流程...
 })

5.1.3 新建数据对象(5.2.5)

javascript 复制代码
 const articleInfo = {
  // 标题、内容、状态、所属的分类Id
  ...req.body,
  // 文章封面在服务器端的存放路径
  cover_img: path.join('/uploads', req.file.filename),
  // 文章发布时间
  pub_date: new Date(),
  // 文章作者的Id
  author_id: req.user.id,
 }

断更...

相关推荐
武子康1 小时前
Java-118 深入浅出 MySQL ShardingSphere 分片剖析:SQL 支持范围、限制与优化实践
java·大数据·数据库·分布式·sql·mysql·性能优化
问道飞鱼4 小时前
【数据库相关】TxSQL新增数据库节点步骤
数据库·mysql·txsql·新增节点
Ka1Yan5 小时前
MySQL索引优化
开发语言·数据结构·数据库·mysql·算法
程序猿 董班长8 小时前
springboot配置多数据源(mysql、hive)
hive·spring boot·mysql
且行志悠13 小时前
Mysql的使用
mysql
白鹭13 小时前
MySQL源码部署(rhel7)
数据库·mysql
不知名raver(学python版)14 小时前
npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR!
前端·npm·node.js
星期天要睡觉15 小时前
MySQL 综合练习
数据库·mysql
惜.己15 小时前
针对nvm不能导致npm和node生效的解决办法
前端·npm·node.js
JosieBook16 小时前
【数据库】MySQL 数据库创建存储过程及使用场景详解
数据库·mysql