前言

记录使用Node.js的Express与Sequelize开发后端接口方法。


yarn工具

1
2
3
4
5
6
7
8
9
// 全局安装;
npm i -g yarn
// 安装国内镜像文件,三个命令行;
yarn config set registry https://registry.npm.taobao.org --global
yarn config set disturl https://npm.taobao.org/dist --global
yarn config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g
// 基本命令格式
yarn add //安装
yarn global add //全局安装

Hello word项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 初始化,建立package.json文件
npm init
// 注意使用yarn init好像有问题(具体不清楚原因,推荐还是npm init)

// 安装express
yarn add express

// 建立main里面的js入口文件
// 引用express
var express = require('express');
// 使用
var app = express();
// req是前端发送,res是后端返回
app.get('/', function (req, res) {
res.send('Hello World!');
});
// 命令行提示
app.listen(3000, function () {
console.log('成功启动');
});
1
2
3
4
5
6
// 注意,package.json文件中,启动命令是serve时;
"scripts": {
"serve": "node index.js"
},
npm run serve // 需要加run(其他不需要)
yarn serve // 直接输入

快速构建node项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 安装命令
cnpm install express-generator -g
// 新建项目
express --view=ejs blog
cd blog
cnpm install
// 启动项目
npm start
// 修改端口号(app.js里面修改)可选
process.env.PORT = 2000
// 使用nodemon监听代码变动
cnpm i nodemon -S
// 在package.json文件修改为
"scripts": {
"start": "nodemon ./bin/www"
},

安装sequelize连接数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 安装sequelize和mysql2
cnpm install sequelize -S
cnpm install mysql2 -S
// 全局安装sequelize-cli(下次不要安装了)
cnpm install sequelize-cli -g
// 初始化项目
sequelize init
// 配置数据库
"development": {
"database": "blog_development",
}
// 使用命令行创建数据库(报错就手动,推荐手动),后面迁移不成功就输入下面的代码
sequelize db:create --charset 'utf8mb4'
// 手动创建
// 使用软件创建数据库:先创建连接,后建立和config文件中名字相同的库;
// 排序选择utf8mb4_general_ci;

// 创建模型
sequelize model:generate --name Article --attributes title:string,content:text
// 运行迁移
sequelize db:migrate

实现增删改查

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
43
44
45
46
// 在routes中创建路由,并在app.js入口文件引入和使用;(如下)
var articlesRouter = require('./routes/articles');
app.use('/articles',articlesRouter)

// 先引入
var models = require('../models');

// get请求
router.get('/', function (req, res, next) {
models.Article.findAll().then(articles => {
res.json({articles: articles});
})
});
// 第二种写法
router.get('/', async function (req, res, next) {
var articles = await models.Article.findAll({
order: [['id', 'DESC']], // 这里是倒叙
})
res.json({articles: articles});
});

// 查询
router.get('/:id', async function (req, res, next) {
var article = await models.Article.findByPk(req.params.id);
res.json({article: article});
});

// 新增
router.post('/', async function (req, res, next) {
var article = await models.Article.create(req.body)
res.json({article: article});
});

// 删除
router.delete('/:id', async function (req, res, next) {
var article = await models.Article.findByPk(req.params.id)
article.destroy();
res.json({msg: '删除成功'});
});

// 修改
router.put('/:id', async function (req, res, next) {
var article = await models.Article.findByPk(req.params.id);
article.update(req.body);
res.json({article: article});
});

跨域:读取不了数据

这个配置方法只在开发环境有效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1、vue的跨域配置
module.exports = {
devServer: {
proxy: { // 增加一个api的前缀(proxyTable代理服务器)
'/api': {
target: process.env.VUE_APP_BASE_API, // 后台接口域名
ws: true, // 如果要代理 websockets,配置这个参数
secure: false, // 如果是https接口,需要配置这个参数
changeOrigin: true, // 是否跨域
pathRewrite: {
'^/api': '' //用'/api'代替target里面的地址
}
}
}
},
}

2、CORS
通过自定义请求头来让服务器和浏览器进行沟通

3、nginx代理跨域
nginx模拟一个虚拟服务器,因为服务器与服务器之间是不存在跨域的,
发送数据时 ,客户端->nginx->服务端
返回数据时,服务端->nginx->客户端

模糊搜索和分页

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
router.get('/', async function (req, res, next) {
var where = {};
var title = req.query.title;
var content = req.query.content;
var currentPage = parseInt(req.query.currentPage) || 1;
var pageSize = parseInt(req.query.pageSize) || 2;
// 查询关联id字段时,就不要使用[Op.like]了,否者查询1出现11的bug
if (title) {
where.title = {
[Op.like]: `%${title}%`
}
}
if (content) {
where.content = {
[Op.like]: `%${content}%`
}
}
var result = await models.Article.findAndCountAll({
order: [['id', 'DESC']],
where: where,
offset: (currentPage - 1) * pageSize,
limit: pageSize,
})
res.json({
articles: result.rows,
pagination: {
currentPage: currentPage,
pageSize: pageSize,
total: result.count
}
});
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 创建模型
sequelize model:generate --name Category --attributes name:string,sort:integer
// 在迁移文件添加默认值
allowNull: false,
defaultValue: '0'
// 迁移
sequelize db:migrate
// 回滚
npx sequelize-cli db:migrate:undo
// 或者(网上看的还未验证)sequelize db:migrate:undo
// 新的模型为添加字段
sequelize-cli migration:create --name add-categoryID-to-userID
// 在新的迁移文件中
// up里面
await queryInterface.addColumn('articles', 'categoryId', {
type: Sequelize.INTEGER
})

await queryInterface.addColumn('articles', 'userId', {
type: Sequelize.INTEGER
})
// down里面
await queryInterface.removeColumn('articles', 'categoryId')
await queryInterface.removeColumn('articles', 'userId')
1
2
3
4
5
6
7
// 模型里面从属
models.Category.hasMany(models.Article)
models.Article.belongsTo(models.Category)
// articles中关联模型
include: [models.Category, models.User],
// categories中使用sort排序
order: [['id', 'DESC']], // 正序ASC

在下拉读取时分类时,接口里面写分页写大,不然在读取只能读第一页


数据库密码加密

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
43
44
45
46
47
48
49
50
51
52
53
// 安装bcryptjs
npm install bcryptjs
// 在users里面引用
var bcrypt = require('bcryptjs');
// 用户新增里面
// 密码需要转化为字符串
var password = req.body.password ? req.body.password.toString().trim() : false
var name = req.body.name ? req.body.name.toString().trim() : false
// 判断填写的密码和用户名
if (!name) {
return res.json({
code: 20000,
data: {status: false,msg: "用户名必须填写"}
})
}
if (!password) {
return res.json({
code: 20000,
data: {status: false,msg: "密码必须填写"}
})
}
// 查询单条属性findOne
var user = await models.User.findOne({
where: {name: req.body.name}
});
// 判断用户名已经存在(这里可以写return,必须要在函数里面,当前if在一个函数里面)
if (user) {
return res.json({
code: 20000,
data: {
status: false,
msg: "用户名已经存在"
}
})
}
// 生成盐(加密用)
var salt = bcrypt.genSaltSync(10);
// 将密码加密转化
var hash = bcrypt.hashSync(password, salt);
// 推到数据库(...为展开下面可以直接覆盖)
var body = {
...req.body,
password: hash
}
user = await models.User.create(body)
// 返回结果
res.json({
code: 20000,
data: {
status: true,
user: user
}
});

登录生成token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 检查输入的密码是否正确
var password = req.body.password // 这里
var hash = user.password // 数据库保存的乱码
var check = bcrypt.compareSync(password, hash);
// 安装生成token的包
yarn add jsonwebtoken
// 引用jwt
var jwt = require('jsonwebtoken');
// 生成token,将id和name生成token,其中有个环境变量,最后的生成有效期为7天的token;
// 注意这里生成token时,如果有多个中间件,后台和前台的token都会验证成功(bug)
var token = jwt.sign({
id: user.id,
name: user.name
}, process.env.SECRET, {expiresIn: 60 * 60 * 24 * 7});
// 返回token
res.json({token:token})

// 环境变量
// 安装dotenv
yarn add dotenv
//安装完成后,在项目根目录建一个.env文件,里面代码
SECRET=clwy.cn // 例如
// 在根目录app.js中引用
require('dotenv').config()

中间件验证登录

1
2
3
4
5
6
7
8
// 根目录下新建/middlewares/checkAuth.js文件
// 里面代码(如下checkAuth)

// 在app.js中,后台的路由中,添加上中间件
// 引用
var checkAuth = require('./middlewares/checkAuth')
// 添加
app.use('/admin/categories', checkAuth(), adminCategoriesRouter);
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
// 中间件代码
var jwt = require('jsonwebtoken')

module.exports = function (options) {
return function (req, res, next) {
// 验证是否有token
var token = req.headers.token;
if (!token) {
return res.json({
code: 50008,
message: 'Token未提供'
});
}

// 验证token是否正确
jwt.verify(token, process.env.SECRET, function (err, decoded) {
if (err) {
if (err.name == "TokenExpiredError") {
return res.json({
code: 50014,
message: 'Token过期'
});
}
if (err.name == "JsonWebTokenError") {
return res.json({
code: 50008,
message: 'Token错误'
});
}
}

// decoded里面装的是解密出来的是之前加密生成的token的东西
// 解析出来的东西存入req

req.decoded = decoded;
next();
})
}
}

时间moment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 安装moment
yarn add moment
var moment = require('moment');// 引用
// config.json配置时区,设置成国内
"timezone": "+08:00"
// 设置时间对象(当前月去i,返回一年的数据)
let months = []
let data = []
moment.locale('zh-cn');// 中国时区
for (let i = 0; i < 12; i++) {
let month = moment().subtract(i, 'months').format("YYYY-MM");
// 注意format使用;
months.push(month)
let start = moment().subtract(i, 'months').startOf('month').format("YYYY-MM-DD")
+ ' 00:00:00'
let end = moment().subtract(i, 'months').endOf('month').format("YYYY-MM-DD")
+ ' 23:59:59'
let res = await models.Product.count({
where: {'createdAt': {[Op.between]: [start, end]}}
})// count是查询符合条件的个数
data.push(res)
}

seed种子文件添加数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 创建seed文件
sequelize seed:generate --name order
// 在文件seeders/xxx-order.js(可以修改后再次提交)
// up里面(添加的数必须要有时间)
let data= {
name: 'admin',
createdAt: new Date(),
updatedAt: new Date()
}
await queryInterface.bulkInsert('Orders', data, {});// 需要大写
// down(注意这个不是使用回滚,会导致之前的表迁移返回,暂时不知道如何使用)
await queryInterface.bulkDelete('Orders', null, {});
// 运行迁移(所有的seed)
sequelize db:seed:all
// 迁移单个文件
sequelize db:seed --seed xxx-order
// 撤消最近种子
sequelize db:seed:undo
sequelize db:seed:undo:all// 所有的种子

七牛云图片后台

1
2
3
4
5
6
7
8
9
10
11
12
// 安装
npm install qiniu
// 引用
const qiniu = require('qiniu');
// router中
var accessKey = process.env.ACCESS_KEY;// 七牛云的密钥
var secretKey = process.env.SECRET_KEY;// 七牛云的私钥
var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
var options = {scope: process.env.SCOPE,};// 后为空间名字
var putPolicy = new qiniu.rs.PutPolicy(options);
var uploadToken = putPolicy.uploadToken(mac);
// 最后返回uploadToken

封装res返回的success、error

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 新建utils/message.js文件
const success = (res, message = '', data = {}) => {
return res.json({
code: 20000,
message,
data
})
}

const error = (res, message = '') => {
return res.json({
code: 20000,
message,
})
}

module.exports = {
success,
error
}
// 路由中引用
const {success, error} = require('../utils/message')
// 使用
success(res, '查询成功', data)

CORS跨域处理

1
2
3
4
5
6
yarn add cors
// app.js中
var cors = require('cors');
// ... 注意位置
var app = express();
app.use(cors());