关于使用Koa2的点点滴滴
Koa是基于Nodejs的一个后端框架,算是比较常用的,核心思想就是中间件,Koa实现底层逻辑,其余的就需要自己实现,包括:session、数据库操作、文件上传、路由、模板、静态资源访问等
前言
一直以来都是使用thinkjs
这个框架,集成很多功能,基本上就只需要负责写逻辑就OK了,所以想尝试一下Koa这种需要动手逐个实现功能的,类似也有express
这里并不打算写新手教程,可自行百度,只是写使用过程中遇到的一些疑惑
下面代码基于ES6、7
1、安装
官网推荐使用Nodejs >= 7.6的版本,小于这个版本需要采用babel方式曲线救国
实际使用发现,用了babel方式,可以解决koa本身的兼容问题,但koa的部分插件还是有问题,所以还是别采用这种方式了
2、配置文件
很多时候,我们需要有本地配置、测试配置、正式配置,如果每次发布都要注释掉其它两个环境的配置,就很容易出错
可以结合package.json
中配置脚本命令,启动时候带不同参数来区分
"scripts": {
"start": "set NODE_ENV=test && node ./app.js",
"dev": "set NODE_ENV=dev && node ./app.js",
"release": "set NODE_ENV=release && node ./app.js"
}
思路为设置环境变量NODE_ENV
的值,然后启动项目,上面对应的命令为:npm run (scripts的key)
然后在代码中根据环境变量的值,引用对应的配置文件
//根据不同的NODE_ENV,输出不同的配置对象
const env = process.env.NODE_ENV.replace(/\s+/,"");
let config = require('./'+(env||"test"));
3、登陆态(上)
Koa本身是没有实现session这块,需要用插件才能实现
可以使用插件koa-session2
,默认使用它,会将session内容保存在内存中,这样会有一个坏处,开发的时候需要经常重启服务,内存就被清空,意味着你需要重新登陆一次,所以一般不会存在内存中
- 本地与测试环境:采用文件形式保存
- 正式环境:采用数据库或者缓存方式保存,考虑到多台机器部署
而koa-session2
并没有提供上述的保存方式,只提供了参数可选,具体方式需要自行实现,虽然也有对应的插件,不过使用时候发现并不好使,还是自己实现,其实很简单,覆盖三个函数即可:
//插件提供的基类
const { Store } = require("koa-session2");
//自定义一个类继承它
class FileStore extends Store {
//构造函数,照抄即可
constructor() {
super();
}
//读取session内容的方法,sid为每个session的key,一串加密串,读取出session内容,返回
async get(sid, ctx) {}
//设置session内容的方法,需要将session对象的内容保存起来
async set(session, { sid = this.getID(24)} = {}, ctx) { }
//session过期或者删除时候,需要如何清除
async destroy(sid, ctx) {}
}
实现完之后,需要配置session的保存参数store
为上面实现的new FileStore()
,这样就可以在所有的controller
中读取和设置属性ctx.session
了
4、登陆态(下)
登陆态一般会在登陆成功后、注册完进行设置,那如何判断是否登陆了?然后没有登陆跳转到登陆页面;最简单就是在每个controller的action中判断 ctx.session是否为空,但每个都写,很头疼
常见的方式是:自定义一个中间件,这样用户的每一个请求都会经过它,由它去判断
let loginMiddle = async (ctx, next) => {
//获取用户请求的URL
let url = ctx.request.url;
//login路径为不需要登陆态,因为它是登陆方法,执行next则跳到controller中
if(url.indexOf("/login") > -1)return await next();
//假设登陆态是设置了user属性
let user = ctx.session.user;
//没有登陆态
if(!user){
//跳转到登陆页
return ctx.response.redirect('/login');
}
//正常执行controller
await next();
};
只需要使用koa的时候,加载这个中间件就可以,需要注意:加载顺序需要在路由之前
登陆态是在用户浏览器设置一个加密的sessionid的cookie,而页面的地址与后台地址不同域名的话,则会出现种植失败,需要添加跨域允许种cookie才行,使用插件koa-cors
,使用时候,设置参数credentials : true
即可
5、防止CSRF漏洞
CSRF算是比较普遍的漏洞了,koa也有插件可以实现,不过使用起来不那么方便,直接将token种在页面中,对于后台只实现接口,没有页面渲染的不友好,所以可以自行实现个简单版的
在登录成功的action中,设置一个用于校验的token的cookie
//定义用户的token值,这里简单用时间戳,应该是用加密的md5值
let token = new Date() - 0;
//将token设置到session中,用于未来校验
ctx.session._csrf = token;
//将token种在用户的cookie中
ctx.cookies.set(
'_csrf',token,{
path:'/',
httpOnly:false, // 是否只用于http请求中获取
overwrite:true // 是否允许重写
}
);
可以在上面的第四步中的登陆态中间件加入校验,这样就不需要每个action都去校验一次
let loginMiddle = async (ctx, next) => {
//此处省略判断session登陆态
//...
//获取请求中的token
let csrfToken = ctx.request.method.toLowerCase() == "get" ? ctx.request.query["_csrf"] : ctx.request.body["_csrf"];
//没有传,提示token错误
if(!csrfToken)return ctx.body = "token错误";
//与session中的token对比
if(csrfToken != ctx.session._csrf) return ctx.body = "token错误";
//正常执行controller
await next();
};
6、jsonp
koa有很多jsonp的插件,大部分是直接全局替换,即返回的内容直接就是jsonp方式,连普通的返回都没了
使用koa-response-jsonp
插件,可以按需要自行使用jsonp方式,在controller的action中,使用ctx.jsonp(object)
,请求参数固定为callback=xxx
PS:当然你可以自行实现这个中间件,需要校验参数中的一些合法字符即可
7、数据校验
很多时候后台需要校验前端传送过来的数据,校验是否合法,可以避免后续操作很多问题,写在action中的话,会显得有些长,而action中更应该专注于逻辑
数据校验可以使用parameter
插件,内置了基本的规则,比如:数字、字符串、邮箱等
//demo,简单的三条规则
mobile : {type : 'id',required:false}
email : {type : 'email',required:false}
nickname : {type : 'string',required: true,min:1}
校验可以写在router
路由中,比如下面这个,在调用具体action前,先校验数据
router.get('/get_order', async (ctx)=>{
//校验规则
let rule = {
openid : {type : 'string',required: false,min:6,allowEmpty:true},
code : {type : 'string',required:true,min:6}
};
//校验数据
let ret = validateUtil.validate(rule,ctx.request.query);
//校验失败,返回错误信息,不执行具体action
if(ret.status == false)return ctx.body = ret;
//校验完毕,执行后续操作
await controller.get_order(ctx);
});
8、设置404
很奇怪,koa是连这个也没有,需要自己去实现,不麻烦,就是所有的路由规则都没有命中,就是404了
//设定404页面,即所有路由都没命中
router.get('*', async (ctx, next) => {
ctx.status = 404;
await ctx.render('error/404');
});
9、日志
一般都用Log4js来做日志记录操作,本地开发的时候还好好的,直到用上了pm2来启动,以及采用了多线程方式,日志就哑火了,一个都没记录了
Log4js的配置,需要加上这个:
- "pm2": true
- "pm2InstanceVar": 'INSTANCE_ID'
以及需要再pm2.json文件加上:
- "instances": 0
- "instance_var": "INSTANCE_ID"
10、其余常用的插件
- koa-bodyparser:主要用于获取post请求的参数
- koa-helmet:安全插件,防止SQL注入与攻击等
- koa-router:路由插件
- koa-static:静态资源处理,用户显示css、img、js等
- koa-views:模板渲染,需要搭配ejs插件使用
- axios:请求第三方接口的插件
后话
Koa很多事情都需要自行去处理解决,虽然有插件能帮忙完成,但根据自身需要,还是需要手动去实现一些功能,总体来说,起步上手,会比什么都搭好的thinkjs要麻烦,但也是因为这样,可以只保留自己业务需要的功能,其余不需要就可以放弃掉,性能上会有所提升。
手机阅读请扫描下方二维码:
12345678
555
555
555
555
555
555
1
1
12345678
12345678
12345678
1
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
kfupmrlirwjzjzeorkze
12345678
12345678
12345678
12345678
1
1
1
1
1
1
1
1
12345678
12345678
12345678
12345678
12345678
mplhwgewrjagdxhcjsck
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
expr 821903781 + 874759020
12345678 |expr 876292717 + 868615207
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
12345678
1
1
%{40933*40566}
1
1
1
1
1
1
1
1
555
1
1
1
555
1
1
1
1
1
1
1
1
555
1
555
1
1
1
1
1
555
1
1
555
1
555
1
1
1
1
1
1
1
1
1
1
1
1
1
1