1.初始化项目
1-1.创建项目
- 新建api_server文件夹,运行
npm init -y
- 安装exoress
npm i express
- 在项目中新建app.js作为整个项目的入口文件,并创建服务器
// 导入模块
const express = require('express')
// 创建服务器
const app = express()
// 启动服务器
app.listen(80,(req,res)=>{
console.log(run in localhost);
})
1-2.配置cors
- 安装cors中间件
npm i cors
- 在app.js中导入中间件
// 导入中间件
const cors = require('cors')
- 注册cors中间件
// 注册全局中间件
app.use(cors())
1-3.配置解析表单数据的中间件
- 配置解析application/x-www-form-urlencoided表单数据的中间件
- urlencoded()是express内置的方法
app.use(express.urlencoded({extended:false}))
2.初始化路由
2-1.初始化路由相关的文件夹
- 在项目根目录中,新建router文件夹,用来存放所有的路由模块
路由模块中,只存放客户端的请求与处理函数之间的映射关系
- 在项目根目录中,新建router_handler文件夹,用来存放所有的路由处理函数模块
路由处理函数模块中,专门负责存放每个路由对应的处理函数
2-2.初始化用户路由模块
- 在router文件夹中,新增use.js文件,作为用户的路由模块,初始化代码如下
const express = require('express')
const router = express.Router()
// 注册新用户
router.post('/reguser',(req,res)=>{
res.send('ok')
})
// 登录
router.post('/losin',(req,res)=>{
res.send('ok')
})
module.exports = router
- 在app.js中,导入并且使用用户路由模块
const userRouter = require('./router/user')
app.use('/api',userRouter)
2-3.抽离路由函数中的处理函数
目的 :为了保证路由模块的纯粹性,所有的路由处理函数,必须抽离到对应的路由处理函数模块中
- 在 /router_handler/user.js 中,使用 exports 对象 , 分别向外共享两个 路由处理函数
// 定义一些路由处理函数供路由模块使用
// 注册用户的处理函数
exports.reguser = (req,res)=>{
res.send('reguser ok')
}
// 登录的处理函数
exports.login = (req,res)=>{
res.send('login ok')
}
- 将/router/user.js中的代码修改为如下结构
const express = require('express')
const router = express.Router()
// 导入用户路由处理函数的模块
const userHandler = require('../router_handler/user')
// 注册新用户
router.post('/reguser',userHandler.reguser)
// 登录
router.post('/login',userHandler.login)
module.exports = router
3.注册接口
3-1.新建 ev_users 表
- 新建my_db_01 数据库中,新建 ev_users 表
3-2.安装并且配置mysql模块
- 运行如下命令,安装 mysql 模块
npm i mysql
- 在项目根目录中新建 /db/index.js 文件,在此自定义模块中创建数据库的连接对象
const mysql = require('mysql')
// 创建数据库连接对象
const db = mysql.createPool({
host:'127.0.0.1',
user:'root',
password:'qwer',
database:'my_db_01'
})
module.exports = db
3-3.注册
3-3-1.实现步骤
- 检验表单数据是否合法
- 检验用户名是否被占用
- 对密码进行加密处理
- 插入新用户
3-3-2.检验表单数据是否合法
- 判断用户名和密码是否为空
// 获取客户端提交到服务器的用户信息
const userinfo = req.body
// 对表单中的数据进行判断
if(!userinfo.username || !userinfo.password){
return res.send({
status:1,
message:'用户可或者密码不能为空'
})
}
3-3-3.检验用户名是否被占用
- 导入数据库操作模块(在router_handler下面的user.js中)
// 导入数据库操作模块
const db = require('../db/index')
- 定义sql语句
// 注册用户的处理函数
exports.reguser = (req,res)=>{
// 获取客户端提交到服务器的用户信息
const userinfo = req.body
// 对表单中的数据进行判断
if(!userinfo.username || !userinfo.password){
return res.send({
status:1,
message:'用户可或者密码不能为空'
})
}
// 定义sql语句,查询用户名是否被占用
const sqlStr = 'select * from ev_users where username=?'
db.query(sqlStr,userinfo.username,(err,results)=>{
// 执行语句失败
if(err){
return res.send({
status:1,
message:err.message
})
}
// 判断用户是否被占用
if(results.length > 0 ){
return res.send({
status:1,
message:'用户名被占用'
})
}
// 用户名可以使用,待会写
})
res.send('reguser ok')
}
- 执行sql语句并根据结果判断用户名是否被占用
// 判断用户是否被占用
if(results.length > 0 ){
return res.send({
status:1,
message:'用户名被占用'
})
}
// 用户名可以使用,待会写
3-3-4.对密码进行加密处理
为了保证密码的安全性,不建议在数据库以明文的形式保存用户密码,推荐对密码进行加密存储
在当前项目中,使用berypjs 对用户密码进行加密,优点:
1.加密之后的密码,无法被逆向破解
2.同一明文密码多次加密,得到的加密结果各不相同,保证了安全性
- 运行以下命令安装bcryptjs
npm i bcryptjs
- 在**/router handler/user.js中,导入bcrypt.js**
// 导入bcryptjs
const bcrypt = require('bcryptjs')
- 在注册用户的处理函数中,确认用户名可用之后,调用berypt .hashSync(明文密码,随机盐的长度)方法,对用户的密码进行加密处理:
在这里插入代码片
3-3-5.插入新用户
- 定义插入用户的sql语句
const sql = 'insert into ev_users set ?'
- 调用db.query()执行sql语句,插入新用户
// 定义插入新用户的sql语句
const sql = 'insert into ev_users set ?'
// 调用db.query()执行sql语句,插入新用户
db.query(sql,{username:userinfo.username,password:userinfo.password},(err,results)=>{
// 判断语句是否执行成功
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:'注册成功'})
})
})
4.代码优化
4-1.优化res.send()代码
在处理函数中,需要多次调用res. send()向客户端响应处理失败的结果, 为了简化代码,可以手动封装一个res.cw()函数
- 在app.js 中,所有路由之前,声明一一个全局中间件,为res对象挂载-一个res.cc() 函数:
// 代码优化,在路由前面封装res.cw函数
app.use((req,res,next)=>{
//status 的值默认为1 ,表示注册失败
//err 的值可能是个错误对象也可能是个字符串
res.cw = (err,status = 1)=>{
res.send({
status,
message:err instanceof Error ? err.message : err
})
}
next()
})
4-2.验证规则
- 在router_handler下面的router.js中添加下面规则代码
// 3.对表单数据进行验证看是否合法
// 用户名由英文、数字组成
const userTest = /^[0-9a-zA-Z]\w{1,11}$/
// 密码由英文、数字组成
const pwdTest = /^[0-9a-zA-Z]\w{2,11}$/
// console.log(!userTest.test(userinfo.username));
// console.log(!pwdTest.test(userinfo.password));
if (!userTest.test(userinfo.username)) {
return res.cw('用户名不合法')
} else if(!pwdTest.test(userinfo.password)){
return res.cw('密码不合法')
}
5.登录接口
5-1.实现步骤
- 检测表单数据是否合法
- 根据用户查询用户的数据
- 判断用户输入的密码是否正确
- 生成JWT的token数据
5-2.检测表单数据是否合法
- 将/router/user.js中的登录路由修改如下
// 登录的处理函数
exports.login = (req, res) => {
// 1.获取客户端提交到服务器的用户信息
const userinfo1 = req.body
// 2.对表单数据进行验证看是否合法
// 用户名由英文、数字组成
const userTest = /^[0-9a-zA-Z]\w{1,11}$/
// 密码由英文、数字组成
const pwdTest = /^[0-9a-zA-Z]\w{1,11}$/
if (!userTest.test(userinfo1.username)) {
return res.cw('用户名不合法')
} else if(!pwdTest.test(userinfo1.password)){
return res.cw('密码不合法')
}
res.send('登陆成功')
}
5-3.根据用户名查询用户的数据
- 定义sql语句
// 3-1.定义sql语句
const sql = 'select * from ev_users where username:?'
- 将/router/user.js中的登录路由修改如下
// 3.根据用户名查询用户的数据
// 3-1.定义sql语句
const sql = 'select * from ev_users where username:?'
// 3-2.执行sql语句,查询用户数据
db.query(sql,userinfo.username,(err,results)=>{
// 3-3.执行sql语句失败
if(err){res.cw(err)}
// 3-4.执行sql语句成功,但是得到的数据不等于1
if(results.length != 1){
res.cw('登陆失败')
}
})
res.send('登陆成功')
5-4.判断用户输入的密码是否正确
核心实现思路:调用berypt . compareSyne(用户提交的密码,数据库中的密码)方法比较密码是否一致
返回值是布尔值(true- 致,false 不一致)
具体实现代码如下:
// 4判断用户输入的密码是否正确
//拿着用户输入的密码,和数据库中存储的密码进行对比
const conpareResult = bcrypt.compareSync(userinfo.password,results[0],password)
//如果对比的结果等于false, 则证明用户输入的密码错误
if(!conpareResult){
return res.cw('登陆失败')
}
res.send('登陆成功')
}
5-5.生成JWT的token字符串
为了不让token把生成的字符串中的密码和头像中的值返回给客户端,在生成token的时候把他们剔除 了
- 通过es6语法,快速剔除密码和头像的值
// 5-1.剔除密码和头像、
const user = {...results[0],password:'',user_pic:''}
// console.log(user);
- 运行命令安装生成token字符串的包jsonwebtoken包
npm i jsonwebtoken
- 在**/router_handler/user.js模块的头部区域,导入jsonwebtoken包**
// 导入生成token的包
const jwt = require('jsonwebtoken')
// 导入全局配置文件
const config = require('../config')
- 根目录创建config.js文件,并向外共享加密和还原token的jwtSecretKey字符串
// 这是一个全局的配置文件
module.exports = {
// 加密密钥
jwtSecretKey:'liuq520 ^_^'
}
- 将用户信息对象加密成token字符串
// 5-2.对用户信息进行加密,生成token字符串
const tokenStr = jwt.sign(user,config.jwtSecretKey,{expiresIn:'10h'})
- 将生成的token字符串响应给客户端
res.send({
status:0,
message:'登陆成功',
token:'Bearer ' + tokenStr
})
5-6.配置解析token的中间件
- 安装express-jwt中间件
npm i express-jwt@5.3.3
- 在app.js中注册路由之前,配置解析token的中间件
app.use(expressJWT({secret:config.jwtSecretKey}).unless({path:[/^\/api\]}))
- 在app.js中的错误级别中间件里面捕获并且处理token认证失败后的错误
// 定期错误级别的中间件
app.use((err,req,res,next)=>{
//验证失败导致的错误
if(err instanceof joi.ValidationError){return res.cw(err)}
// 身份认证失败后的错误
if(err.name === 'UnauthorizedError'){return res.cw('身份认证失败')}
// 未知错误
res.cw(err)
})
6.个人中心(开发获取用户的基本信息的接口)
6-1.初始路由模块
- 在router目录下创建userinfo.js路由模块,并且初始化如下代码
const express = require('express')
const router = express.Router()
// 注册新用户
router.get('/userinfo',(req,res)=>{
res.send('ok')
})
module.exports = router
- 在app.js中导入并使用个人中心的路由模块
// 导入userinfo的路由模块
const userinfoRouter = require('./router/userinfo')
app.use('/my',userinfoRouter)
6-2.初始路由处理函数的模块
- 创建/router_handler/userinfo.js路由处理函数,并且初始化如下代码
exports.getUserInfo = (req,res)=>{
res.send('ok')
}
- 修改/router/userinfo.js中的代码
const express = require('express')
const router = express.Router()
// 导入事件处理函数
const userinfo_handler = require('../router_handler/userinfo')
// 注册新用户
router.get('/userinfo',userinfo_handler.getUserInfo)
module.exports = router
6-3.获取用户的基本信息
- 导入数据库在处理函数userinfo.js中
const db = require('../db/index')
- 定义sql语句
// 定义sql语句,根据用户id,查询用户基本信息
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)=>{
// 解析成功后的token的值会挂在到req上
// 执行sql失败
if(err){
return res.send(err)
}
if(results.length !== 1){
res.send('获取用户信息失败')
}
res.send({
status:0,
message:'获取用户信息成功',
data:results[0]
})
})
7.个人中心(更新用户信息的接口)
7-1.定义路由和处理函数
- 在/router/userinfo.js 模块中,新增更新用户基本信息的路由:
// 更新用户信息的接口
router.post('/userinfo',userinfo_handler.updateUserInfo)
- 在/router. handler/userinfo.js模块中,定义并向外共享更新用户基本信息的路由处理函数:
// 1.更新用户信息
exports.updateUserInfo = (req,res)=>{
res.send('ok')
}
7-2.验证表单数据 (nickname和email)
- 直接在处理函数中的userinfo.js 的updateInfo添加代码
// 1.更新用户信息
exports.updateUserInfo = (req,res)=>{
// 1.获取客户端提交到服务器的用户信息
const userinfo = req.body
// 验证表单数据 (nickname和email)
// 昵称由英文、数字组成
const nicknameTest = /^[0-9a-zA-Z]\w{1,11}$/
// 邮箱由英文、数字组成
const emailTest = /^[0-9a-zA-Z]\w{2,11}$/
// console.log(!userTest.test(userinfo.username));
// console.log(!pwdTest.test(userinfo.password));
if (!nicknameTest.test(userinfo.nickname)) {
return res.cw('昵称不合法')
} else if(!emailTest.test(userinfo.email)){
return res.cw('邮箱不合法')
}
res.send('ok')
}
7-3.实现更新用户基本信息的功能
- 定义待执行的sql语句
// 2-1.定义sql语句
const sql = 'update ev_users set ? where id=?'
- 调用db.query()执行sql语句
// 2-2. 调用db.query()执行sql语句
db.query(sql,[req.body,req.body.id],(err,results)=>{
// 执行sql语句失败
if(err){return res.send(err)}
// z执行sql语句成功,但是行数不为1
if(results.affectedRows != 1){return res.send('修改用户信息失败')}
// 修改用户信息成功、
return res.send('修改用户信息成功')
})
8.个人中心(重置密码的接口)
8-1.定义路由和处理函数
- . 在/router/userinfo.js 模块中,新增重置密码的路由
// 3.重置密码的接口
router.post('/updatepwd',userinfo_handler.updatePassword)
- 在/router. .handler/userinfo.js模块中,定义并向外共享重置密码的路由处理函数:
// 1.重置密码
exports.updatePassword = (req,res)=>{
res.send('ok')
}
8-2.验证表单数据
// 1.重置密码
exports.updatePassword = (req,res)=>{
// 1.获取客户端提交到服务器的用户信息
const userinfo = req.body
// 验证表单数据 (nickname和email)
// 新密码由英文、数字组成
const newPwd = /^[0-9a-zA-Z]\w{1,11}$/
if (!newPwd.test(userinfo.password)) {
return res.send('新密码不合法')
}
return res.send('ok')
}
8-3.实现重置密码功能
- 定义待执行的sql语句
// 2-1.定义sql语句
const sql = 'update ev_users set ? where id=?'
- 调用db.query()执行sql语句
//2.实现更新用户密码功能
const sql = 'update ev_users set ? where id=?'
// 2-2. 调用db.query()执行sql语句
db.query(sql,[req.body,req.body.id],(err,results)=>{
// 执行sql语句失败
if(err){return res.send(err)}
// z执行sql语句成功,但是行数不为1
if(results.affectedRows != 1){return res.send('修改密码失败')}
// 修改用户信息成功、
return res.send('修改用户信息成功')
})
本文含有隐藏内容,请 开通VIP 后查看