简介

作为前端,我一直希望能独立做一个包含前后端的项目,做到更了解后端开发,做“完整”的开发者。

本文是我学习后端的一个简易事例。我认为这个事例很有价值,我可以在此基础上做起自己的后端服务。

该后端事例使用node(koa2)+mysql,旨在提供restful接口,能实现增删改查,并总结了相关后端服务的知识。

主要参考教程

前置条件

数据库安装

  1. 需要先在服务器端安装好mysql数据库(当然也可以安装在本地),并开启远程访问权限
  2. 本地安装mysql远程管理终端sequel pro for mac
  3. 使用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项目目录结构

使用koa-generator生成的项目目录:

koaindex.png

配置mysql

我之前看过mysql,但回想起来学习方法有问题:

“先学语法”-“再看安装”-“再找最佳实践”

这样的学习路径总是容易困在第一步,因为语法实际上很多,像读字典一样容易让人兴致了了。

实际上mysql只需要理解它的基本结构,跑一个事例程序,完成增删改查,不要在意过多的语法细节,这样更有助于快速学习而不丧失热情。

mysql管理工具

mysql管理工具有很多,如网页版的phpmyadmin,这里使用sequel Pro for mac客户端版本,其界面如下:

sequel1.png

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

sequel2.png

之后遇到疑惑的指令随时搜索即可

常见数据库操作命令

网上有大量总结教程,如参考,这里介绍最常用的增删改查

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,如本例

实例.png

当使用get请求localhost:3000/mysql时,就会返回数据库查询到的值

当使用post请求localhost:3000/add时,就会增加
一条新的纪录

当然,该接口并没做错误处理、返回值格式等工作