Node.js实践----注册-登录-个人中心(更换密码、头像)接口实现(包含mysql数据库)

6 篇文章 0 订阅
3 篇文章 0 订阅

项目结构目录如下
在这里插入图片描述

1.初始化

1.1创建项目

1.新建api_server文件,初始化包配置管理配置文件

npm init -y

2.安装特定版本的express

npm i express@4.17.1

3.在根目录中新建app.js作为 整个项目的入口文件,并初始化如下:

// 导入 express 模块
const express = require('express')
    // 创建 express 的服务器实例
const app = express()



// 调用 app.listen 方法,指定端口号并启动web服务器
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.5

2.在app.js中导入并配置cors中间件

//导入并配置cors中间件
const cors = require('cors')
//注册为全局可用的中间件
app.use(cors())

1.3配置解析表单数据

1.配置解析表单数据的中间件

app.use(express.urlencoded({extended:false}))

1.4初始化路由

1.新建router文件夹,用来存放路由模块。
2.在根目录中,新建router_handler文件夹,用来存放所有路由处理函数模块。
初始化路由模块:

const router = express.Router()
    //注册新用户
router.post('/reguser', (req, res) => {
    res.send('reguser ok')
})
//登录
router.post('/login', (req, res) => {
    res.send('login ok')
    
})
//将路由对象共享出去
module.exports=router

在app.js中导入用户路由模块

//导入并注册路由模块
const userRouter = require('./router/user')
app.use('./api', userRouter)

1.5抽离用户路由模块中的处理函数

在router_handle/user.js中,使用exports对象,分别向外暴露如下两个路由处理函数

//注册处理函数
exports.regUser = (req, res) => {
    res.send('reguser ok')
}
//登录处理函数
exports.login = (req, res) => {
    res.send('login ok')
}

并且在router/user.js中进行调用

    //导入用户路由处理函数对应的模块
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模块

npm i mysql@2.18.1

在根目录中新建/db/index.js文件,在此定义模板中创建数据库的连接对象

//导入mysql模块
const mysql = require('mysql')
    //创建数据库连接对象
const db = mysql.createPool({
        host: '127.0.0.1',
        user: 'root',
        password: 'admin',
        database: 'my_db_01'
    })
    //向外共享db数据库连接对象
module.exports = db

2.3注册

2.3.1检测表单数据是否合法

//注册处理函数
exports.regUser = (req, res) => {
        //获取客户端提交到服务器的用户信息
        const userinfo = req.body
            //对表单数据进行合法校验(为空)
        if (!userinfo.username || !userinfo.password)
            return res.send({ status: 1, message: '用户名或密码不合法' })
        console.log(userinfo);
        res.send('reguser ok')
    }

2.3.2检测用户名是否被占用

1.导入数据库操作模块

//导入数据库操作模块
const db = require('../db/index')

2.SQL语句(用户名是否被占用)

    //定义SQL语句,查询用户名是否被占用
const sqlStr = 'select * from ev_users where username=?'
db.query(sqlStr, userinfo.username, (err, results) => {
    //执行SQL语句失败
    if (err) {
        return res.send({ status: 1, message: err.message })
    }
    //判断用户名是否被占用
    if (results.length > 0) {
        return results.send({ status: 1, message: '用户名被占用 ' })
    }
    //用户名可以使用
})

2.3.3 对密码进行加密处理

加密中后,无法被逆向破解
同一明文密码多次加密,得到的加密结果各不相同,保证了安全性
安装:

npm i bcryptjs@2.4.3

导入

//导入加密bcryptjs
const bcrypt=require('bcryptjs')

确认用户名之后,对密码进行加密处理

userinfo.password = bcrypt.hashSync(userinfo.password, 10)

加密前后效果对比:
在这里插入图片描述

2.3.4插入新用户

代码:

 //定义插入新用户的插入语句
        const sql = 'insert into ev_users set ?'

        //调用db.query()执行SQL语句
        db.query(sql, { username: userinfo.username, password: userinfo.password }, (err, results) => {
            //判断SQL语句是否执行成功
            if (err)
                return res.send({ status: 1, message: err.message })
                    //判断影响行数是否为1
            if (results.affectedRows !== 1)
                return res.send({ status: 1, message: '注册新用户失败,请稍后再试' })
                    //注册用户成功
            res.send({ status: 0, message: '注册成功!' })
        })

遇到问题:
Field ‘id’ doesn’t have a default value
解决方案:
原因是因为mysql的中没有将主键设置为自增,所以在增加元素时获取生成主键时出现异常,打开Navicat,点击表右键->设计表 到下面的页面 选择自动递增,保存即可
在这里插入图片描述
解决后成功注册

在这里插入图片描述

2.4 优化res.send()代码

因为在处理函数中,需要多次调用res.send()向客户端响应处理失败的结果,于是可以手动封装一个res.cc函数
1.在app.js入口文件中,所有路由之前,声明一个全局中间件,为res 对象挂在一个res.cc()函数

    //封装res.cc函数
app.use((req, res, next) => {
    //status默认值为1,表示失败的情况
    //err的值,可能是一个错误对象,也可能是一个错误的描述字符串
    res.cc = function(err, status = 1) {
        res.send({
            status,
            message: err instanceof Error ? err.message : err,
        })
    }
    next()
})

2.5优化表单验证

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

npm install joi

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

npm i @escook/express-joi

3.新建/schema/user.js用户信息验证规则模块,并初始化代码如下

//导入定义验证规则的包
const joi = require('@hapi/joi')
    //定义用户名和密码的验证规则
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
    }
}

2.6登录

1.检验表单数据是否合法
2.根据用户名查询用户的数据
3.判断用户输入的密码是否正确
4.生成JWT的token字符串

2.6.1检验用户提交的表单数据是否正确

注册用户登录使用的是同一种校验规则:

    //登录处理函数
exports.login = (req, res) => {
    //接收表单数据
    const userinfo = req.body
        //定义SQL数据
    const sql = 'select * from ev_users where username=?'
        //执行SQL语句,根据用户名查询用户的信息
    db.query(sql, userinfo.username, (err, results) => {
        //执行SQL语句失败
        if (err) return res.cc(err)
            //执行SQL语句成功,但是获取数据条数不是1
        if (results.length !== 1)
            return res.cc('登录失败')
                //判断密码是否正确

    })
    res.end('login ok')
}

在这里插入图片描述

2.6.3服务器端生成jwt字符串

1.通过ES6高级语法,快速剔除用户的密码和头像的值

  //在服务器端生成Token的字符串,后面跟进的两项被踢出
        const user = {...results[0] ,password:'',user_pic:''}
        console.log(user);

2.运行如下的命令,安装Token字符串的包

npm i jsonwebtoken@8.5.1

3.在/router_handler/user.js导入这个包

    //导入生成Token的包
const jwt = require('jsonwebtoken')

4.在根目录新建文件夹config.js,写入下面代码。建议秘钥有效时间写得久一点,不然很快就失效了,需要重新生成,比较麻烦。

///这是一个全局的配置文件

module.exports = {
    //加密和解密Token的秘钥
    jwtSecretKey: 'itheima',
    //token的有效期
    expiresIn: '100h'
}

5.在路由函数管理文件/router_handler/user.js中导入

    //导入全局的配置文件
const config = require('../config')

6.把用户信息对象加密成Token字符串

      //对用户的信息进行加密,生成Token字符串
        const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: config.expiresIn })
        //     //调用res.send()将Token响应给客户端
        // console.log(tokenStr);
        res.send({
            status: 0,
            message: '登录成功',
            token: 'Bearer ' + tokenStr
        })

用户登录成功就会生成token字符串
在这里插入图片描述

2.7配置token中间件

1.安装Token中间件

 npm i express-jwt@5.3.3

2.注册中间件,在app.js入口文件中注册路由之前,配置解析Token的中间件

    //一定在路由之前配置解析Token中间件
const expressJWT = require('express-jwt')
const config = require('./config')
app.use(expressJWT({ secret: config.jwtSecretKey }).unless({ path: [/^\/api/] }))
    //导入并注册路由模块
const userRouter = require('./router/user')

3.在app.js入口文件中的错误中间件里面,捕获并且处理Token认证失败后的错误

    //定义错误级别中间件
app.use((err, req, res, next) => {
    //验证失败导致的错误
    if (err instanceof Joi.ValidationError) return res.cc(err)
        //身份认证失败后的错误
    if (err.name === 'UnauthorizedError') return res.cc('身份认证失败')
        //未知的错误
    return res.cc(err)
})

如果出现报错情况: jwt expired
解决方案:重新进行登录操作,会生成新的token字符串作为请求头信息,以这个新的请求头发送请求,可以生效
在这里插入图片描述

3.个人中心

3.1获取用户信息

1.初始化路由模块
2.初始化路由处理函数模块
3.获取用户的基本信息

3.1.1初始化路由模块

1.创建/router/userinfo.js模块

//导入express
const express = require('express')
    //创建路由对象
const router = express.Router()
    //挂载路由
    //获取用户的基本信息
router.get('/userinfo', (req, res) => {
        res.send('ok')
    })
    //向外共享路由对象
module.exports = router

2.在app.js入口文件中导入个人中心路由

//导入并使用用户信息的路由模块
const userinfoRouter = require('./router/userinfo')
app.use('/my', userinfoRouter)

由于个人信息这个接口设置了权限,需要在请求头里面加入一个认证的字段,value里面是前面生成的token字段
在这里插入图片描述
路由成功运行!

3.1.2初始化路由处理函数模块

1.在/router_handler新建文件userinfo.js路由处理函数模块,并初始化

exports.getUserInfo = (req, res) => {
    res.send('ok')
}

2./router/userinfo.js代码

//导入express
const express = require('express')
    //创建路由对象
const router = express.Router()
    //挂载路由

//导入路由处理函数模块
const userinfo_handler = require('../router_handler/userinfo')
    //获取用户的基本信息
router.get('/userinfo', userinfo_handler.getUserInfo)
    //向外共享路由对象
module.exports = router

3.1.3获取用户的基本信息

1.导入数据库

const db = require('../db/index')

2.定义SQL语句

//根据用户的id,查询用户的基本信息
    //防止用户的密码泄漏,需要排除password字段
const sql = 'select id,username,nickname,email,user_pic from ev_users where id=?'

3.调用db.qurey()执行语句

   //定义SQL语句
    //根据用户的id,查询用户的基本信息
    //防止用户的密码泄漏,需要排除password字段
    const sql = 'select id,username,nickname,email,user_pic from ev_users where id=?'
        //调用db.query()执行SQL语句
    db.query(sql, req.user.id, (err, results) => {
        //执行SQL语句失败
        if (err) return res.cc(err)
            //执行SQL语句成功,但是查询的结果可能为空
        if (results.length !== 1) return res.cc('获取用户信息失败')
            //用户信息获取成功
        res.send({ status: 0, message: '获取用户信息成功', data: results[0] })
    })

在这里插入图片描述

3.2更新用户的基本信息

1.定义路由和处理函数
2.验证表单数据
3.实现更新用户基本信息的功能

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验证表单数据

1.在/schema/user.js验证规则模块中,定义id,nickname,email的验证规则

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模块中,导入验证数据合法性的中间件

//导入验证数据的中间件
const expressJoi = require('@escook/express-joi')
    //2.导入需要的验证规则
const { update_userinfo_schema } = require('../schema/user')
router.post('/userinfo', expressJoi(update_userinfo_schema), userinfo_handler.updateUserInfo)

在这里插入图片描述

3.2.3实现更新用户基本信息的功能

修改router_handler/userinfo.js代码

   //更新用户基本信息的处理函数
exports.updateUserInfo = (req, res) => {
    //定义执行的SQL语句
    const sql = 'update ev_users set ? where id=?'
        //调用db.query执行SQL语句并传递参数
    db.query(sql, [req.body, req.body.id], (err, results) => {
        //执行SQL语句失败辽
        if (err) return res.cc(err)
            //执行SQL语句成功,但是影响行数不等于1
        if (results.affectedRows !== 1) return res.cc('更新用户基本信息失败')
            //成功
        res.cc('更新用户信息成功', 0)
    })
}

在这里插入图片描述
在这里插入图片描述

3.3重置密码

1.定义路由和处理函数
2.验证表单数据
3.实现重置密码的功能

3.3.1定义路由和处理函数

1.在/router/userinfo.js模块中,新增重置密码的路由

router.post('/updatepwd', userinfo_handler.updatePassword)

2.在/router_handler/userinfo.js模块,定义并向外共享重置密码的路由处理函数

exports.updatePassword = (req, res) => {
    res.send('ok')
}

3.3.2验证表单数据

1.在/schema/user.js模块,使用exports向外共享

exports.update_password_schema = {
    body: {
        oldPwd: password,
        //joi.ref 表示新旧一致
        //2.joi.not表示newPwd的值不能等于旧密码的值
        //3..concat表示合并新的和旧的密码验证规则
        newPwd: joi.not(joi.ref('oldPwd')).concat(password)
    }
}

2.在router/userinfo.js

//导入需要的验证规则对象
const { update_userinfo_schema, update_password_schema } = require('../schema/user')
router.post('/updatepwd', expressJoi(update_password_schema), userinfo_handler.updatePassword)

3.3.3重置密码的功能

1.根据id查询用户是否存在

    //更新用户密码的处理函数
exports.updatePassword = (req, res) => {
    //根据id查询用户的信息
    const sql = 'select * from ev_users where id=?'
        //执行根据id查询用户信息的SQL语句
    db.query(sql, req.user.id, (err, results) => {
        //执行SQL语句失败辽
        if (err) return res.cc(err)
            //执行SQL语句成功,但是影响行数不等于1
        if (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.对新密码进行bcrypt加密之后,更新到数据库中

  //更新数据库中的密码
        //定义更新密码的SQL语句
        const sql = 'update ev_users set password=? where id=?'
            //对新密码进行加密处理
        const newPwd = bcrypt.hashSync(req.body.newPwd, 10)
            //调用db.query()执行SQL语句
        db.query(sql, [newPwd, req.user.id], (err, results) => {
            //执行SQL语句失败辽
            if (err) return res.cc(err)
                //执行SQL语句成功,但是影响行数不等于1
            if (results.affectedRows !== 1) return res.cc('更新用户基本信息失败')
                //成功
            res.cc('更新用户信息成功', 0)
        })

总代码:

    //更新用户密码的处理函数
exports.updatePassword = (req, res) => {
    //根据id查询用户的信息
    const sql = 'select * from ev_users where id=?'
        //执行根据id查询用户信息的SQL语句
    db.query(sql, req.user.id, (err, results) => {
        //执行SQL语句失败辽
        if (err) return res.cc(err)
            //执行SQL语句成功,但是影响行数不等于1
        if (results.length !== 1) return res.cc('更新用户基本信息失败')
            //成功
            //判断密码是否正确
        const compareResult = bcrypt.compareSync(req.body.oldPwd, results[0].password)
        if (compareResult) return res.cc('旧密码错误')

        //更新数据库中的密码
        //定义更新密码的SQL语句
        const sql = 'update ev_users set password=? where id=?'
            //对新密码进行加密处理
        const newPwd = bcrypt.hashSync(req.body.newPwd, 10)
            //调用db.query()执行SQL语句
        db.query(sql, [newPwd, req.user.id], (err, results) => {
            //执行SQL语句失败辽
            if (err) return res.cc(err)
                //执行SQL语句成功,但是影响行数不等于1
            if (results.affectedRows !== 1) return res.cc('更新用户基本信息失败')
                //成功
            res.cc('更新用户信息成功', 0)
        })

    })

}

在这里插入图片描述

3.4 用户头像信息更新

3.4.1定义路由和处理函数

1.在/router/userinfo.js模块中,新增更换用户头像路由

router.post('/update/avatar', userinfo_handler.updateAvatar)

2./router_handler/userinfo.js模块中,定义向外共享更新头像的路由处理函数

    //更新用户头像的处理函数
exports.updateAvatar = (req, res) => {
    res.send('ok')
}

3.4.2验证表单数据

1./schema/user.js,定义验证规则如下:

const avatar = joi.string().dataUri().required() //更新头像

    //验证规则对象--更换头像
exports.update_avatar_schema = {
    body: {
        avatar
    }
}

2./router/userinfo.js.导入需要的的验证规则对象

//更换头像验证规则对象
const { update_avatar_schema } = require('../schema/user')
    //更换头像路由
router.post('/update/avatar', expressJoi(update_avatar_schema), userinfo_handler.updateAvatar)

3.4.3实现更新用户头像的功能

    //更新用户头像的处理函数
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) => {
        //执行SQL语句失败辽
        if (err) return res.cc(err)
            //执行SQL语句成功,但是影响行数不等于1
        if (results.affectedRows !== 1) return res.cc('更新头像失败')
            //成功
        return res.cc('更新头像成功', 0)
    })
}

在这里插入图片描述

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值