KoaJs框架--实践
# 使用koaJs框架
安装koa2项目生成器并创建项目:
npm install koa-generator -g
Sequelize连接MySQL Sequelize具有强大的事务支持,关联关系,读取和复制等功能; 在项目的根目录下创建一个config目录,config目录中创建db.js,mysql连接。
# 创建schema、modules、controllers
schema--数据表模型实例 建立与数据表的对应关系,用代码建表。schema目录下的article.js用来创建数据表模型--创建数据表
module--实体模型 modules目录下创建article.js文件,为文章表,该文件为文章的实例,存放对数据表在操作的各种方法。
controllers--控制器 控制器的主要作用为功能的处理,controller目录下创建article.js,主要是对api的实现
路由 路径,主要是作为请求的url,请求的路径来处理一些请求,返回数据
# 常用插件
Sequelize:连接MySQL
koa-bodyparser: 处理 post 请求体中的参数
log4js: log4j记录日志,必须放在第一个中间件,才能保证所以的请求及操作会先经过logger进行记录再到下一个中间件。
response.js:新建response.js中间件对返回前端的响应进行处理,用 ctx.body返回前端,有些东西重复写--进行封装,使得返回的格式一致,暴露出responseHandler和errorHandler
# koa-cors:解决跨域
// koa-cors.js插件源码
/* 1、如果需要支持 cookies, Access-Control-Allow-Origin 不能设置为 *,且 Access-Control-Allow-Credentials 需要为 true (注意前端请求需要设置 withCredentials = true)
* 2、当method=OPTIONS时, 属于预检(复杂请求), 当为预检时可直接返回空响应体, 对应的http状态码为204
* 3、通过 Access-Control-Max-Age 可以设置预检结果的缓存, 单位(秒)
* 4、通过 Access-Control-Allow-Headers 设置需要支持的跨域请求头
* 5、通过 Access-Control-Allow-Methods 设置需要支持的跨域请求方法
*/
// app.js 使用koa2-cors插件
const Koa = require('koa');
const bodyParser = require('koa-bodyparser'); //post数据处理
const router = require('koa-router')(); //路由模块
const cors = require('koa2-cors'); //跨域处理
const app = new Koa();
app.use( cors({
origin: function(ctx) { //设置允许来自指定域名请求
if (ctx.url === '/test') { return '*'; } // 允许来自所有域名请求
return 'http://localhost:8080'; //只允许http://localhost:8080这个域名的请求
},
maxAge: 5, //指定本次预检请求的有效期,单位为秒。
credentials: true, //是否允许发送Cookie
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], //所允许的HTTP请求方法
allowHeaders: ['Content-Type', 'Authorization', 'Accept'], //服务器支持的head字段
exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] //获取其他自定义字段
}) );
router.post('/', async function (ctx) { ctx.body = '请求成功了' });
app.listen(3000);
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
# JWT
用于 jwt 权限认证,依赖于json-web-token;用来在身份提供者和服务提供者间传递被认证的用户身份信息
整体流程: 用户使用用户名密码来请求服务器 服务器进行验证用户的信息,通过验证发送给用户一个token 客户端存储token,并在每次请求时附送上这个token值 服务端验证token值,并返回数据
- koa-jwt:对token比对校检鉴权
const Koa = require('koa');
const jwt = require('koa-jwt');
const app = new Koa();
app.use(async (ctx, next) => { // 鉴权失败,错误处理
return next().catch((err) => {
if (401 == err.status) {ctx.body = { code: 50001, message: '用户鉴权失败' }; }
else { throw err; }
});
});
app.use(jwt({ secret: JWT_SECRET }).unless({ path: [/^\/public/, /\/login/] }));
2
3
4
5
6
7
8
9
10
JWT_SECRET
是一个加密因子,安全性,可设置成123456
,可用密钥生成器生成密钥。后面的path路径是设置匹配不需要鉴权的路由或目录,比如所有的public开头的、登录xxxx/login的请求都不需要鉴权。
- jsonwebtoken 库生成token。
// controller/UserController.js
const jwt = require('jsonwebtoken');
async login(ctx, next) {
// 在登录成功后
const token = jwt.sign({ uid: user._id }, JWT_SECRET, { expiresIn: '15d' });
ctx.body = { code: 200, entry: { token: token}, };
}
2
3
4
5
6
7
{ uid: user._id } payload 数据载体,放些参数在 token 中,比如用户id。 JWT_SECRET 加密因子,要跟 koa-jwt 设置的保持一致。 expiresIn 设置 token 的过期时间。
# 具体实现
# Sequelize连接MySQL
在项目的根目录下创建一个config目录,config目录中创建db.js,该文件主要用来创建mysql的数据库链接的。
const Sequelize = require('sequelize');
const sequelize = new Sequelize('dbname','dbusername','password',{
host:'localhost',
dialect:'mysql',
operatorsAliases:false,
dialectOptions:{ //字符集
charset:'utf8mb4', collate:'utf8mb4_unicode_ci',
supportBigNumbers: true, bigNumberStrings: true
},
pool:{ max: 5, min: 0, acquire: 30000, idle: 10000 },
timezone: '+08:00' //东八时区
});
module.exports = { sequelize };
2
3
4
5
6
7
8
9
10
11
12
13
创建schema、modules、controllers
# schema--数据表模型实例
建立与数据表的对应关系,用代码建表。
在schema目录下的article.js用来创建数据表模型的,也可以理解为创建一张数据表
module.exports = function(sequelize,DataTypes){
return sequelize.define('article',{
id:{ type: DataTypes.INTEGER, primaryKey: true,
allowNull: true, autoIncrement: true },
//文章标题
title:{ type: DataTypes.STRING, allowNull: false, field: 'title' },
//作者
author:{ type: DataTypes.STRING, allowNull: false, field: 'author' },
//内容
content:{ type: DataTypes.STRING, allowNull: false, field:'content' },
//文章分类
category:{ type: DataTypes.STRING, allowNull: false, field: 'category' },
// 创建时间
createdAt:{ type: DataTypes.DATE },
// 更新时间
updatedAt:{ type: DataTypes.DATE }
},{ freezeTableName: true });
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
使用mysql工具建表方式
DROP TABLE IF EXISTS `article`;
CREATE TABLE `article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`author` varchar(255) NOT NULL,
`content` varchar(255) NOT NULL,
`category` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;
2
3
4
5
6
7
8
9
10
# module--实体模型
在项目中modules目录下创建article.js文件,为文章表,该文件为文章的实例
// 引入mysql的配置文件
const db = require('../config/db');
const Sequelize = require("sequelize");
// 引入sequelize对象
const sequelize = db.sequelize;
// 引入数据表模型
const Article = require('../schema/article')(sequelize, Sequelize.DataTypes);
Article.sync({force: false}); //自动创建表
class ArticleModel {
static async createArticle(data){
return await Article.create({
title: data.title, author: data.author,
content: data.content, category: data.category
});
}
static async getArticleDetail(id){
return await Article.findOne({ where:{ id } });
}
}
module.exports = ArticleModel;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# controllers--控制器
控制器的主要作用为功能的处理,项目中controller目录下创建article.js
const ArticleModel = require("../modules/article");
class articleController {
static async create(ctx){
let req = ctx.request.body; //接收客服端
if(req.title && req.author && req.content && req.category){
try{ //创建文章模型
const ret = await ArticleModel.createArticle(req);
//使用刚刚创建的文章ID查询文章详情,且返回文章详情信息
const data = await ArticleModel.getArticleDetail(ret.id);
ctx.response.status = 200;
ctx.body = { code: 200, msg: '创建文章成功', data }
}catch(err){
ctx.response.status = 412;
ctx.body = { code: 412, msg: '创建文章失败', data: err }
}
}else {
ctx.response.status = 416;
ctx.body = { code: 200, msg: '参数不齐全' }
}
}
static async detail(ctx){
let id = ctx.params.id;
if(id){
try{ // 查询文章详情模型
let data = await ArticleModel.getArticleDetail(id);
ctx.response.status = 200;
ctx.body = { code: 200, msg: '查询成功', data }
}catch(err){
ctx.response.status = 412;
ctx.body = { code: 412, msg: '查询失败', data }
}
}else {
ctx.response.status = 416;
ctx.body = { code: 416, msg: '文章ID必须传' }
}
}
}
module.exports = articleController;
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
# 路由
路径,主要是作为请求的url,请求的路径来处理一些请求,返回数据
const Router = require('koa-router');
const ArtileController = require('../controllers/article');
const router = new Router({ prefix: '/api/v1' });
//创建文章
router.post('/article/create',ArtileController.create);
//获取文章详情
router.get('/article/:id',ArtileController.detail)
module.exports = router
2
3
4
5
6
7
8
9
10