Koa - 中间件

匿名 (未验证) 提交于 2019-12-02 21:53:52

前言

Koa 应用程序是一个包含一组中间件函数的对象,它是按照类似堆栈的方式组织和执行的。

next()

以上两句话,是我在官方文档中找到其对 Koa 中间件的描述。

在Koa中,中间件是一个很有意思的设计,它处于request和response中间,被用来实现某种功能。像上篇文章所使用的 koa-router 、koa-bodyparser 等都是中间件。

可能有些人喜欢把中间件理解为插件,但我觉得它们两者并不是同一种概念的东西。插件像是一个独立的工具,而中间件更像是流水线,将加工好的材料继续传递下一个流水线。所以中间件给我的感觉更灵活,可以像零件一样自由组合。

单看中间件有堆栈执行顺序的特点,两者就出现质的区别。

中间件的概念

这张图是 Koa 中间件执行顺序的图示,被称为“洋葱模型”。

中间件按照栈结构的方式来执行,有“先进后出“的特点。

一段简单的代码来理解上图:

app.use(async (ctx, next)=    console.log('--> 1')    next()    console.log('<-- 1') })  app.use(async (ctx, next)=>{    console.log('--> 2')    //这里有一段异步操作    await  new Promise((resolve)=>{        ....    })    await  next()    console.log('<-- 2') })  app.use(async (ctx, next)=>{    console.log('--> 3')    next()    console.log('<-- 3') })  
app.use(async (ctx, next)=>{    console.log('--> 4') }) 

当我们运行这段代码时,得到以下结果

--> 1

--> 2

--> 3

--> 4

<-- 3

<-- 2

<-- 1

在第二个中间件有一段异步操作,所以要加上await,让执行顺序按照预期去进行,否则可能会出现一些小问题。

中间件的使用方式

1.应用中间件

const Koa = require('koa'); const Router = require('koa-router');  const app = new Koa(); const router = new Router(); app.use(async (ctx,next)=>{     console.log(new Date());     await next(); }) router.get('/', function (ctx, next) {     ctx.body="Hello koa"; }) router.get('/news',(ctx,next)=>{     ctx.body="新闻页面" }); app.use(router.routes()); //作用:启动路由 app.use(router.allowedMethods()); //作用: 当请求出错时的处理逻辑 app.listen(3000,()=>{     console.log('starting at port 3000'); });

2.路由中间件

router.get('/', async(ctx, next)=>{     console.log(1)     next() }) router.get('/', function (ctx) {     ctx.body="Hello koa"; })

3.错误处理中间件

app.use(async (ctx,next)=> {     next();     if(ctx.status==404){         ctx.status = 404;         ctx.body="这是一个404页面"     } });

4.第三方中间件

const bodyParser = require('koa-bodyparser'); app.use(bodyParser());

实现验证token中间件

生成token

const Router = require('koa-router') const route = new Router() const jwt = require('jsonwebtoken')  route.get('/getToken', async (ctx)=>{     let {name,id} = ctx.query     if(!name && !id){         ctx.body = {             msg:'不合法',             code:0         }         return     }     //生成token     let token = jwt.sign({name,id},'secret',{ expiresIn: '1h' })     ctx.body = {         token: token,         code:1     } })  module.exports = route

使用 jwt.sign 生成token:

第一个参数为token中携带的信息;

第二个参数为key标识(解密时需要传入该标识);

第三个为可选配置选项,这里我设置过期时间为一小时;

详细用法可以到npm上查看。

使用中间件

app.js:

const {checkJwt,extractors} = require('./check-jwt')  app.use(checkJwt({   jwtFromRequest: extractors.fromBodyField('token'), secretOrKeyL: 'secret',   safetyRoutes: ['/user/getToken'] }))
是否必选 接收类型 备注
jwtFromRequest 函数
authorization
extractors提供的提取函数,支持get、post、header方式提取
这些函数都接收一个字符串参数(需要提取的key)
对应函数:
fromUrlQueryParameter、
fromBodyField、
fromHeader
secretOrKey 字符串 与生成token时传入的标识保持一致
safetyRoutes 数组 不需要验证的路由

使用该中间件后,会对每个路由都进行验证

路由中获取token解密的信息

route.get('/getUser', async ctx=>{     let {name, id} = ctx.payload      ctx.body = {         id,         name,         code:1     } })

通过ctx.payload来获取解密的信息

实现代码

extractors.js 工具函数(用于提取token)

let extractors = {}  extractors.fromHeader = function(header_name='authorization'){     return function(ctx){         let token   = null,             request = ctx.request;         if (request.header[header_name]) {             token = header_name === 'authorization' ?              request.header[header_name].replace('Bearer ', '') :             request.header[header_name];         }else{             ctx.body = {                 msg: `${header_name} 不合法`,                 code: 0             }         }         return token;     } }  extractors.fromUrlQueryParameter = function(param_name){     return function(ctx){         let token   = null,             request = ctx.request;         if (request.query[param_name] && Object.prototype.hasOwnProperty.call(request.query, param_name)) {             token = request.query[param_name];         }else{             ctx.body = {                 msg: `${param_name} 不合法`,                 code: 0             }         }         return token;     } }  extractors.fromBodyField = function(field_name){     return function(ctx){         let token   = null,             request = ctx.request;         if (request.body[field_name] && Object.prototype.hasOwnProperty.call(request.body, field_name)) {             token = request.body[field_name];         }else{             ctx.body = {                 msg: `${field_name} 不合法`,                 code: 0             }         }         return token;     } }  module.exports = extractors

const jwt = require('jsonwebtoken') const extractors = require('./extractors')  /**  *   * @param {object} options   *    @param {function} jwtFromRequest  *    @param {array} safetyRoutes  *    @param {string} secretOrKey   */  function checkJwt({jwtFromRequest,safetyRoutes,secretOrKey}={}){     return async function(ctx,next){         if(typeof safetyRoutes !== 'undefined'){             let url = ctx.request.url             //对安全的路由 不验证token             if(Array.isArray(safetyRoutes)){                 for (let i = 0, len = safetyRoutes.length; i < len; i++) {                     let route = safetyRoutes[i],                         reg = new RegExp(`^${route}`);                        //若匹配到当前路由 则直接跳过  不开启验证                                     if(reg.test(url)){                         return await next()                     }                 }             }else{                 throw new TypeError('safetyRoute 接收类型为数组')             }         }         if(typeof secretOrKey === 'undefined'){             throw new Error('secretOrKey 为空')         }         if(typeof jwtFromRequest === 'undefined'){             jwtFromRequest = extractors.fromHeader()         }         let token = jwtFromRequest(ctx)         if(token){             //token验证             let err = await new Promise(resolve=>{                 jwt.verify(token, secretOrKey,function(err,payload){                     if(!err){                         //将token解码后的内容 添加到上下文                         ctx.payload = payload                     }                     resolve(err)                 })             })             if(err){                 ctx.body = {                     msg: err.message === 'jwt expired' ? 'token 过期' : 'token 出错',                     err,                     code:0                 }                 return             }             await next()         }     } }  module.exports = {     checkJwt,     extractors }

https://gitee.com/ChanWahFung/koa-demo

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!