关于使用Koa2的点点滴滴

Koa是基于Nodejs的一个后端框架,算是比较常用的,核心思想就是中间件,Koa实现底层逻辑,其余的就需要自己实现,包括:session、数据库操作、文件上传、路由、模板、静态资源访问等

前言

一直以来都是使用thinkjs这个框架,集成很多功能,基本上就只需要负责写逻辑就OK了,所以想尝试一下Koa这种需要动手逐个实现功能的,类似也有express

这里并不打算写新手教程,可自行百度,只是写使用过程中遇到的一些疑惑

下面代码基于ES6、7

1、安装

官网推荐使用Nodejs >= 7.6的版本,小于这个版本需要采用babel方式曲线救国

实际使用发现,用了babel方式,可以解决koa本身的兼容问题,但koa的部分插件还是有问题,所以还是别采用这种方式了

2、配置文件

很多时候,我们需要有本地配置、测试配置、正式配置,如果每次发布都要注释掉其它两个环境的配置,就很容易出错

可以结合package.json中配置脚本命令,启动时候带不同参数来区分

1

2

3

4

5

"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)

然后在代码中根据环境变量的值,引用对应的配置文件

1

2

3

4

//根据不同的NODE_ENV,输出不同的配置对象

const env = process.env.NODE_ENV.replace(/\s+/,"");

let config = require('./'+(env||"test"));

3、登陆态(上)

Koa本身是没有实现session这块,需要用插件才能实现

可以使用插件koa-session2,默认使用它,会将session内容保存在内存中,这样会有一个坏处,开发的时候需要经常重启服务,内存就被清空,意味着你需要重新登陆一次,所以一般不会存在内存中

  • 本地与测试环境:采用文件形式保存
  • 正式环境:采用数据库或者缓存方式保存,考虑到多台机器部署

koa-session2并没有提供上述的保存方式,只提供了参数可选,具体方式需要自行实现,虽然也有对应的插件,不过使用时候发现并不好使,还是自己实现,其实很简单,覆盖三个函数即可:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

//插件提供的基类

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是否为空,但每个都写,很头疼

常见的方式是:自定义一个中间件,这样用户的每一个请求都会经过它,由它去判断

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

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

1

2

3

4

5

6

7

8

9

10

11

12

//定义用户的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都去校验一次

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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插件,内置了基本的规则,比如:数字、字符串、邮箱等

1

2

3

4

//demo,简单的三条规则

mobile : {type : 'id',required:false}

email : {type : 'email',required:false}

nickname : {type : 'string',required: true,min:1}

校验可以写在router路由中,比如下面这个,在调用具体action前,先校验数据

1

2

3

4

5

6

7

8

9

10

11

12

13

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了

1

2

3

4

5

//设定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要麻烦,但也是因为这样,可以只保留自己业务需要的功能,其余不需要就可以放弃掉,性能上会有所提升。

猜你喜欢

转载自blog.csdn.net/weixin_38098195/article/details/83509768
今日推荐