如何将MySQL与Node.js应用程序整合在一起

292 阅读5分钟

简介

MySQL是一个非常流行的 关系型数据库管理系统(RDBMS)- 在很长一段时间里,它一直是任何后端工程师工具包中的一个主要部分,就在PostgreSQL旁边。

随着后端JavaScript的出现,通过Node.js的流行和适用性的显著上升,MySQL通常被用作用Node构建的项目和应用程序的数据库。

在本指南中,我们将看看如何将MySQL与Node.js应用程序集成。我们将通过设置过程,以编程方式执行CRUD操作,并通过转义输入和设置超时来查看基本的查询安全性。

**注意:**在整个指南中,我们将把npm MySQL驱动称为mysql ,把MySQL数据库本身称为MySQL

MySQL的设置

要使用MySQL,你需要下载 MySQL数据库并在你的本地机器上托管一个小型服务器。社区版本是完全免费的!

你也可以选择安装一个支持MySQL数据库的开源Web服务器*(Xampp*、Lampp)--它们与Node的MySQL驱动也能很好地工作。

安装程序很简单,一旦你设置了一个服务实例,你就可以创建一个项目来连接它了

Node项目的MySQL驱动

假设服务器正在运行,你可以通过一个Node应用程序,使用驱动程序与它进行程序性通信。为了使一个应用程序具有这种能力,你需要安装一个MySQL驱动。驱动程序可在npm ,即mysql!

首先,让我们创建一个新的目录来托管我们的应用程序,并初始化一个新的Node项目,并使用默认设置。

$ mkdir mysql-app
$ cd mysql-app
$ npm init -y

这将生成一个新的package.json ,其中包含我们项目的基本元数据。一旦完成,我们就可以通过npm ,安装node MySQL驱动包。

$ npm install mysql

我们可以通过检查我们的package.json 文件来验证安装是否成功,在那里我们应该发现在dependencies 下添加了一个新的条目mysql

 ...
 "dependencies": {
    "mysql": "^2.18.1"
  }

注意: ^2.18.1 代表版本号,这将取决于你所安装的MySQL的版本。

建立一个连接

可以通过mysql 实例的createConnection() 函数来创建一个连接对象。然后,这个对象可以用来在服务器和客户端/驱动程序之间建立一个连接!它接受三个参数:1.它接受三个参数。

  • 主机- 你的数据库的host 。默认值是'localhost'
  • 用户- 授权数据库管理员的用户名。默认值是'root'
  • 密码- 授权数据库管理员的密码。默认值是一个空字符串(没有密码)。
  • 数据库- 一个可选的参数,定义你要修改的数据库名称。

考虑到这些,让我们打开默认的index.js 文件 - 我们可以用它连接到MySQL服务器。

var mysql = require("mysql");

var connection = mysql.createConnection({
    host: "localhost",
    user: "root",
    password: "",
});

connection.connect((err) => {
    if (err) {
      console.log("Error occurred", err);
    } else {
      console.log("Connected to MySQL Server");
    }
});

一旦配置了一个连接,你就可以简单地将connect() 到服务器上!connect() 函数使用一个回调,允许你检查连接是否成功。

通过JavaScript编写查询

几乎所有你需要在MySQL数据库中执行的操作都是通过查询完成的,自然是用SQL编写的。connection 实例的query() 方法接受一个字符串,代表我们要发送给数据库的查询。

一个查询可能会失败或返回一些结果,所以我们会有另一个回调,根据操作的结果,有一个errresult

创建一个数据库

让我们开始通过Node的MySQL驱动来创建一个数据库。你可以通过CLI(如果你已经安装了它)或通过任何形式的GUI(官方或第三方)在服务器上创建一个数据库。

另外,你也可以使用我们创建的连接来发送一个查询,为你创建一个数据库!让我们创建一个新的students_records 数据库。在项目的根目录中,在我们之前使用过的同一个index.js ,让我们在连接到数据库之后,如果连接成功的话,添加一个query() 的调用。

var mysql = require("mysql");

// Create connection configuration
var connection = mysql.createConnection({
    host: "localhost",
    user: "root",
    password: "",
});

// Connect to the server
connection.connect((err) => {
    if (err) {
      // Return error if present
      console.log("Error occurred", err);
    } else {
      // Create database
      console.log("Connected to MySQL Server");
      const query = "CREATE DATABASE students_records";
      connection.query(query, function (err, result) {
        if (err) {
          err;
        }
        console.log("New database created");
      });
    }
});

在验证了我们的连接是否成功之后,我们执行了一个带有查询字符串的查询--'CREATE DATABASE students_records' ,如果还没有存在,就创建一个新的数据库。现在,让我们运行该项目。

$ node index.js

其结果是

Connected to MySQL Server
New database created

棒极了!现在,一个students_records 数据库已经存在,我们可以取消数据库创建查询,直接连接到配置中的students_records

var connection = mysql.createConnection({
    host: "localhost",
    user: "root",
    password: "",
    database: "students_records",
});

创建记录

就像其他关系型数据库一样,MySQL的数据记录被存储在 .一个表可以由任意数量的列和行组成,但它们必须是一致的。数据库中的列代表一个对象的特征/字段,而每一行代表一个条目。

每一行必须有每一列的值,而且只有这些列,即使它只是NULL 。对于任何关系型数据库,模式必须保持一致。

这意味着我们需要预先创建一个表,并定义其模式(每一个列和类型),以便向我们的数据库添加新记录。

要在MySQL中创建一个新表,我们使用CREATE TABLE 语句。假设我们想在我们的students_records 数据库中创建一个students 表。同样,一旦连接,我们可以直接执行相关的查询。

var mysql = require("mysql");

var connection = mysql.createConnection({
    host: "localhost",
    user: "root",
    password: "",
    database: "students_records",
});

connection.connect((err) => {
    if (err) {
      console.log("Error occurred", err);
    } else {
      console.log("Connected to database");
      var sql = "CREATE TABLE students (id INT, name VARCHAR(255), course VARCHAR(255))";
      connection.query(sql, function (err, result) {
        if (err) {
          console.log(err);
        }
        console.log("New table created");
      });
    }
});

这就创建了一个新的表,可以容纳带有namecourse 字段的记录!每个条目都会有一个namecourse ,即使其中有些是空的。此外,我们已经将它们的类型设置为VARCHAR ,最大长度为255个字符。

在这里,你可以另外设置其他的约束条件,比如某些字段是强制性的(NOT NULL ),或者PRIMARY KEY/FOREIGN KEY

CREATE TABLE students (
  id INT NOT NULL,
  name VARCHAR(255) NOT NULL,
  course VARCHAR(255) NOT NULL,
  PRIMARY KEY (ID)
)

在这里,没有一个字段可以被设置为NULL ,而且每个字段都有一些相关的数据类型。

**注意:**在SQL中,你是否将关键字大写并不重要。create table 在功能上等同于CREATE TABLE ,就像varchar 相对于VARCHAR 。将命令、关键字和约束条件大写是常见的惯例,不过,这并不是一个要求。

创建记录

INSERT INTO 查询,然后是表名和映射到它的列的VALUES ,用于用SQL插入/创建记录!例如,让我们在我们的students 表中添加一个John Doe

var sql = "INSERT INTO students (id, name, course) VALUES (1, 'John Doe', 'Computer Science')";

不出所料,这条语句将在students 表中插入一条记录(行),并为namecourse 。像往常一样,该查询可以通过connection.query() 来执行。

var sql = "INSERT INTO students (id, name, course) VALUES (1, 'John Doe', 'Computer Science')";

// Just execute
connection.query(sql);
// Or execute with callback to handle results/errors
connection.query(sql, function (err, result) {
  // ...
});

读取记录

我们不只是想存储数据--我们还想能够读取它。而且,如下图所示,我们可以通过SELECT 查询从students 表中检索数据。

我们可以选择特定的记录或使用通配符(* )来选择所有可用的记录。

var sql = "SELECT * FROM students";
connection.query(sql, function (err, result) {
  console.log(result);
});

运行这段代码将返回我们表中的所有数据。

[
  RowDataPacket { id: 1, name: 'John Doe', course: 'Computer Science' },
  RowDataPacket { id: 2, name: 'Jane Doe', course: 'Art' }
]

你还可以使用BETWEEN 语句来创建一个匹配行的范围。

SELECT * FROM students WHERE id BETWEEN 1 AND 5

这将返回所有的学生,其ID在1到5之间。

更新记录

我们可以使用UPDATE 语句来更新数据库表,以及它们的内容。例如,你可以使用几个语句来识别某条记录并更新该记录。一般来说,一个id ,应该是一个用于识别行的PRIMARY KEY ,同时也是一个唯一的值,所以id ,通常是用来搜索记录的列。

假设我们想把学生,John Doe's,课程从Computer Science 更新到Art ,和他的表弟一起。约翰的id1

var sql = "UPDATE students SET course = 'Art' WHERE id = 1";

connection.query(sql, function (err, result) {
  console.log(result);
});

像这样的查询结果通常包括关于查询如何影响表的细节,这就是你如何验证结果是否按照你的意图工作。

 {
  fieldCount: 0,
  affectedRows: 1,
  insertId: 1,
  serverStatus: 34,
  warningCount: 0,
  message: '(Rows matched: 1  Changed: 1  Warnings: 0',
  protocol41: true,
  changedRows: 1
}

删除记录

最后,要删除记录--我们使用DELETE 语句,并指定哪些记录要被删除。

var sql = "DELETE FROM students WHERE id = 1";
connection.query(sql, function (err, result) {
  console.log(result);
});

**注意:**在使用DELETE 语句时,你应该小心,并仔细指定WHERE 子句。此外,如果你不指定WHERE 子句,整个表的内容将被清除。

转移查询值

当软件工程师允许在SQL调用中使用用户输入时,一个常见的早期漏洞被发现。例如,你可以有一个带输入字段的搜索栏。用户可以插入一本书的名字,这个输入将被用在一个查询中,例如。

SELECT * FROM books WHERE name = user_input

user_input ,自然会被替换成一个字符串,如'Foundation' 。然而,这为巨大的漏洞打开了一扇门,这些漏洞就像逃避你建立的查询并运行他们自己的查询一样简单。

例如,如果系统被构建为天真地接受输入并将其插入到查询中,那么用户只要输入a; DROP TABLE books ,就可以利用你的查询。

SELECT * FROM books WHERE name = a; DROP TABLE books

分号将结束前一个语句,而排在后面的一个语句将运行,从数据库中删除整个表,并删除所有书籍。这是一个简单的SQL注入的例子,攻击可以比这复杂得多。此外,并不是所有的攻击都是破坏性的--攻击者也可能只是读取敏感数据,这是一种无声的攻击,通常会被掩盖。

鉴于这些攻击可能变得如此普遍--大多数软件包都有基本的内置安全。为Node构建的mysql 驱动程序提供了帮助避免SQL注入的功能和措施。

最常见的方法是 转义值并将它们作为命名参数来代替。

const input = 'Foundation';

connection.query('SELECT * FROM books WHERE name = ?', [input], function (err, results, fields) {
// ... 
});

通过使用? 转义字符--你转义了输入,把它变成了一个字面字符串,无法被评估。如果有人在输入字段中输入一个命令,导致这个参数--它不会被评估为一个命令。它将被视为一个字面字符串,并将发送一个查询,以找到一个名称与该命令相匹配的书。

该方法接受一个参数数组,这些参数将依次输入到查询中。如果查询有2个注释参数,列表中的第一个项目将被映射到第一个参数,列表中的第二个项目将被映射到第二个参数。

另外,你也可以通过JavaScript的字符串格式化明确地注入这些值,并使用escape() 方法来转义可能的命令,不过--这种方法的可读性一般较差。

const input = mysql.escape('Foundation');

connection.query(`SELECT * FROM books WHERE name = ${input}`, function (err, results, fields) {
// ... 
});

设置超时时间

每个与连接相关的操作都会在默认的超时时间后终止,我们可以通过设置我们自己喜欢的超时时间来解决这个问题。这可以通过向.query() 函数传递一个对象来实现,我们的查询和超时时间(以毫秒为单位)是该对象的字段。

connection.query({sql: 'SELECT * FROM students', timeout: 50000}, function (err, results) {
// ...
});

这段代码将运行一个SELECT 查询,如果没有返回结果,将在50秒后终止。

总结

MySQL是一个常用的关系型数据库。在本指南中,我们看了如何为Node.js项目安装MySQL服务器的驱动,如何设置与服务器的连接并执行CRUD操作。

最后,我们指出了转义用户输入的重要性,并看了一下自定义超时时间。