Node.js实践----注册-登录-个人中心(更换密码、头像)接口实现(包含mysql数据库)
項目結(jié)構(gòu)目錄如下
- 1.初始化
- 1.1創(chuàng)建項目
- 1.2 配置cors跨域
- 1.3配置解析表單數(shù)據(jù)
- 1.4初始化路由
- 1.5抽離用戶路由模塊中的處理函數(shù)
- 2.注冊登錄
- 2.1新建ev_user表
- 2.3注冊
- 2.3.1檢測表單數(shù)據(jù)是否合法
- 2.3.2檢測用戶名是否被占用
- 2.3.3 對密碼進(jìn)行加密處理
- 2.3.4插入新用戶
- 2.4 優(yōu)化res.send()代碼
- 2.5優(yōu)化表單驗證
- 2.6登錄
- 2.6.1檢驗用戶提交的表單數(shù)據(jù)是否正確
- 2.6.3服務(wù)器端生成jwt字符串
- 2.7配置token中間件
- 3.個人中心
- 3.1獲取用戶信息
- 3.1.1初始化路由模塊
- 3.1.2初始化路由處理函數(shù)模塊
- 3.1.3獲取用戶的基本信息
- 3.2更新用戶的基本信息
- 3.2.1
- 3.2.2驗證表單數(shù)據(jù)
- 3.2.3實現(xiàn)更新用戶基本信息的功能
- 3.3重置密碼
- 3.3.1定義路由和處理函數(shù)
- 3.3.2驗證表單數(shù)據(jù)
- 3.3.3重置密碼的功能
- 3.4 用戶頭像信息更新
- 3.4.1定義路由和處理函數(shù)
- 3.4.2驗證表單數(shù)據(jù)
- 3.4.3實現(xiàn)更新用戶頭像的功能
1.初始化
1.1創(chuàng)建項目
1.新建api_server文件,初始化包配置管理配置文件
npm init -y2.安裝特定版本的express
npm i express@4.17.13.在根目錄中新建app.js作為 整個項目的入口文件,并初始化如下:
// 導(dǎo)入 express 模塊 const express = require('express')// 創(chuàng)建 express 的服務(wù)器實例 const app = express()// 調(diào)用 app.listen 方法,指定端口號并啟動web服務(wù)器 app.listen(8080, () => {console.log('api server running at http://127.0.0.1:8080') })1.2 配置cors跨域
1.安裝cors
npm i cors@2.8.52.在app.js中導(dǎo)入并配置cors中間件
//導(dǎo)入并配置cors中間件 const cors = require('cors') //注冊為全局可用的中間件 app.use(cors())1.3配置解析表單數(shù)據(jù)
1.配置解析表單數(shù)據(jù)的中間件
app.use(express.urlencoded({extended:false}))1.4初始化路由
1.新建router文件夾,用來存放路由模塊。
2.在根目錄中,新建router_handler文件夾,用來存放所有路由處理函數(shù)模塊。
初始化路由模塊:
在app.js中導(dǎo)入用戶路由模塊
//導(dǎo)入并注冊路由模塊 const userRouter = require('./router/user') app.use('./api', userRouter)1.5抽離用戶路由模塊中的處理函數(shù)
在router_handle/user.js中,使用exports對象,分別向外暴露如下兩個路由處理函數(shù)
//注冊處理函數(shù) exports.regUser = (req, res) => {res.send('reguser ok') } //登錄處理函數(shù) exports.login = (req, res) => {res.send('login ok') }并且在router/user.js中進(jìn)行調(diào)用
//導(dǎo)入用戶路由處理函數(shù)對應(yīng)的模塊 const user_handler = require('../router_handler/user')//注冊新用戶 router.post('/reguser', user_handler.regUser)//登錄 router.post('/login', user_handler.login)2.注冊登錄
2.1新建ev_user表
1.在my_db_01中新建ev_user表
2.安裝配置mysql模塊
在根目錄中新建/db/index.js文件,在此定義模板中創(chuàng)建數(shù)據(jù)庫的連接對象
//導(dǎo)入mysql模塊 const mysql = require('mysql')//創(chuàng)建數(shù)據(jù)庫連接對象 const db = mysql.createPool({host: '127.0.0.1',user: 'root',password: 'admin',database: 'my_db_01'})//向外共享db數(shù)據(jù)庫連接對象 module.exports = db2.3注冊
2.3.1檢測表單數(shù)據(jù)是否合法
//注冊處理函數(shù) exports.regUser = (req, res) => {//獲取客戶端提交到服務(wù)器的用戶信息const userinfo = req.body//對表單數(shù)據(jù)進(jìn)行合法校驗(為空)if (!userinfo.username || !userinfo.password)return res.send({ status: 1, message: '用戶名或密碼不合法' })console.log(userinfo);res.send('reguser ok')}2.3.2檢測用戶名是否被占用
1.導(dǎo)入數(shù)據(jù)庫操作模塊
//導(dǎo)入數(shù)據(jù)庫操作模塊 const db = require('../db/index')2.SQL語句(用戶名是否被占用)
//定義SQL語句,查詢用戶名是否被占用 const sqlStr = 'select * from ev_users where username=?' db.query(sqlStr, userinfo.username, (err, results) => {//執(zhí)行SQL語句失敗if (err) {return res.send({ status: 1, message: err.message })}//判斷用戶名是否被占用if (results.length > 0) {return results.send({ status: 1, message: '用戶名被占用 ' })}//用戶名可以使用 })2.3.3 對密碼進(jìn)行加密處理
加密中后,無法被逆向破解
同一明文密碼多次加密,得到的加密結(jié)果各不相同,保證了安全性
安裝:
導(dǎo)入
//導(dǎo)入加密bcryptjs const bcrypt=require('bcryptjs')確認(rèn)用戶名之后,對密碼進(jìn)行加密處理
userinfo.password = bcrypt.hashSync(userinfo.password, 10)加密前后效果對比:
2.3.4插入新用戶
代碼:
//定義插入新用戶的插入語句const sql = 'insert into ev_users set ?'//調(diào)用db.query()執(zhí)行SQL語句db.query(sql, { username: userinfo.username, password: userinfo.password }, (err, results) => {//判斷SQL語句是否執(zhí)行成功if (err)return res.send({ status: 1, message: err.message })//判斷影響行數(shù)是否為1if (results.affectedRows !== 1)return res.send({ status: 1, message: '注冊新用戶失敗,請稍后再試' })//注冊用戶成功res.send({ status: 0, message: '注冊成功!' })})遇到問題:
Field ‘id’ doesn’t have a default value
解決方案:
原因是因為mysql的中沒有將主鍵設(shè)置為自增,所以在增加元素時獲取生成主鍵時出現(xiàn)異常,打開Navicat,點(diǎn)擊表右鍵->設(shè)計表 到下面的頁面 選擇自動遞增,保存即可
解決后成功注冊
2.4 優(yōu)化res.send()代碼
因為在處理函數(shù)中,需要多次調(diào)用res.send()向客戶端響應(yīng)處理失敗的結(jié)果,于是可以手動封裝一個res.cc函數(shù)
1.在app.js入口文件中,所有路由之前,聲明一個全局中間件,為res 對象掛在一個res.cc()函數(shù)
2.5優(yōu)化表單驗證
1.安裝@hapi/joi包,為表單中攜帶的每個數(shù)據(jù)項,定義驗證規(guī)則
npm install joi2.安裝@escook/express-joi中間件,實現(xiàn)自動對表單數(shù)據(jù)進(jìn)行驗證的功能
npm i @escook/express-joi3.新建/schema/user.js用戶信息驗證規(guī)則模塊,并初始化代碼如下
//導(dǎo)入定義驗證規(guī)則的包 const joi = require('@hapi/joi')//定義用戶名和密碼的驗證規(guī)則 const username = joi.string().alphanum().min(1).max(10).required() const password = joi.string().pattern(/^[\S]{6,12}$/).required()//定義驗證注冊和登錄表單數(shù)據(jù)的規(guī)則對象 exports.reg_login_schema = {body: {username,password} }2.6登錄
1.檢驗表單數(shù)據(jù)是否合法
2.根據(jù)用戶名查詢用戶的數(shù)據(jù)
3.判斷用戶輸入的密碼是否正確
4.生成JWT的token字符串
2.6.1檢驗用戶提交的表單數(shù)據(jù)是否正確
注冊用戶和登錄使用的是同一種校驗規(guī)則:
//登錄處理函數(shù) exports.login = (req, res) => {//接收表單數(shù)據(jù)const userinfo = req.body//定義SQL數(shù)據(jù)const sql = 'select * from ev_users where username=?'//執(zhí)行SQL語句,根據(jù)用戶名查詢用戶的信息db.query(sql, userinfo.username, (err, results) => {//執(zhí)行SQL語句失敗if (err) return res.cc(err)//執(zhí)行SQL語句成功,但是獲取數(shù)據(jù)條數(shù)不是1if (results.length !== 1)return res.cc('登錄失敗')//判斷密碼是否正確})res.end('login ok') }2.6.3服務(wù)器端生成jwt字符串
1.通過ES6高級語法,快速剔除用戶的密碼和頭像的值
//在服務(wù)器端生成Token的字符串,后面跟進(jìn)的兩項被踢出const user = {...results[0] ,password:'',user_pic:''}console.log(user);2.運(yùn)行如下的命令,安裝Token字符串的包
npm i jsonwebtoken@8.5.13.在/router_handler/user.js導(dǎo)入這個包
//導(dǎo)入生成Token的包 const jwt = require('jsonwebtoken')4.在根目錄新建文件夾config.js,寫入下面代碼。建議秘鑰有效時間寫得久一點(diǎn),不然很快就失效了,需要重新生成,比較麻煩。
///這是一個全局的配置文件module.exports = {//加密和解密Token的秘鑰jwtSecretKey: 'itheima',//token的有效期expiresIn: '100h' }5.在路由函數(shù)管理文件/router_handler/user.js中導(dǎo)入
//導(dǎo)入全局的配置文件 const config = require('../config')6.把用戶信息對象加密成Token字符串
//對用戶的信息進(jìn)行加密,生成Token字符串const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: config.expiresIn })// //調(diào)用res.send()將Token響應(yīng)給客戶端// console.log(tokenStr);res.send({status: 0,message: '登錄成功',token: 'Bearer ' + tokenStr})用戶登錄成功就會生成token字符串
2.7配置token中間件
1.安裝Token中間件
npm i express-jwt@5.3.32.注冊中間件,在app.js入口文件中注冊路由之前,配置解析Token的中間件
//一定在路由之前配置解析Token中間件 const expressJWT = require('express-jwt') const config = require('./config') app.use(expressJWT({ secret: config.jwtSecretKey }).unless({ path: [/^\/api/] }))//導(dǎo)入并注冊路由模塊 const userRouter = require('./router/user')3.在app.js入口文件中的錯誤中間件里面,捕獲并且處理Token認(rèn)證失敗后的錯誤
//定義錯誤級別中間件 app.use((err, req, res, next) => {//驗證失敗導(dǎo)致的錯誤if (err instanceof Joi.ValidationError) return res.cc(err)//身份認(rèn)證失敗后的錯誤if (err.name === 'UnauthorizedError') return res.cc('身份認(rèn)證失敗')//未知的錯誤return res.cc(err) })如果出現(xiàn)報錯情況: jwt expired
解決方案:重新進(jìn)行登錄操作,會生成新的token字符串作為請求頭信息,以這個新的請求頭發(fā)送請求,可以生效
3.個人中心
3.1獲取用戶信息
1.初始化路由模塊
2.初始化路由處理函數(shù)模塊
3.獲取用戶的基本信息
3.1.1初始化路由模塊
1.創(chuàng)建/router/userinfo.js模塊
//導(dǎo)入express const express = require('express')//創(chuàng)建路由對象 const router = express.Router()//掛載路由//獲取用戶的基本信息 router.get('/userinfo', (req, res) => {res.send('ok')})//向外共享路由對象 module.exports = router2.在app.js入口文件中導(dǎo)入個人中心路由
//導(dǎo)入并使用用戶信息的路由模塊 const userinfoRouter = require('./router/userinfo') app.use('/my', userinfoRouter)由于個人信息這個接口設(shè)置了權(quán)限,需要在請求頭里面加入一個認(rèn)證的字段,value里面是前面生成的token字段
路由成功運(yùn)行!
3.1.2初始化路由處理函數(shù)模塊
1.在/router_handler新建文件userinfo.js路由處理函數(shù)模塊,并初始化
exports.getUserInfo = (req, res) => {res.send('ok') }2./router/userinfo.js代碼
//導(dǎo)入express const express = require('express')//創(chuàng)建路由對象 const router = express.Router()//掛載路由//導(dǎo)入路由處理函數(shù)模塊 const userinfo_handler = require('../router_handler/userinfo')//獲取用戶的基本信息 router.get('/userinfo', userinfo_handler.getUserInfo)//向外共享路由對象 module.exports = router3.1.3獲取用戶的基本信息
1.導(dǎo)入數(shù)據(jù)庫
const db = require('../db/index')2.定義SQL語句
//根據(jù)用戶的id,查詢用戶的基本信息//防止用戶的密碼泄漏,需要排除password字段 const sql = 'select id,username,nickname,email,user_pic from ev_users where id=?'3.調(diào)用db.qurey()執(zhí)行語句
//定義SQL語句//根據(jù)用戶的id,查詢用戶的基本信息//防止用戶的密碼泄漏,需要排除password字段const sql = 'select id,username,nickname,email,user_pic from ev_users where id=?'//調(diào)用db.query()執(zhí)行SQL語句db.query(sql, req.user.id, (err, results) => {//執(zhí)行SQL語句失敗if (err) return res.cc(err)//執(zhí)行SQL語句成功,但是查詢的結(jié)果可能為空if (results.length !== 1) return res.cc('獲取用戶信息失敗')//用戶信息獲取成功res.send({ status: 0, message: '獲取用戶信息成功', data: results[0] })})3.2更新用戶的基本信息
1.定義路由和處理函數(shù)
2.驗證表單數(shù)據(jù)
3.實現(xiàn)更新用戶基本信息的功能
3.2.1
1.在/router/userinfo.js文件中,新增更新用戶信息的接口
router.post('/userinfo', userinfo_handler.updateUserInfo)2.在/router_handler/userinfo.js文件中,定義向外共享
exports.updateUserInfo = (req, res) => {res.send('ok') }3.2.2驗證表單數(shù)據(jù)
1.在/schema/user.js驗證規(guī)則模塊中,定義id,nickname,email的驗證規(guī)則
const id = joi.number().integer().min().required() const nickname = joi.string().required() const email = joi.string().email().required()2.向外共享
exports.update_userinfo_schema = {body: {id,nickname,email} }3.在router/userinfo.js模塊中,導(dǎo)入驗證數(shù)據(jù)合法性的中間件
//導(dǎo)入驗證數(shù)據(jù)的中間件 const expressJoi = require('@escook/express-joi')//2.導(dǎo)入需要的驗證規(guī)則 const { update_userinfo_schema } = require('../schema/user') router.post('/userinfo', expressJoi(update_userinfo_schema), userinfo_handler.updateUserInfo)3.2.3實現(xiàn)更新用戶基本信息的功能
修改router_handler/userinfo.js代碼
//更新用戶基本信息的處理函數(shù) exports.updateUserInfo = (req, res) => {//定義執(zhí)行的SQL語句const sql = 'update ev_users set ? where id=?'//調(diào)用db.query執(zhí)行SQL語句并傳遞參數(shù)db.query(sql, [req.body, req.body.id], (err, results) => {//執(zhí)行SQL語句失敗遼if (err) return res.cc(err)//執(zhí)行SQL語句成功,但是影響行數(shù)不等于1if (results.affectedRows !== 1) return res.cc('更新用戶基本信息失敗')//成功res.cc('更新用戶信息成功', 0)}) }
3.3重置密碼
1.定義路由和處理函數(shù)
2.驗證表單數(shù)據(jù)
3.實現(xiàn)重置密碼的功能
3.3.1定義路由和處理函數(shù)
1.在/router/userinfo.js模塊中,新增重置密碼的路由
router.post('/updatepwd', userinfo_handler.updatePassword)2.在/router_handler/userinfo.js模塊,定義并向外共享重置密碼的路由處理函數(shù)
exports.updatePassword = (req, res) => {res.send('ok') }3.3.2驗證表單數(shù)據(jù)
1.在/schema/user.js模塊,使用exports向外共享
exports.update_password_schema = {body: {oldPwd: password,//joi.ref 表示新舊一致//2.joi.not表示newPwd的值不能等于舊密碼的值//3..concat表示合并新的和舊的密碼驗證規(guī)則newPwd: joi.not(joi.ref('oldPwd')).concat(password)} }2.在router/userinfo.js
//導(dǎo)入需要的驗證規(guī)則對象 const { update_userinfo_schema, update_password_schema } = require('../schema/user') router.post('/updatepwd', expressJoi(update_password_schema), userinfo_handler.updatePassword)3.3.3重置密碼的功能
1.根據(jù)id查詢用戶是否存在
//更新用戶密碼的處理函數(shù) exports.updatePassword = (req, res) => {//根據(jù)id查詢用戶的信息const sql = 'select * from ev_users where id=?'//執(zhí)行根據(jù)id查詢用戶信息的SQL語句db.query(sql, req.user.id, (err, results) => {//執(zhí)行SQL語句失敗遼if (err) return res.cc(err)//執(zhí)行SQL語句成功,但是影響行數(shù)不等于1if (results.length !== 1) return res.cc('更新用戶基本信息失敗')//成功res.cc('ok')})}2.判斷提交的舊密碼是否正確
const compareResult = bcrypt.compareSync(req.body.oldPwd, results[0].password)if (compareResult) return res.cc('舊密碼錯誤')3.對新密碼進(jìn)行bcrypt加密之后,更新到數(shù)據(jù)庫中
//更新數(shù)據(jù)庫中的密碼//定義更新密碼的SQL語句const sql = 'update ev_users set password=? where id=?'//對新密碼進(jìn)行加密處理const newPwd = bcrypt.hashSync(req.body.newPwd, 10)//調(diào)用db.query()執(zhí)行SQL語句db.query(sql, [newPwd, req.user.id], (err, results) => {//執(zhí)行SQL語句失敗遼if (err) return res.cc(err)//執(zhí)行SQL語句成功,但是影響行數(shù)不等于1if (results.affectedRows !== 1) return res.cc('更新用戶基本信息失敗')//成功res.cc('更新用戶信息成功', 0)})總代碼:
//更新用戶密碼的處理函數(shù) exports.updatePassword = (req, res) => {//根據(jù)id查詢用戶的信息const sql = 'select * from ev_users where id=?'//執(zhí)行根據(jù)id查詢用戶信息的SQL語句db.query(sql, req.user.id, (err, results) => {//執(zhí)行SQL語句失敗遼if (err) return res.cc(err)//執(zhí)行SQL語句成功,但是影響行數(shù)不等于1if (results.length !== 1) return res.cc('更新用戶基本信息失敗')//成功//判斷密碼是否正確const compareResult = bcrypt.compareSync(req.body.oldPwd, results[0].password)if (compareResult) return res.cc('舊密碼錯誤')//更新數(shù)據(jù)庫中的密碼//定義更新密碼的SQL語句const sql = 'update ev_users set password=? where id=?'//對新密碼進(jìn)行加密處理const newPwd = bcrypt.hashSync(req.body.newPwd, 10)//調(diào)用db.query()執(zhí)行SQL語句db.query(sql, [newPwd, req.user.id], (err, results) => {//執(zhí)行SQL語句失敗遼if (err) return res.cc(err)//執(zhí)行SQL語句成功,但是影響行數(shù)不等于1if (results.affectedRows !== 1) return res.cc('更新用戶基本信息失敗')//成功res.cc('更新用戶信息成功', 0)})})}3.4 用戶頭像信息更新
3.4.1定義路由和處理函數(shù)
1.在/router/userinfo.js模塊中,新增更換用戶頭像路由
router.post('/update/avatar', userinfo_handler.updateAvatar)2./router_handler/userinfo.js模塊中,定義向外共享更新頭像的路由處理函數(shù)
//更新用戶頭像的處理函數(shù) exports.updateAvatar = (req, res) => {res.send('ok') }3.4.2驗證表單數(shù)據(jù)
1./schema/user.js,定義驗證規(guī)則如下:
const avatar = joi.string().dataUri().required() //更新頭像//驗證規(guī)則對象--更換頭像 exports.update_avatar_schema = {body: {avatar} }2./router/userinfo.js.導(dǎo)入需要的的驗證規(guī)則對象
//更換頭像驗證規(guī)則對象 const { update_avatar_schema } = require('../schema/user')//更換頭像路由 router.post('/update/avatar', expressJoi(update_avatar_schema), userinfo_handler.updateAvatar)3.4.3實現(xiàn)更新用戶頭像的功能
//更新用戶頭像的處理函數(shù) exports.updateAvatar = (req, res) => {const sql = 'update ev_users set user_pic=? where id=?'db.query(sql, [req.body.avatar, req.user.id], (err, results) => {//執(zhí)行SQL語句失敗遼if (err) return res.cc(err)//執(zhí)行SQL語句成功,但是影響行數(shù)不等于1if (results.affectedRows !== 1) return res.cc('更新頭像失敗')//成功return res.cc('更新頭像成功', 0)}) }總結(jié)
以上是生活随笔為你收集整理的Node.js实践----注册-登录-个人中心(更换密码、头像)接口实现(包含mysql数据库)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Airbnb背后周下载量数百万的Esli
- 下一篇: SQL常见的操作