1. 创建项目文件,然后执行初始化命令(会生成package.json项目描述文件,记录项目的信息,安装依赖包信息、相关命令等)

node init -y

2. 安装需要的依赖包

/*
* express: 快速搭建服务器 对于nodejs中http模块的进一步封装
* nodemon: 热重载
* cors: 处理跨域
* @hapi/joi: 请求数据进行验证
* @escook/express-joi: 表单数据进行验证
* express-jwt:解析 Token 
* mysql:数据库
* bcryptjs:非对称加密
* jsonwebtoken:生成 Token 字符串
*/

npm add express nodemon cors @hapi/joi@17.1.0 @escook/express-joi express-jwt jsonwebtoken mysql

3. 项目结构

│  config.js
│  index.js
│  package-lock.json
│  package.json
│  
├─db
│      index.js
│      
├─routerHandle
│      user.js
│      userinfo.js
│      
├─routers
│      user.js
│      userinfo.js
│      
└─schema
        user.js

4. 执行文件 index.js

// 导入模块
const express = require('express');
const cors = require('cors');
// 导入定义验证规则的包
const joi = require('joi');
const app = express();

app.use(cors());

app.use(express.json()) // 解析表单中json数据,不配置的情况下,req.body默认为undefined
app.use(express.urlencoded({ extended: false })) // 解析表单中url-encoded数据,不配置的情况下,req.body默认为 {}

// 失败响应方法中间件
app.use((req, res, next) => {
    res.cc = (status, err) => {
        res.send({
            status,
            message: err instanceof Error ? err.message : err
        })
    }
    next()
})

// 路由之前声明中间件
// token 解析中间件
const expressJWT = require('express-jwt')
const config = require('./config')
app.use(expressJWT({ secret: config.jwtSecretKey }).unless({ path: [/^\/api/] }))

// 导入用户登录注册路由模块
const userRouter = require('./routers/user')
app.use('/api', userRouter)
// 导入用户信息路由模块
const userInfoRouter = require('./routers/userinfo')
app.use('/my', userInfoRouter)

// 错误级别中间件,需放在所有路由最后
app.use((err, req, res, next) => {
    // 表单验证失败
    if (err instanceof joi.ValidationError) return res.cc(400, err)
    // 身份认证失败
    if (err.name === 'UnauthorizedError') return res.cc(401, '身份认证失败')
    res.cc(400, err)
})

// 启动服务
app.listen(8081, () => {
    console.log('Server is running..., http://127.0.0.1');
})

5. 数据库文件 db/index.js

const mysql = require('mysql')
const db = mysql.createPool({
    host: '127.0.0.1',
    user: 'root',
    password: '123456',
    database: 'text'
})
module.exports = db

6. 登录注册路由模块 routers/user

// 导入模块
const express = require('express');
const router = express.Router()
// 导入用户路由处理函数模块
const userHandle = require('../routerHandle/user')

// 导入验证表单中间件
const expressJoi = require('@escook/express-joi')

// 导入需要验证的对象
const { registerValidate } = require('../schema/user')

// 用户注册
router.post('/register', expressJoi(registerValidate), userHandle.register)

// 用户登录
router.post('/login', expressJoi(registerValidate), userHandle.login)


module.exports = router
  • 登录注册处理模块 routerHandle/user

    // 导入数据库操作模块
    const db = require('../db')
    // 导入加密包
    const bcrypt = require('bcryptjs')
    // 导入生成token的包
    const jwt = require('jsonwebtoken')
    // 导入全局配置文件
    const config = require('../config')
    
    // 用户注册处理模块
    exports.register = (req, res) => {
        const userinfo = req.body
        if (!userinfo.username || !userinfo.password) {
            return res.cc(400, '请输入用户名和密码')
        }
        // 用户名查重
        const DcSql = 'select * from node_users where username = ?'
        db.query(DcSql, userinfo.username, (err, results) => {
            if (err) {
                return res.cc(500, err)
            }
            if (results.length > 0) {
                return res.cc(400, '用户名已存在')
            }
    
            // 调用bcrypt.hashSync() 对密码进行加密
            userinfo.password = bcrypt.hashSync(userinfo.password, 10)
            // 新增用户
            const addSql = 'insert into node_users set ?'
            db.query(addSql, { username: userinfo.username, password: userinfo.password }, (err, results) => {
                if (err) {
                    return res.cc(500, err)
                }
                if (results.affectedRows !== 1) {
                    return res.cc(400, '注册失败')
                }
                // 注册成功
                return res.cc(200, '注册成功')
            })
        })
    
    }
    
    // 用户登录处理模块
    exports.login = (req, res) => {
        const usesrinfo = req.body;
        // 查询用户名是否存在
        const querySql = 'select * from node_users where username = ?';
        db.query(querySql, usesrinfo.username, (err, results) => {
            if (err) {
                return res.cc(500, err);
            }
            if (results.length !== 1) return res.cc(400, '用户名不存在')
            // 用户名存在的话校验密码是否正确
            const comparPassword = bcrypt.compareSync(usesrinfo.password, results[0].password);
            if (!comparPassword) return res.cc(400, '密码错误')
            // 生成token字符串
            const user = { ...results[0], password: '', user_pic: '' }
            const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: config.expiresin })
            // 用户与密码正确则登陆成功
            res.send({
                status: 200,
                message: '登陆成功',
                token: 'Bearer ' + tokenStr
            })
        })
    }
  • 登录注册表单校验模块 schema/user

    // @ts-nocheck
    // 导入定义验证规则的包
    const joi = require('joi');
    
    // 定义登录注册验证规则
    const username = joi.string().alphanum().min(3).max(8).required();
    const password = joi.string().pattern(/^(?=.*\d)(?=.*[a-z])[a-zA-Z0-9]{6,12}$/).required();
    exports.registerValidate = {
        body: {
            username,
            password
        }
    }
    
    // 定义用户信息修改验证规则
    const id = joi.number().integer().min(1).required();
    const nickname = joi.string();
    const editUsername = joi.string().alphanum().min(3).max(8);
    const email = joi.string().email();
    exports.updateUserInfoValidate = {
        body: {
            id,
            nickname,
            username: editUsername,
            email
        }
    }
    
    // 定义修改密码验证规则
    exports.updatePasswordValidate = {
        body: {
            oldPwd: password,
            newPwd: joi.not(joi.ref('oldPwd')).concat(password),
        }
    }
    
    // 定义用户头像验证规则
    const avatar = joi.string().dataUri().required();
    exports.updateAvatarValidate = {
        body: {
            avatar
        }
    }

7. 流程总结

node搭建服务器.png

最后修改:2023 年 12 月 02 日
如果觉得我的文章对你有用,请随意赞赏