项目结构目录如下

  • 1.初始化
    • 1.1创建项目
    • 1.2 配置cors跨域
    • 1.3配置解析表单数据
    • 1.4初始化路由
    • 1.5抽离用户路由模块中的处理函数
  • 2.注册登录
    • 2.1新建ev_user表
    • 2.3注册
      • 2.3.1检测表单数据是否合法
      • 2.3.2检测用户名是否被占用
      • 2.3.3 对密码进行加密处理
      • 2.3.4插入新用户
    • 2.4 优化res.send()代码
    • 2.5优化表单验证
    • 2.6登录
      • 2.6.1检验用户提交的表单数据是否正确
      • 2.6.3服务器端生成jwt字符串
    • 2.7配置token中间件
  • 3.个人中心
    • 3.1获取用户信息
      • 3.1.1初始化路由模块
      • 3.1.2初始化路由处理函数模块
      • 3.1.3获取用户的基本信息
    • 3.2更新用户的基本信息
      • 3.2.1
      • 3.2.2验证表单数据
      • 3.2.3实现更新用户基本信息的功能
    • 3.3重置密码
      • 3.3.1定义路由和处理函数
      • 3.3.2验证表单数据
      • 3.3.3重置密码的功能
    • 3.4 用户头像信息更新
      • 3.4.1定义路由和处理函数
      • 3.4.2验证表单数据
      • 3.4.3实现更新用户头像的功能

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 })//判断影响行数是否为1if (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语句成功,但是获取数据条数不是1if (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语句成功,但是影响行数不等于1if (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语句成功,但是影响行数不等于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.对新密码进行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语句成功,但是影响行数不等于1if (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语句成功,但是影响行数不等于1if (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语句成功,但是影响行数不等于1if (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语句成功,但是影响行数不等于1if (results.affectedRows !== 1) return res.cc('更新头像失败')//成功return res.cc('更新头像成功', 0)})
}

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

  1. 「跨域」利用node.js实践前端各种跨域方式(上)

    前言 常言道,"读万卷书,不如行万里路".技术的学习也是如此,唯有实践才能更清楚的明白原理和加深印象,因此本文会利用node.js对前端的各种跨域方式进行实践,强烈建议一步一步跟着 ...

  2. node.js在注册表删除_Node.JS 11年:时间表和重要贡献

    node.js在注册表删除 Do you know 你知道吗 Node.js在2020年5月27日已满11岁 (Node.js has turned 11 on 27th May 2020) ? Ca ...

  3. linux更换登录用户名和密码怎么办,用passwd及chage命令让用户在下次登录Linux时更换密码...

    当你在 Linux 系统中使用默认密码创建用户时,你必须强制用户在下一次登录 Linux 时更换密码,做到这一点其实不懂,目前有两种办法,就是使用 passwd 命令或者 chage 命令,以下就具体 ...

  4. 通过regedt查看计算机密码,win10系统通过注册表设置定时更换密码提醒的处理步骤...

    有关win10系统通过注册表设置定时更换密码提醒的操作方法想必大家有所耳闻.但是能够对win10系统通过注册表设置定时更换密码提醒进行实际操作的人却不多.其实解决win10系统通过注册表设置定时更换密 ...

  5. 【正版免费工具】 股票盯盘助手(简化版才154KB),无注册登录,可以悬浮任意桌面任意位置(包含任务栏)

    @[TOC]([正版免费工具] 股票盯盘助手(简化版才154KB),无注册登录,可以悬浮任意桌面任意位置(包含任务栏)) [正版免费工具] 股票盯盘助手(简化版才154KB),无注册登录,可以悬浮任意 ...

  6. 基于JAVA师大家教中心管理系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署

    基于JAVA师大家教中心管理系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署 基于JAVA师大家教中心管理系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署 本源码技术栈: ...

  7. 计算机毕业设计Java宠物寄存中心计时收费系统(源码+系统+mysql数据库+lw文档)

    计算机毕业设计Java宠物寄存中心计时收费系统(源码+系统+mysql数据库+lw文档) 计算机毕业设计Java宠物寄存中心计时收费系统(源码+系统+mysql数据库+lw文档) 本源码技术栈: 项目 ...

  8. java计算机毕业设计美容中心在线后台管理系统源码+系统+mysql数据库+lw文档

    java计算机毕业设计美容中心在线后台管理系统源码+系统+mysql数据库+lw文档 java计算机毕业设计美容中心在线后台管理系统源码+系统+mysql数据库+lw文档 本源码技术栈: 项目架构:B ...

  9. 计算机毕业设计JavaVue.js网上书城管理系统设计与实现服务端(源码+系统+mysql数据库+lw文档)

    计算机毕业设计JavaVue.js网上书城管理系统设计与实现服务端(源码+系统+mysql数据库+lw文档) 计算机毕业设计JavaVue.js网上书城管理系统设计与实现服务端(源码+系统+mysql ...

最新文章

  1. Linux 环境 搭建Git 服务器,并且修改SSH端口使用
  2. linux 如何禁用账号和解除禁用账号
  3. /usr/bin/perl^M: bad interpreter: No such file or directory
  4. 改权限 chown改用户归属_域权限维持 | 改密码我也能获取你的密码 | Hook PasswordChangeNotify 攻击...
  5. dcdc模块降额设计_模块电源应用设计的可靠性和注意事项
  6. 装饰者模式(Decorator)
  7. 【单位换算】存储单位(bit Byte KB MB GB TB PB EB ZB YB BB)时间单位(ms μs ns ps)长度单位(dm cm mm μm nm pm fm am zm ym)
  8. 种草平台--持续更新
  9. 课堂作业:首尾相连求最大子数组
  10. SGX软硬件栈(四)——桥函数
  11. Hotpot - 让使用CKettle像吃火锅一样爽
  12. WebRTC 系列2--双摄像头同时预览
  13. matlab求逆函数— inv函数
  14. java RandomAccess 遍历效率
  15. 7-1 软硬车厢交替排列 (13 分)
  16. 直方图和柱状图的区别有哪些
  17. 邵柏庆与Ace Lead Profits Ltd.在英属维尔京群岛针对和利时自动化科技有限公司提起诉讼
  18. Javascript实战——电子钟(时钟、闹钟、计时器、倒计时)
  19. Echarts之map地图隐藏港澳台等区域
  20. mstsc时报0x88错误,解决办法

热门文章

  1. RDKit|化学特征、药效团提取与2D药效团指纹计算
  2. HTML5新增内容元素和表单控件
  3. 钢琴学习:B站:时一:《周杰伦的《告白气球》真是百听不厌,5分钟教会你弹唱,一学便会》
  4. 无法创建目录或文件问题的解决办法
  5. web页面注册验证,实现从后端动态获取证码,到前端页面显示
  6. 2017年7月6号生活感悟
  7. maxthon 2 ua
  8. 1980-不存在的泳池 ZCMU
  9. 怎样用计算机求立方根的近似数,立方根教学设计人教版.doc
  10. 对于Listview未满一屏添加footerView 满了一屏幕将View固定在屏幕下方的解决方案