目錄
0 前言?
1 初始化
2 注冊登錄?
2.1 注冊
2.1.1 功能:密碼加密(2.3.3)
2.1.1.1 操作?
2.1.1.2 bcryptjs詳解
2.1.2 插入新用戶(2.3.4)?
?2.1.3?優化:表單數據驗證(2.5)
2.1.3.1 過時代碼修正
2.1.3.2 關鍵操作
2.2 登錄?
2.2.1 判斷密碼是否正確(2.6.3)
2.2.2 生成 JWT 的 Token 字符串的注意點(2.6.4)
3 個人中心
3.1 更新用戶基本信息
3.1.1?驗證表單數據(3.2.2)
3.2 重置密碼
3.2.1 驗證表單數據(3.3.2)
3.3 更新頭像
3.3.1 驗證表單數據(3.4.2)
4 文章分類管理
4.1?根據Id更新文章分類數據
4.1.1?查詢分類名稱與別名是否被占用(4.5.4)
5 文章管理
5.1 發布新文章
5.1.1?使用 multer 解析表單數據(5.2.3)
?5.1.2 驗證表單數據(5.2.4)
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 :
npm i bcryptjs@2.4.3
導入 bcryptjs :
const bcrypt = require('bcryptjs')
在注冊用戶的處理函數中,確認用戶名可用之后,調用 bcrypt.hashSync(明文密碼, 隨機鹽的長度) 方法,對用戶的密碼進行加密處理:
// 對用戶的密碼,進行 bcrype 加密,返回值是加密之后的密碼字符串
userinfo.password = bcrypt.hashSync(userinfo.password, 10)
2.1.1.2 bcryptjs詳解
bcrypt.hashSync?是 `bcrypt` 庫中用于同步生成哈希值的函數,在 Node.js 中經常使用該庫來對密碼等敏感信息進行哈希處理。以下詳細介紹 bcrypt.hashSync?函數的參數:
函數
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)?
判斷插入是否成功
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 包,為表單中攜帶的每個數據項,定義驗證規則:
npm install @hapi/joi@17.1.0
安裝 @escook/express-joi 中間件,來實現自動對表單數據進行驗證的功能:
npm i @escook/express-joi
常用驗證規則:
除此之外,本文下方的驗證表單數據中還包含一些其他常用規則
/*** string() 值必須是字符串* alphanum() 值只能是包含 a-zA-Z0-9 的字符串* min(length) 最小長度* max(length) 最大長度* required() 值是必填項,不能為 undefined* pattern(正則表達式) 值必須符合正則表達式的規則*/
注意:一定要先指定一種數據類型string()或者其他(包括any()),然后才能執行后續操作
導出規則
// 注冊和登錄表單的驗證規則對象
exports.reg_login_schema = {
// 表示需要對 req.body 中的數據進行驗證body: {username,password,},}
使用規則
// 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 不一致)
bcrypt.compareSync(用戶提交的密碼, 數據庫中的密碼)
?示例:
// 拿著用戶輸入的密碼,和數據庫中存儲的密碼進行對比
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 的高級語法,快速剔除 密碼 和 頭像 的值:?
// 剔除完畢之后,user 中只保留了用戶的 id, username, nickname, email 這四個屬性的值
const user = { ...results[0], password: '', user_pic: '' }
3 個人中心
3.1 更新用戶基本信息
3.1.1?驗證表單數據(3.2.2)
記錄新屬性
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)
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)
// dataUri() 指的是如下格式的字符串數據:
// data:image/png;base64,VE9PTUFOWVNFQ1JFVFM=const avatar = joi.string().dataUri().required()
dataUrl() 方法用于驗證一個字符串是否符合數據 URL 的格式
4 文章分類管理
4.1?根據Id更新文章分類數據
4.1.1?查詢分類名稱與別名是否被占用(4.5.4)
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 格式的表單數據
安裝:
npm i multer@1.4.2
創建與使用:
// 導入解析 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檢測
// 導入驗證數據的中間件
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)
驗證文件類型:
// 發布新文章的處理函數
exports.addArticle = (req, res) => {// 手動判斷是否上傳了文章封面if (!req.file || req.file.fieldname !== 'cover_img') return res.cc('文章封面是必選
參數!')// TODO:表單數據合法,繼續后面的處理流程...})
5.1.3 新建數據對象(5.2.5)
const articleInfo = {// 標題、內容、狀態、所屬的分類Id...req.body,// 文章封面在服務器端的存放路徑cover_img: path.join('/uploads', req.file.filename),// 文章發布時間pub_date: new Date(),// 文章作者的Idauthor_id: req.user.id,}
斷更...