1.初始化
1.1 创建项目
1创建一个文件夹并且初始化一个包管理配置文件
npm init -y
2.运行如下命令安装express包
yarn add express
3.在项目根目录中新建 app.js作为整个项目的入口文件,并初始化如下的代码:
//导入express模块
const express = require('express')
//创建express的服务器实例
const app = express()
//调用app.listen方法,指定端口号并启动web服务器
app.listen(80,function (){
console.log('api server running at http://127.0.0.1')
})
1.2.配置跨域
1.运行如下命令,安装cors中间件
yarn add cors
2.在app.js中导入并配置cors中间件
//导入cors中间件
const cors = require('cors')
//将cors注册为全局中间件
app.use(cors())
1.3 配置解析表单数据的中间件
1.通过如下的代码,配置解析application/x-www-form-urlencoded格式的表单数据的中间件
app.use(express.urlencoded({extended:false }))
1.4 初始化路由相关的文件夹
1.在项目根目录中,新建router文件夹,用来存放所有的路由模块
路由模块中,只存放客户端的情求与处理函数之间的映射关系
2.在项目根目录中,新建router_handler文件夹,用来存放所有的路由处理函数模块
路由处理函数模块中,专引门负责存放每个路由对应的处理函数
1.5 初始化用户路由模块
1.在router文件夹中,新建user.js文件,作为用户的路由模块,并初始化代码如下:
const express = require('express')
//创建路由对象
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
2.在app.js中,导入并使用用户路由模块
//导入并注册用户路由摸块
const userRouter = require('./router/user')
app.use('/api',userRouter)
1.6 抽离用户路由摸块中的处理函数
目的:为了保证路由模块的纯粹性,所有的路由处理函数,必须抽离到对应的路由处理函数模块中
1.在/router_hander/user.js中,使用exports对象,分别向外共享如下两个路由处理函数:
/**
*在这里定义和用户相关的路由处理函数,供/router/user.js模块进行调用
*/
//注册用户的处理函数
exports.reguser = (req, res) => {
res.send('reguser OK')
}
//登录的处理函数
exports.login = (req, res) => {
res.send('login OK')
}
2.将/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
2.登录注册
2.1 新建用户表 my_db数据库中新建ev_users表,根据自己需求创建
示例,我的表中包含用户id,username,password,nickname,email,user_pic这几个属性
2.2 安装并配置mysql模块
在项目中,需要安装并配置mysq1这个第三方模块,来连接和操作MySQL数据库
1. 运行如下命令,安装mysql模块
yarn add mysql
2.在项目根目录中新建 /db/index.js 文件,在此自定义模块中创建数据库的连接对象
//导入mysq1模块
const mysql = require('mysql')
//创建数据库连接对象
const db = mysql.createPool({
host: '127.0.0.1',
user: 'root',
password: 'root',
database: 'my_db',
})
//向外共享db数据库连接对象
module.exports = db
2.3 注册
2.3.0 实现步骤
1.检测表单数据是否合法
2.检测用户名是否被占用
3.对密码进行加密处理
4.插入新用户
2.3.1 检测表单数据是否合法
//接收表单数据
const userinfo = req.body
//判断数据是否合法
if (!userinfo.username || !userinfo.password){
return res.send({status:1,message:'用户名或密码不能为空!'})
}
2.3.2检测用户名是否被占用
1.导入数据库操作模块:
const db = require('../db/index')
2. 定义sql 语句
const sql = 'select from ev_users where username=?'
3.执行SQL语句并根据结果判断用户名是否被占用:
db.query(sql,[userinfo.username],function (err,results){
//执行SQL语句失败
if (err){
return res.send({status:1,message:err.message})
}
//用户名被占用
if (results.length>0){
return res.send({status:1,message:'用户名被占用,请更换其他用户名!'})
}
//TOD0:用户名可用,继续后续流程..·
})
2.3.3 对密码进行加密处理
1. 下载加密包
yarn add bcryptjs@2.4.3
2.在/router_.handler./user.js中,导入bcryptjs
const bcrypt = require('bcryptjs')
3.在注册用户的处理函数中,确认用户名可用之后,调用bcrypt.hashSync(明文密码,随机盐的长度)方法,对用户的密码进行加密处理:
// 对用户的密码,进行bcrype加密,返回值是加密之后的密码字符串
userinfo.password=bcrypt.hashSync(userinfo.password,10)
2.3.4 .插入新用户
1.编写插入语句
// 定义插入用户的sql语句
const sql='insert into ev_user set ?'
2. 调用db.query()方法,执行sql语句,插入新用户
db.query(sql,req.body,(err,results)=>{
if(err) return res.send({status:1,message:err.message})
// 判断影响行数来确定是否插入成功
if(results.affectedRows!==1) return res.send({status:1,message:'注册用户失败,请稍后重试'})
// 注册用户成功
res.send({status:0,message:'注册成功'})
})
2.4 封装res.cc函数
// 一定要在路由之前,封装res.cc函数
app.use((req,res,next)=>{
// status值默认为1,表示失败的情况下
// err的值,可能是一个错误对象,也可能是一个错误的描述字符串
res.cc=(err,status=1)=>{
res.send({
status,
message:err instanceof Error ? err.message : err
})
}
next()
})
2.5 优化表单数据验证
表单验证的原则:前端验证为辅后端验证为主,后端永远不要相信前端提交过来的任何内容
在实际开发中,前后端都需要对表单的数据进行合法性的验证,而且,后端做为数据合法性验证的最后一个关口,在拦截非法数据方面,起到了至关重要的作用。
单纯的使用if...else....的形式对数据合法性进行验证,效率低下、出错率高、维护性差。因此,推荐使用第三方数据验证模块,来降低出错率、提高验证的效率与可维护性,让后端程序员把更多的精力放在核心业务逻辑的处理上。
1.安装joi包,为表单中携带的每项数据定义规则
yarn add joi
2.安装@escook/express-joi 中间件,来实现自动对表单数据进行验证的功能
yarn add @escook/express-joi
3.新建/schema/user.js用户信息验证模块,并初始化代码
// 引入定义规则的包
const joi=require('joi')
const user= joi.string().alphanum().min(3).max(13).required()
const password= joi.string().pattern(/^[\S]{6,15}$/).required()
const repassword=joi.ref('password')
exports.reg_login_schema={
body:{
user,
password,
repassword
}
}
4.在app.js中挂载全局错误中间件
const joi=require('joi')
// 在所有路由之后挂载
// 定义错误级中间件
app.use( (err, req, res, next)=> {
// 4.1 Joi 参数校验失败
if (err instanceof joi.ValidationError) return res.cc(err)
// 4.2 未知错误
res.cc(err)
})
5. 在注册路由中使用验证规则
// 引入验证表单数据的中间件
const expressJoi=require('@escook/express-joi')
//注册新用户
router.post('/reguser',expressJoi(reg_login_schema),userHandler.reguser)
2.6 登录
2.6.0 实现步骤
1.检测表单数据是否合法
2.根据用户名查询用户的数据
3.判断用户输入的密码是否正确
4.生成JWT的Token字符串
2.6.1检测登录表单的数据是否合法
1.将/router/user.js中登录的路由代码修改如下:
//登录
router.post('/login',expressJoi(reg_login_schema),userHandler.login)
2.6.2 根据用户名查询用户的数据
在router_handler/user.js中
1.接收表单数据
const userinfo = req.body
2.定义sql语句
const sql= 'select * from app_user where username=?'
3.执行sql语句查询用户数据
//登录的处理函数
exports.login = (req, res) => {
const userinfo=req.body
const sql='select * from app_user where user =?'
db.query(sql,userinfo.user,(err,results)=>{
if(err) return res.cc(err)
if(results.length!==1) return res.cc('登录失败')
// 都通过此时在判断密码是否正确
res.send('login OK')
})
}
2.6.3 判断用户输入密码是否正确
核心实现思路:调用bcrypt.compareSync(用户提交的密码,数据库中的密码)方法比较密码是否一致
返回值是布尔值(true一致、false不一致)
具体实现代码如下
//拿着用户输入的密码,和数据库中存储的密码进行对比
const compareResult= bcrypt.compareSync(userinfo.password,results[0].password)
//如果对比的结果等于fa1se,则证明用户输入的密码错误
if (!compareResult){
return res.cc('登录失败!')
}
//TOD0:登录成功,生成Token字符串
2.6.4 生成JWT的token字符串
核心注意点:在生成token字符串的时候一定要剔除密码和头像
1.通过es6的高级语法,快速剔除密码和头像
const user={...results[0],password:'',pic:''}
2.运行如下的命令,安装生成Token字符串的包:
yarn add jsonwebtoken@8.5.1
3.在/router_handler/user.js模块的头部区域,导入jsonwebtoken包:
//用这个包来生成Token字符串
const jwt = require('jsonwebtoken')
4.创建config.js文件,并向外共享加密和还原Token的jwtSecretKey字符串:
module.exports = {
jwtSecretKey: '我是你爸爸'
}
5.将用户信息对象加密成Token字符串:
//导入配置文件
const config =require('../config')
//生成Token字符串
const tokenStr= jwt.sign(user,config.jwtSecretKey,{
expiresIn:'10h',//token有效期为10个小时
})
6.将生成的Token字符串响应给客户端:
res.send({
status:0,
message:'登录成功!',
//为了方便客户端使用Token,在服务器端直接拼接上Bearer的前缀
token:'Bearer '+ tokenStr,
})
2.6.4 还原token内容
1.下载解析token的包
// 下载还原token的包
yarn add express-jwt@5.3.3
2.在app.js引入解析token的中间件
//解析token的中间件
const expressJWT = require('express-jwt')
3.引入config文件
//导入配置文件
const config = require('./config')
4.指定哪些接口不用进行身份认证
//使用.unless({path:[/^\/api\//]})指定哪些接口不需要进行Token的身份认证
app.use(expressJWT({secret: config.jwtSecretKey}).unless({path: [/^\/api\//]}))
5.在错误中间件中监听
app.use( (err, req, res, next)=> {
// 4.1 Joi 参数校验失败
if (err instanceof joi.ValidationError) return res.cc(err)
if (err.name === 'UnauthorizedError') return res.cc('请提供正确的token!')
// 4.2 未知错误
res.cc(err)
})
6.设置好后要拿到token中的数据时可以通过req.user来获取
db.query(sql,req.user.id,(err,results)=>{
if(err) return res.cc(err)
res.send({
status:0,
message:'获取文章列表成功',
data:results
})
})