简介
作为前端,我一直希望能独立做一个包含前后端的项目,做到更了解后端开发,做“完整”的开发者。
本文是我学习后端的一个简易事例。我认为这个事例很有价值,我可以在此基础上做起自己的后端服务。
该后端事例使用node(koa2)+mysql,旨在提供restful接口,能实现增删改查,并总结了相关后端服务的知识。
前置条件
数据库安装
- 需要先在服务器端安装好mysql数据库(当然也可以安装在本地),并开启远程访问权限
- 本地安装mysql远程管理终端sequel pro for mac
- 使用sequel pro连接数据库
koa2安装
安装koa专用脚手架
yarn global add koa-generator
使用脚手架新建koa2模板项目
koa2 myproject [options]
其中options用来设置初始化项目的参数,如使用node渲染引擎ejs要这样写
koa2 myproject -e
启动web服务
cd myproject
yarn
yarn start
打开默认的3000端口即可预览
koa概述
- koa做的事情其实很少,除了路由和视图模板,其实并没有做太多其他的封装,非常轻量
- 但是他的中间件机制可以让我们使用其他的第三方模块,在这里,我们需要mysql
koa项目目录结构
使用koa-generator生成的项目目录:

配置mysql
我之前看过mysql,但回想起来学习方法有问题:
“先学语法”-“再看安装”-“再找最佳实践”
这样的学习路径总是容易困在第一步,因为语法实际上很多,像读字典一样容易让人兴致了了。
实际上mysql只需要理解它的基本结构,跑一个事例程序,完成增删改查,不要在意过多的语法细节,这样更有助于快速学习而不丧失热情。
mysql管理工具
mysql管理工具有很多,如网页版的phpmyadmin,这里使用sequel Pro for mac客户端版本,其界面如下:

令人兴奋的功能是GUI操作转换为指令,有利于初学者学习sql语句

之后遇到疑惑的指令随时搜索即可
常见数据库操作命令
网上有大量总结教程,如参考,这里介绍最常用的增删改查
1.查询表数据
SELECT * FROM `user`
返回数组,元素是某行的数据组成的对象
2.插入某行
可以加反引号,可以不加,不能加引号,但值必须加引号
INSERT INTO `test` (`id`, `Name`, `sex`, `age`, `address`, `email`) VALUES (NULL, 'mengma', '女', '22', '北京', '123@126.com');
INSERT INTO mmytable (Name, sex, age, address, email) VALUES ('xxx', '女', '22', '北京', '123@126.com');
反引号的作用是允许用户字段与mysql保留字相同而不报错
INSERT INTO `test` SET `Name`='zhang',`sex`='女';
INSERT INTO mmytable SET Name="zhang",sex='女';
3. 修改某行
UPDATE tableName SET key=val WHERE(primaryKey=primaryVal);
4. 删除某行
DELETE FROM tableName WHERE(primaryKey=primaryVal)
代码设计参考
1. 将mysql配置文件单独写在config文件夹
写在根目录config文件夹中
const mysqlConfig = {
user: "root",
password: 'mypw',
database: 'mydb', // 这一行可以省略,省略后就要代码中创建数据库(不推荐做法)
host: 'localhost',
port: '3306'
}
module.export = mysqlConfig;
注意: 如果数据库用户名和密码写在项目目录中,使用git开源的话有风险
2. 将常用查询函数和sql语句写在utils文件夹
用 createConnection 创建 Mysql 连接,每执行一次 connection.query 都是一个全新的连接,会造成一个资源的极大浪费,降低性能。
连接池是另外的一种执行方法,它一次性的创建了多个连接,然后根据客户端的查询,自动的 分发、复用、管理 这些连接。
query.js
const mysql = require('mysql')
const MYSQL_CONFIG = require('../config/mysql_config') // 数据库配置
// mysql
const pool = mysql.createPool(MYSQL_CONFIG)
// query sql语句入口
const query = (sql, val) => {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, connection) {
if (err) {
reject(err)
}
else {
connection.query(sql, val, (err, fields) => {
if (err) reject(err)
else resolve(fields)
connection.release()
})
}
})
})
}
module.exports = {
query
}
sql.js
// 创建数据库
const CREATE_TABLE = `CREATE TABLE IF NOT EXISTS user(
user_id INT(5) NOT NULL AUTO_INCREMENT,
user_name VARCHAR(255) NOT NULL,
user_phone VARCHAR(255) NOT NULL,
PRIMARY KEY (user_id)
);`.replace(/[\r\n]/g, '')
// 查询数据表
const QUERY_TABLE = (tableName) => `SELECT * FROM ${tableName}`
// 插入数据
const INSERT_TABLE = (tableName, {key, val}) => `INSERT INTO ${tableName}(${key}) VALUES (${val})`
// 更新数据
const UPDATE_TABLE = (tableName, {primaryKey, primaryVal}, {key, value}) => `UPDATE ${tableName} SET ${key}=${val} WHERE(${primaryKey}=${primaryVal});`
// 删除数据
const DELETE_TABLE = (tableName, {primaryKey, primaryVal}) => `DELETE FROM user WHERE(${primaryKey}=${primaryVal});`
module.exports = {
QUERY_TABLE,
CREATE_TABLE,
INSERT_TABLE,
UPDATE_TABLE,
DELETE_TABLE
}
sql防注入(安全性)
sql防注入的关键就是不能直接把数据拼接到sql语句中。必须得对数据进行转义,或者使用提供的方法拼接sql语句,默认不适用多语句查询也是防注入措施
防注入方法主要有三种:
1. connection.escape()
var sql = 'SELECT * FROM `user` WHERE `uid`='+connection.escape('"123";//--');
console.log(sql); // SELECT * FROM `user` WHERE `uid`='\"123\";//--'
connection.query(sql, function(err, result){
if(err) throw err;
console.log(result);
})
2. 占位符 ?
占位符的作用是将query的第二个参数params数组元素依次拼接到sql语句中,其实是隐式调用connection.escape转义
var params = ['test', 'test'];
var query = connection.query('SELECT * FROM `user` WHERE `username`=? AND `password`=?', params, function(err, result){
console.log(result);
});
console.log(query.sql); // SELECT * FROM `user` WHERE `username`='test' AND `password`='test'
数据库中的表名和字段名,可以使用??作为占位符,在拼接完成后会自动添加上``(反引号表示可以使用mysql保留字),??主要用来加反引号,占位顺序依然是params数组元素顺序:
var params = ['user', 'username', 'test', 'password', 'test'];
var query = connection.query('SELECT * FROM ?? WHERE ??=? AND ??=?', params, function(err, result){
console.log(result);
})
console.log(query.sql); // SELECT * FROM `user` WHERE `username`='test' AND `password`='test'
3. 使用mysql.format()转义参数
var userId = 1;
var sql = "SELECT * FROM ?? WHERE ?? = ?";
var inserts = ['user', 'uid', userId];
sql = mysql.format(sql, inserts); // SELECT * FROM `user` WHERE `uid` = 1
Q&A
1. nodemon是什么
nodemon用来监视node.js应用程序中的任何更改并自动重启服务,非常适合用在开发环境中。以便每一次更改都会刷新服务。
2. 多语句查询
为了防止SQL注入,默认不支持多语句查询,当然可以设置更改支持多条sql语句查询
var connection = mysql.createConnection({multipleStatements: true});
3. 模板引擎是什么
每一种后端语言,一旦涉及到渲染页面,就需要有一种把数据和模板组合到一起然后渲染出页面的技术,这就是模板引擎。
如java的jsp、php的Mustache等。这里node的模板引擎常用的有pug、ejs。
前后端分离时,如果后端只提供restful api,前端渲染页面,前后端通过ajax交互,后端就不需要用到模板引擎了。
4. 如何写接口
当node(koa)服务启动后,会监听客户端请求的url,如本例

当使用get请求localhost:3000/mysql时,就会返回数据库查询到的值
当使用post请求localhost:3000/add时,就会增加
一条新的纪录
当然,该接口并没做错误处理、返回值格式等工作
评论 1