Node.js的基本使用(四)项目实战——项目初始化及用户注册登录接口的实现

初始化

创建项目
  1. 初始化包管理配置文件npm init -y
  2. 运行如下的命令,安装特定版本的 express
  3. 在项目根目录中新建 app.js 作为整个项目的入口文件,并初始化如下的代码
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()

// write your code here...

// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(3007, function () {
  console.log('api server running at http://127.0.0.1:3007')
})
配置cors跨域
  1. 运行如下的命令,安装 cors 中间件npm i cors@2.8.5
  2. app.js 中导入并配置 cors 中间件
// 导入 cors 中间件
const cors = require('cors')
// 将 cors 注册为全局中间件
app.use(cors())
配置解析表单数据的中间件

只能解析application/x-www-form-urlencoded

app.use(express.urlencoded({ extended: false }))
初始化路由相关的文件夹
  1. 在项目根目录中,新建 router 文件夹,用来存放所有的路由模块
  2. 在项目根目录中,新建 router_handler 文件夹,用来存放所有的 路由处理函数模块
初始化用户路由模块
  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
  1. app.js 中,导入并使用 用户路由模块
// 导入并注册用户路由模块
const userRouter = require('./router/user')
app.use('/user', userRouter)
抽离用户路由模块中的处理函数

为了保证 路由模块 的纯粹性,所有的 路由处理函数,必须抽离到对应的 路由处理函数模块

  1. 在router_handle中的user.js中定义路由处理函数
exports.register = (req,resp)=>{
    resp.send("发送成功")
    console.log('注册成功');
}
exports.login =(req,resp)=>{
    resp.send("登录成功!")
}
  1. 在router中的user.js中使用定义的处理函数模块
const express = require("express")
const router = express.Router()

const userHandle = require("../router_handle/user.js")

router.get("/register",userHandle.register)
router.post("/login",userHandle.login)

module.exports = router

image-20220829134235810

安装配置mysql
  1. 使用npm安装mysql模块npm install mysql
  2. 配置mysql模块
const mysql = require("mysql")
const db = mysql.createPool({
    host:'127.0.0.1',
    user:'root',
    password:'root',
    database:'node_demo'
})
// 向外共享 db 数据库连接对象
module.exports = db

登录注册

初始化数据表
  1. 新增user表用来存放用户信息

  1. 检查表单数据是否合法
  • 获取表单数据 res.body 需要安装body-parser中间件,并且需要在引入路由之前引用
//app.js
var bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())

//router_handle/user.js
exports.register = (req, resp) => {
    // 接收表单数据
    const userinfo = req.body
    // 判断数据是否合法
    if (!userinfo.username || !userinfo.password) {
        return res.send({
            status: 500,
            message: '用户名或密码不能为空!'
        })
    }    
}
  1. 验证用户名是否存在
    // 检测用户名是否被占用
    db.query('select * from user where username = ?',userinfo.username,function(err,result){
        if(err){
            return res.send({ code:500,msg:err.message})
        }
        if(result.length > 0){
            return res.send({ code:-1,msg:"用户名已被占用,请更换其他用户名!"})
        }
        res.send("新增成功")
    })
  1. 对密码进行加密,在当前项目中,使用 bcryptjs 对用户密码进行加密,优点:
  • 加密之后的密码,无法被逆向破解
  • 同一明文密码多次加密,得到的加密结果各不相同,保证了安全性

运行如下命令,安装指定版本的 bcryptjs

npm i bcryptjs@2.4.3

/router_handler/user.js 中,导入 bcryptjs

const bcrypt = require('bcryptjs')

在注册用户的处理函数中,确认用户名可用之后,调用 bcrypt.hashSync(明文密码, 随机盐的长度) 方法,对用户的密码进行加密处理

// 对用户的密码,进行 bcrype 加密,返回值是加密之后的密码字符串
userinfo.password = bcrypt.hashSync(userinfo.password, 10)
优化res.send()代码

处理函数中,需要多次调用 res.send() 向客户端响应 处理失败 的结果,为了简化代码,可以手动封装一个 res.cc() 函数,需要定义在路由之前

app.use(function(req,res,next){
    res.cc = function (err,code = 500){
        res.send({
            code,
            msg:err instanceof Error ? err.message : err
        })
    }
    next()
})

此时,注册代码简化为

exports.register = (req, res) => {
    // 接收表单数据
    const userinfo = req.body
    // 判断数据是否合法
    if (!userinfo.username || !userinfo.password) {
        return res.cc('用户名或密码不能为空!')
    }
    // 检测用户名是否被占用
    db.query('select * from user where username = ?',userinfo.username,function(err,result){
        if(err){
            return res.cc(err)
        }
        if(result.length > 0){
            return res.cc("用户名已被占用,请更换其他用户名!",-1)
        }
    })
    //对用户密码进行加密
    userinfo.password = bcrypt.hashSync(userinfo.password, 10)
    userinfo['nickName'] = '新用户'
    userinfo['userPic'] = ''
    // 插入新用户
    db.query("insert into user set ?",userinfo,function(err,result){
        if(err){
            return res.cc(err)
        }
        if(result.affectedRows != 1){
            return res.cc("注册用户失败,请稍后重试!")
        }
        return res.cc("新增用户成功",200)
    })
}
优化表单数据验证

单纯的使用 if...else... 的形式对数据合法性进行验证,效率低下、出错率高、维护性差。因此,推荐使用第三方数据验证模块,来降低出错率、提高验证的效率与可维护性。

  1. 安装 @hapi/joi 包,为表单中携带的每个数据项,定义验证规则:
npm install @hapi/joi@17.1.0
  1. 安装 @escook/express-joi 中间件,来实现自动对表单数据进行验证的功能:
npm i @escook/express-joi
  1. 新建 /schema/user.js 用户信息验证规则模块,并初始化代码如下:
const Joi = require('@hapi/joi')
const joi = require('@hapi/joi')

/**
 * string() 值必须是字符串
 * alphanum() 值只能是包含 a-zA-Z0-9 的字符串
 * min(length) 最小长度
 * max(length) 最大长度
 * required() 值是必填项,不能为 undefined
 * pattern(正则表达式) 值必须符合正则表达式的规则
 */

// 用户名的验证规则
const username = joi.string().alphanum().min(1).max(10).required()
// 密码的验证规则
const password = joi
  .string()
  .pattern(/^[\S]{6,12}$/)
  .required()

// 注册和登录表单的验证规则对象
exports.reg_login_schema = {
  // 表示需要对 req.body 中的数据进行验证
  body: {
    username,
    password,
    repassword:Joi.ref('password')
  },
}
  1. 修改 /router/user.js 中的代码如下:

    // 1. 导入验证表单数据的中间件
    const expressJoi = require('@escook/express-joi')
    // 2. 导入需要的验证规则对象
    const { reg_login_schema } = require('../schema/user')
    
    router.post("/register",expressJoi(reg_login_schema),userHandle.register)
    
  2. app.js 的全局错误级别中间件中,捕获验证失败的错误,并把验证失败的结果响应给客户端:

// 错误中间件
app.use(function (err, req, res, next) {
    // 数据验证失败
    if (err instanceof joi.ValidationError) return res.cc(err)
    // 未知错误
    res.cc(err)
  })
登录判断用户输入的密码是否正确

调用 bcrypt.compareSync(用户提交的密码, 数据库中的密码) 方法比较密码是否一致,返回值为布尔类型

if(!bcrypt.compareSync(userinfo.password,result[0].password)){
    return res.cc('密码错误,请重新输入','-1')
}
生成 JWT 的 Token 字符串

在生成 Token 字符串的时候,一定要剔除 密码头像 的值

// 剔除完毕之后,user 中只保留了用户的 id, username, nickname, email 这四个属性的值
const user = { ...results[0], password: '', user_pic: '' }

知识链接—扩展运算符(…)

对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中

let bar = { a: 1, b: 2 };
let baz = { …bar }; // { a: 1, b: 2 }

如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉

let bar = {a: 1, b: 2};
let baz = {…bar, …{a:2, b: 4}}; // {a: 2, b: 4}

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

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

    npm i jsonwebtoken@8.5.1
    
  2. /router_handler/user.js 模块的头部区域,导入 jsonwebtoken 包:

// 用这个包来生成 Token 字符串
const jwt = require('jsonwebtoken')
  1. 创建 config.js 文件,并向外共享 加密还原 Token 的 jwtSecretKey 字符串
module.exports = {
  jwtSecretKey: 'abcdefg. ^_^',
}
  1. 将用户信息对象加密成 Token 字符串
// 导入配置文件
const config = require('../config')

// 生成 Token 字符串
const tokenStr = jwt.sign(user, config.jwtSecretKey, {
  expiresIn: '10h', // token 有效期为 10 个小时
})
  1. 将生成的 Token 字符串响应给客户端
res.send({
  status: 200,
  message: '登录成功!',
  // 为了方便客户端使用 Token,在服务器端直接拼接上 Bearer 的前缀
  token: 'Bearer ' + tokenStr,
})
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值