Skip to content

前两章,我们安装了 Mysql 运行环境,创建了 blog 数据库,创建了 user 表和相关字段定义,并成功地发起请求,接口解析且拿到了请求数据。

这篇文章,就开始完成登录注册逻辑的开发和数据的入库。

json
{
    "name": "CoolApi",
    "version": "1.0.0",
    "description": "我的接口框架",
    "type": "module",
    "main": "server.js",
    "scripts": {
        "dev": "node server.js"
    },
    "dependencies": {
        "mime-types": "^2.1.35",
        "mysql2": "^3.10.3"
    },
    "devDependencies": {
        "nodemon": "^3.1.4"
    }
}

在此之前呢,不知道大家有没有发现,每次都要重启接口服务,异常麻烦。

所以,我们可以安装一个 Npm 包 nodemon,可以监控代码的改动,自动重启接口服务。

同时呢,也将操作 Mysql 数据库的扩展 mysql2 给安装上。

json
{
    "name": "CoolApi",
    "version": "1.0.0",
    "description": "我的接口框架",
    "type": "module",
    "main": "server.js",
    "scripts": {
        "dev": "node server.js", 
        "dev": "nodemon server.js"
    },
    "dependencies": {
        "mime-types": "^2.1.35",
        "mysql2": "^3.10.3"
    },
    "devDependencies": {
        "nodemon": "^3.1.4"
    }
}

nodemon 的使用也很简单,把我们的脚本命令中的 node 替换成 nodemon 即可。

bash
# 执行 pnpm run dev
[nodemon] files triggering change check: package.json
[nodemon] matched rule: **\*.*
[nodemon] changes after filters (before/after): 1/1
[nodemon] restarting due to changes...
[nodemon] package.json

[nodemon] starting `node server.js`
[nodemon] forking
[nodemon] child pid: 9436
服务已启动,监听端口为:3000

执行 pnpm run dev 的效果如上。

一段小插曲,我们继续开发 用户登录注册功能

用户注册流程

  1. 查询数据库,用户名是否存在。
    1. 如果存在,则提示,用户已存在
    2. 如果不存在,则进行下一步。
  2. 把用户名,密码,创建时间,插入到数据库中。
  3. 接口正常返回,并提示 注册成功

用户登录流程

  1. 查询数据库,用户名是否存在。
    1. 如果不存在,则提示 用名不存在,请注册
    2. 如果存在,这进行下一步。
  2. 将查询出的密码跟上传的密码进行对比。
    1. 如果不一致,则提示,密码错误
    2. 如果密码一致,则进行下一步。
  3. 接口返回用户数据,并提示 登录成功

顺便提一嘴,这个库是使用原生的 SQL 语法操作 Mysql,随着课程的深入,我们后面会换成更方便的操作库。

当我们查看 Mysql2 官网的时候,会发现有 3 种连接方式。

picture 0

我们选择第二种,createPool 即可,createPoolClustercreatePool 的升级版,我们的项目用不着。

这是单连接和连接池的区别:

普通连接 (单连接)

  • 适用场景: 适用于请求量不高,或者每个请求都是独立的应用场景。
  • 特点: 每次请求都会创建一个新的数据库连接,请求结束后关闭连接。
  • 优点: 简单易用,适合小规模应用或脚本。
  • 缺点: 频繁创建和销毁连接可能会影响性能,尤其是在高并发场景下。

连接池 (连接池模式)

  • 适用场景: 适用于请求量高,需要频繁访问数据库的应用,如 Web 应用服务器。
  • 特点: 预先创建一定数量的数据库连接,并在请求到来时从池中获取连接,请求结束后将连接返回到池中,而不是关闭连接。
  • 优点: 提高性能,减少连接创建和销毁的开销,能够更好地处理高并发请求。
  • 缺点: 需要管理连接池的大小和生命周期,可能会占用更多的内存资源。
js
import mysql from "mysql2/promise";

const mysqlPool = mysql.createPool({
    host: "localhost",
    user: "root",
    password: "root",
    database: "blog",
    port: 3306,
    namedPlaceholders: true,
});

export { mysqlPool };

我们单独创建一个 mysql.js 文件,代码如上。

提示: 记得先运行数据库软件。

js
import { mysqlPool } from "../mysql.js";
export default async (req) => {
    try {
        // 从连接池中获取一个数据库连接
        const db = await mysqlPool.getConnection();
        // 查询数据库是否有对应的用户数据
        const result = await db.query({
            sql: "SELECT version()",
        });
        console.log("🚀 ~ result:", result);
        // 释放数据库连接
        db.release();
        // 返回成功信息
        return {
            code: 0,
            msg: "注册成功",
        };
    } catch (err) {
        console.log("🚀 ~ err:", err);
        return {
            code: 1,
            msg: "未知错误",
        };
    }
};

在用户注册接口中,引入 mysql.js 文件。

先来查询数据库版本,看看是否正常。

bash
服务已启动,监听端口为:3000
🚀 ~ result: [ [ { 'VERSION()': '5.7.44' } ], [ `VERSION()` VARCHAR(6) NOT NULL ] ]

可以看到,控制台打印了我们的数据库版本,是 5.7,说明数据库连上了。

我们看这个返回数据,是一个数组。

这是 Mysql2 的统一返回结构,数组的第一个值是操作得到的数据,数组的第二个值是相关的定义。

还记得我们之前说过的 做实验 吗?我们不要急着写功能,初学者,多打印,多看,确认数据库连接正常,查询正常,再进行后面的步骤。

数据库操作完毕,要记得 释放连接,这就好像吃饭的公筷一样,你不放回去,大家都拿到手里,其他人怎么夹菜呢。

js
import { mysqlPool } from "../mysql.js";
export default async (req) => {
    try {
        // 参数去掉前后空格
        const username = req.body.username.trim();
        const password = req.body.password.trim();
        // ------------------------------------------------------
        // 验证用户名参数
        if (username.length < 2) {
            return {
                code: 1,
                msg: "用户名太短",
            };
        }
        // ------------------------------------------------------
        // 验证密码参数
        if (password.length < 6) {
            return {
                code: 1,
                msg: "密码太短",
            };
        }
        // ------------------------------------------------------
        // 从连接池中获取一个数据库连接
        const db = await mysqlPool.getConnection();
        // 查询数据库是否有对应的用户数据
        const [rows] = await db.query({
            sql: "SELECT * FROM `user` WHERE `username`=:username LIMIT 1",
            values: {
                username: username,
            },
        });
        console.log("🚀 ~ rows:", rows);
        // ------------------------------------------------------
        // 如果查到了用户数据,说明该用户名已注册
        if (rows.length > 0) {
            return {
                code: 1,
                msg: "用户已注册",
            };
        }
        // ------------------------------------------------------
        // 插入用户数据
        const [result] = await db.query({
            sql: "INSERT INTO `user` (`username`,`password`,`created_at`) VALUES (:username,:password,:created_at)",
            values: {
                username: username,
                password: password,
                created_at: Date.now(),
            },
        });
        console.log("🚀 ~ result:", result);
        // 释放数据库连接
        db.release();
        // 返回成功信息
        return {
            code: 0,
            msg: "注册成功",
        };
    } catch (err) {
        console.log("🚀 ~ err:", err);
        return {
            code: 1,
            msg: "未知错误",
        };
    }
};

接下来,实现注册的具体逻辑。按照分割线,一步步理解,不理解的在问答群艾特笔者即可。

其实一点也不难,一次理解一小部分,整个代码就理解了。

picture 1

上图是用户注册功能的演示动图。

picture 2

为了让我们对从数据库拿到的数据,有更深的印象,笔者写了两处打印日志。

picture 3

最后,我们查看数据库,可以看到,用户信息已经插入了,注册功能初版完成!

js
// /apis/userLogin.js 文件
import { mysqlPool } from "../mysql.js";
export default async (req) => {
    try {
        // 参数去掉前后空格
        const username = req.body.username.trim();
        const password = req.body.password.trim();
        // ------------------------------------------------------
        // 验证用户名参数
        if (username.length < 2) {
            return {
                code: 1,
                msg: "用户名太短",
            };
        }
        // ------------------------------------------------------
        // 验证密码参数
        if (password.length < 6) {
            return {
                code: 1,
                msg: "密码太短",
            };
        }
        // ------------------------------------------------------
        // 从连接池中获取一个数据库连接
        const db = await mysqlPool.getConnection();
        // 查询数据库是否有对应的用户数据
        const [rows] = await db.query({
            sql: "SELECT * FROM `user` WHERE `username`=:username LIMIT 1",
            values: {
                username: username,
            },
        });
        console.log("🚀 ~ rows:", rows);
        // ------------------------------------------------------
        // 如果查到了用户数据,说明该用户名已注册
        if (rows.length <= 0) {
            return {
                code: 1,
                msg: "用户未注册",
            };
        }
        // ------------------------------------------------------
        // 判断密码是否匹配
        const [user] = rows; // rows是一个数组,我们这里用user变量去取数组的第一个值
        if (user.password !== password) {
            return {
                code: 1,
                msg: "密码错误",
            };
        }
        // 释放数据库连接
        db.release();
        // 返回成功信息
        return {
            code: 0,
            msg: "登录成功",
            data: user,
        };
    } catch (err) {
        console.log("🚀 ~ err:", err);
        return {
            code: 1,
            msg: "未知错误",
        };
    }
};

登录功能,就不多赘述了,跟注册所差无几。

picture 4

点击登录按钮,就能拿到登录的用户数据了。

至此,我们的登录和注册功能,就完成了。

这是我们的第一个初步版本,后续还会进一步完善。

不知道看这里,是不是发现,其实后端开发,也很简单呢?

何以解忧,唯有代码。不忘初心,方得始终。