前言
最近刚刚学完 NodeJS 的入门课程,正好通过简单的 API 开发来总结一下 NodeJS 的学习内容,同时也巩固一下学到的知识点。
本次需要实现的是连接 MySQL 数据库的后端 API,通过使用 GET 和 POST 请求,调用数据库分别实现注册和登录功能。虽然内容较为简单,但基本概括了 mysql2 和 express 进行 API 搭建的主要功能。
书写代码的顺序可能会有一些跳脱,但基本符合该项目的编写流程和思路。
项目准备
新建一个文件夹 mysql-api,使用你的 IDE 打开该文件夹,我在这一步使用的是 JetBrains Rider,进入后在文件夹内打开终端。
确认 NodeJS 和 npm 环境与版本正常
通过 npm 安装前置包:Express 软件包、Body-Parser 软件包和 mysql2 软件包
1 2 3 npm install --save express npm install --save body-parser npm install --save mysql2
这一步会在文件夹内创建一个 node_modules 文件夹和一个 package.json 文件,如果没有在指令中附带 --save
,则安装的软件包不会出现在 package.json 文件的 dependencies
中。
反之,如果使用 --save
指令,后续将代码移交给其他人使用时,只需输入 npm install
即可安装这些依赖软件包。
项目主入口
创建文件
在文件夹下新建脚本文件 index.js,该脚本作为项目的主入口文件,接下来的执行程序操作均使用该指令
进入 index.js
文件,明确该文件的需求:
作为主脚本文件(类似 Main 函数),需要执行核心操作:启动服务器和监听操作
需要使用其他脚本定义的路由并统一管理
需要通过 url 解析用户给出的 GET 请求并返回相应执行结果
需要开启 BodyParser 的 Urlencoded 功能,确保可以解析用户发出的 POST 请求
然后逐个实现上面提到的功能。
编写代码
先在脚本开头导入需求 1、3 需要的软件包
1 2 const express = require ("express" );const bodyParser = require ("body-parser" );
使用 express 的基础语法搭建一个简单的服务器,并在 3000 端口监听
1 2 3 4 5 6 7 8 9 10 const app = express ();app.use (bodyParser.urlencoded ({ extended : true })); app.listen (3000 , function ( ){ console .log ("Server running on port 3000" ); })
此时服务器已经搭建完成,执行 index.js 后,在浏览器内访问 http://localhost:3000 即可进入搭建的服务器。
此时的 index.js 如下
1 2 3 4 5 6 7 8 const express = require ("express" );const bodyParser = require ("body-parser" );const app = express ();app.use (bodyParser.urlencoded ({ extended : true })); app.listen (3000 , function ( ){ console .log ("Server running on port 3000" );
连接数据库
创建文件
同样在文件夹下创建一个新文件 config.js,用来连接和导出数据库。
如果要使项目可以访问第三方数据库(如 MySQL 数据库),就必须使用中间件,这里使用的是 mysql2 软件包作为中间件。通过该软件包的内置函数,可以登录远程 MySQL 数据库,同时创立并导出数据库的操作连接。
注意:mysql 官方提供 mysql 和 mysql2 软件包,且这两个包目前都可以通过 npm 下载。但由于 mysql 连接远程数据库是通过 mysql_native_password 进行验证,而这个验证方式在 MySQL 8.0 中已经被完全舍弃而无法通过,所以这里只能使用 mysql2 软件包作为中间件,否则会出现报错 ERROR 1524 (HY000): Plugin ‘mysql_native_password‘ is not loaded
编写代码
进入 config.js
文件,引入中间件 mysql2
1 const mysql = require ('mysql2' );
随后使用 mysql2 提供的内置函数创建与数据库的连接
1 2 3 4 5 6 const client = mysql.createConnection ({ host :"localhost" , user :"root" , password :"your_password" , database :"database_name" , })
如果配置的信息正确,此时与数据库的联系已经创建并保存在了 client 对象内。但我们希望能在项目的其他地方调用这个数据库的连接,所以我们需要创建一个访问函数,项目的其他部分通过调用这个函数实现对该连接的访问。
1 2 3 4 5 6 7 8 9 10 function connectMySQL (sql, arr, callback ){ client.query (sql, arr, function (err, result ){ if (err){ console .log (err); } callback (result); }) }
回调函数:作为参数传入另一个函数的函数,此处的 callback 为传入的参数,具体传入的函数和功能由其他脚本定义,此处只调用该函数。
创建完成后,导出这个函数,以使得其他脚本可以调用
1 module .exports = connectMySQL;
现在,数据库脚本已经配置完成,完整的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const mysql = require ('mysql2' );const client = mysql.createConnection ({ host :"localhost" , user :"root" , password :"your_password" , database :"NodeJS_Tutorial" , }) function connectMySQL (sql, arr, callback ){ client.query (sql, arr, function (err, result ){ if (err){ console .log (err); } callback (result); }) } module .exports = connectMySQL;
数据库创建
由于本项目需要实现用户注册与登录,所以我们还需要在自己的远程数据库中创建一个对应的数据库,来存储需要的用户信息。
进入 MySQL 远程数据库,在数据库中输入以下指令来创建一个新的数据库,注意不可以有数据库重名。
1 2 3 4 5 6 7 CREATE DATABASE NodeJS_Tutorial;CREATE TABLE NodeJS_Tutorial.Test1( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR (15 ) NOT NULL , password VARCHAR (15 ) NOT NULL );
在这个新建的 NodeJS_Tutorial.Test1 表格中,我们存储了三个数据,分别是作为主键且自增的 id,长度为 15 字符的 username 和 password。后续我们将访问这个表格,并实现在该表格中创建和查询用户数据。
配置路由
创建文件
我们虽然通过 express 创建了一个服务器,但目前并没有配置路由,所以如果需要访问特定信息,所有子页面能访问到的内容都是完全相同的。为了分别实现登录和注册功能,我们使用路由进行配置。所以,我们在文件夹下创建一个新脚本 router.js,用来配置项目的路由。
编写框架
为了尽可能覆盖到更多的功能,且更加贴合使用情况,这里我们使用 GET 方法来进行用户注册,使用 POST 方法进行登录。
首先,进入 router.js 脚本,引入需要的软件包
1 2 const express = require ("express" );const url = require ("url" );
此处的 express 用来创建路由,而 url 用来解析用户输入的数据。
接下来使用 express 的内置函数创建一个路由 router 对象,并写出 Router 提供的 POST 和 GET 函数,然后导出创建的 router 对象
1 2 3 4 5 6 7 8 9 10 11 const router = express.Router ();router.get ("/register" , (req, res )=> { }) router.post ("/login" , (req, res )=> { }) module .exports = router;
回到主入口脚本 index.js
,在其中添加对 router 的引入和使用
1 2 3 const router = require ("./router.js" );app.use ("/api" , router);
得到的 index.js 完整代码如下
1 2 3 4 5 6 7 8 9 10 11 12 const express = require ("express" );const app = express ();const router = require ("./router.js" );const bodyParser = require ("body-parser" );app.use (bodyParser.urlencoded ({ extended : true })); app.use ("/api" , router); app.listen (3000 , function ( ){ console .log ("Server running on port 3000" ); })
注册功能
然后我们来实现 GET 注册方法,由于注册需要访问数据库,所以我们引入之前从脚本 config.js
中导出的数据库访问函数
1 const connectMySQL = require ("./config.js" );
然后实现注册函数
1 2 3 4 5 6 7 8 router.get ("/register" , (req, res )=> { const username = url.parse (req.url , true ).query .username ; const password = url.parse (req.url , true ).query .password ; const createSql = "INSERT INTO Test1 VALUES (DEFAULT, ?, ?);" ; connectMySQL (createSql, [username, password], function (result ){ console .log (result); }) })
这里我们通过 url 提供的解析方法,解析出用户输入的 username 和 password,并通过数据库语句在 Test1 表格中创建该用户的信息。
然后,调用 connectMySQL 函数,这里我们自定义了函数中使用的 callback 函数,函数体为输出数据库处理的结果(result 为 query 函数自带参数,通过访问该参数可以得到数据库处理结果)。
接下来我们使用 node index.js
启动项目,打开浏览器,访问网址 http://localhost:3000/api/register?username=alice&password=123456f ,使用给出的 username 和 password 注册一个用户
如果前面的设置没有问题,这里你会发现网页在持续加载(一直白屏读条但不会报错),同时在控制台中得到下面的信息
1 2 3 4 5 6 7 8 9 ResultSetHeader { fieldCount: 0, affectedRows: 1, insertId: 1, info: '' , serverStatus: 2, warningStatus: 0, changedRows: 0 }
其中的 affectedRows
代表在数据库中造成影响的行数,此处行数为 1,代表成功创建用户数据,并在数据库中占据了 1 行。
所以这里我们使用 affectedRows
来判断注册是否成功:
1 2 3 4 5 6 7 8 9 10 11 router.get ("/register" , (req, res )=> { const username = url.parse (req.url , true ).query .username ; const password = url.parse (req.url , true ).query .password ; const createSql = "INSERT INTO Test1 VALUES (DEFAULT, ?, ?);" ; connectMySQL (createSql, [username, password], function (result ){ console .log (result); if (result.affectedRows > 0 ){ console .log ("Register successfully!" ); } }) })
不出意外的话,你的网页现在还没加载出来,这是因为我们只解析了 req 中的信息,并没有对该函数的 res 做出任何操作,注册成功的信息也只是在控制台中输出。我们应该如何让用户看到注册成功的信息?
其实,express 已经提供了相当完善的方法,这里我们用 express 的方法对 res 参数进行处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 router.get ("/register" , (req, res )=> { const username = url.parse (req.url , true ).query .username ; const password = url.parse (req.url , true ).query .password ; const createSql = "INSERT INTO Test1 VALUES (DEFAULT, ?, ?);" ; connectMySQL (createSql, [username, password], function (result ){ console .log (result); if (result.affectedRows > 0 ){ console .log ("Register successfully!" ); res.send ({ status : 200 , msg : "Register Successfully!" }) } }) })
再次尝试,就会发现网页加载完成,左上角出现了下面这行内容
1 { "status" : 200 , "msg" : "Register Successfully!" }
这就表示注册成功了。
登录功能
接下来我们来实现 POST 登录
1 2 3 4 5 6 7 8 router.post ("/login" , (req, res )=> { const username = req.body .username ; const password = req.body .password ; const sql = "SELECT * FROM Test1 WHERE username = ? AND password = ?" ; connectMySQL (sql, [username, password], function (result ){ console .log (result); }) })
还记得之前在 index.js 中编写的这行代码吗?
1 2 app.use (bodyParser.urlencoded ({ extended : true }));
这里我们使用 bodyParser 的功能,从用户的 POST 请求中解析出 username 和 password,并作出相应处理。我们在数据库中进行查询,这里通过数据库语句 "SELECT * FROM Test1 WHERE username = ? AND password = ?"
查询所有 username 和 password 符合的数据,结果同样通过 result 进行输出。
我们开始调试。由于 POST 和 GET 的请求方式不同,所以这里需要用到第三方工具 POSTMAN,用来向服务器发起 POST 请求。你可以在 https://www.postman.com/downloads/ 下载到最新官方版。
打开 POSTMAN,创建一个 POST 请求窗口,进行如图设置
这里使用刚刚创建的 alice 用户进行 POST 登录请求,但使用的是错误的密码
点击 Send,会发现控制台里多出了这样一行
这代表数据库没有查询到任何信息,result 长度为 0。所以,我们可以使用 result.length
来判断是否登录成功:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 router.post ("/login" , (req, res )=> { const username = req.body .username ; const password = req.body .password ; const sql = "SELECT * FROM Test1 WHERE username = ? AND password = ?" ; connectMySQL (sql, [username, password], function (result ){ console .log (result); if (result.length > 0 ){ res.send ({ status : 200 , message : "Login successfully" , }) } else { res.send ({ msg : "Login failed: wrong username or password" , }) } }) })
再次发送,会发现 POSTMAN 中也得到了登录失败的输出
现在我们将密码修改为正确的密码,重新发起登录请求,会发现控制台中出现了查询到的结果
1 2 3 [ { id : 1, username: 'alice' , password: '123456f' } ]
同时 POSTMAN 中也得到了成功提示
这样,我们就完成了登录的功能。
完整代码
项目结构
1 2 3 4 mysql-api ├── config.js ├── index.js └── router.js
index.js
1 2 3 4 5 6 7 8 9 10 11 12 const express = require ("express" );const app = express ();const router = require ("./router.js" );const bodyParser = require ("body-parser" );app.use (bodyParser.urlencoded ({ extended : true })); app.use ("/api" , router); app.listen (3000 , function ( ){ console .log ("Server running on port 3000" ); })
config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const mysql = require ('mysql2' );const client = mysql.createConnection ({ host :"localhost" , user :"root" , password :"your_password" , database :"NodeJS_Tutorial" , }) function connectMySQL (sql, arr, callback ){ client.query (sql, arr, function (err, result ){ if (err){ console .log (err); } callback (result); }) } module .exports = connectMySQL;
router.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 const express = require ("express" );const router = express.Router ();const url = require ("url" );const connectMySQL = require ("./config.js" );router.get ("/register" , (req, res )=> { const username = url.parse (req.url , true ).query .username ; const password = url.parse (req.url , true ).query .password ; const createSql = "INSERT INTO Test1 VALUES (DEFAULT, ?, ?);" ; connectMySQL (createSql, [username, password], function (result ){ console .log (result); if (result.affectedRows > 0 ){ console .log ("Register successfully!" ); res.send ({ status : 200 , msg : "Register Successfully!" }) } }) }) router.post ("/login" , (req, res )=> { const username = req.body .username ; const password = req.body .password ; const sql = "SELECT * FROM Test1 WHERE username = ? AND password = ?" ; connectMySQL (sql, [username, password], function (result ){ console .log (result); if (result.length > 0 ){ res.send ({ status : 200 , message : "Login successfully" , }) } else { res.send ({ msg : "Login failed: wrong username or password" , }) } }) }) module .exports = router;