木灵鱼儿
阅读:1472
koa教程2 中间件和洋葱模型
koa的中间件通过new出Koa对象的use方法进行注册,每个注册的中间件其实就是一个函数,函数接收两个参数:ctx
和next
。
ctx是上下文对象,是中间件共用的一个对象,也就是共享的对象,我在1中间件对这个ctx添加或者处理内容,再进过2中间件时,可以获取到1所做的所有处理后的结果。
next是一个回调函数,他是运行下一个中间件的关键,如果没有next,那么运行就结束在当前中间件中。
也正是这个next,才能达到koa所提倡的洋葱模型。
const Koa = require("koa");
const app = new Koa();
app.use((ctx, next) => {
console.log(1)
next();
console.log(3)
})
app.use((ctx, next) => {
console.log(2)
next();
console.log(4)
})
app.listen(8080);
上面这段代码最终输出:
//1
//2
//3
//4
这也就是所谓的洋葱模型,先运行fn1的上半段,在运行fn2的上半段,由于更多的中间件,所以结束就运行fn2的下半段,在运行fn1的下半段。
深入理解
首先我知道next做了什么?
next永远会返回一个promise对象,这个fn1的next返回的是fn2抛出的promise对象。
app.use((ctx, next) => {
console.log(1)
const a = next();
console.log(a);
console.log(4)
})
app.use((ctx, next) => {
console.log(2)
console.log(3)
return "abc";
})
输出a得到Promise { 'abc' }
,也就是说fn2 return的值成为了promise的值。
async...await...
在koa中常常给中间件使用async...await
;我们了解下这两个代码做了什么?
await的功用:
- 对promise对象是一个求值,除了promise还可以是表达式
- 阻塞当前线程(异步转同步),但不是真的阻塞,只是当前线程不继续运行了,可以将空出的位置运行其他的
app.use(async (ctx, next) => {
console.log(1)
const a = await next();
console.log(a);
console.log(4)
})
app.use((ctx, next) => {
console.log(2)
console.log(3)
return "abc";
})
输出a得到:"abc"
await一般用于异步转同步,比如异步请求,异步资源操作啊。
async的作用
- 配置await使用,使用await必须加async
- 在函数前面加上async,函数结果会被包装成一个promise对象
async function test() {
return "abc";
}
console.log(test());
//Promise { 'abc' }
那么async和await的使用目的是什么?
为了保持洋葱模型,假如fn2里面运行的代码是一个异步的方法,那么很有可能是我们中间件已经运行完毕了,异步才运行结果处理,那么就无法保持koa所提倡的洋葱模型了,因为fn2中间件没有真正运行完毕。
保持洋葱模型
使用async和awite可以阻塞异步转同步,但是用法上却要注意!
const Koa = require("koa");
const axios = require("axios");
const app = new Koa();
app.use((ctx, next) => {
console.log(1)
next();
console.log(4)
})
app.use(async (ctx, next) => {
console.log(2)
const a = await axios.get("https://www.mulingyuer.com/");
console.log(3)
})
app.listen(8080);
上面代码中我们给中间件2使用async和await,让axios请求转为同步的,那么按道理,应该是输出:1,2,3,4
的,但是实际上确实不同的。
实际输出的是:1,2,4,3
原因是因为中间2的await阻塞的他的运行,他并没有阻塞中间件1中的next运行,所以,如果要真正的保持洋葱模型,那么所有的中间件都应该使用async和await
于是乎:
const Koa = require("koa");
const axios = require("axios");
const app = new Koa();
app.use(async (ctx, next) => {
console.log(1)
await next();
console.log(4)
})
app.use(async (ctx, next) => {
console.log(2)
const a = await axios.get("https://www.mulingyuer.com/");
console.log(3)
})
app.listen(8080);
最终输出:
结果:1,2,3,4
为什么要保持洋葱模型
洋葱模型的保持有一个好处,就是我们在next函数的后面,永远可以得到中间件运行后的结果,next将中间件分作了两半,如果有很多个中间件,我们在第一个中间件的next后面,可以得到所有中间件运行后的结果。
这极大的提高的代码逻辑,使用时也更加方便,不然都是异步,单个处理可能很简单,那么集中处理就非常麻烦,这好处是显而易见的了。
做个例子:
const Koa = require("koa");
const axios = require("axios");
const app = new Koa();
app.use(async (ctx, next) => {
console.log(1)
await next();
console.log(ctx.a);
console.log(4)
})
app.use(async (ctx, next) => {
console.log(2)
const a = await axios.get("https://www.mulingyuer.com/");
ctx.a = a;
console.log(3)
})
app.listen(8080);
我们在中间件2中将axios获取到的内容赋值给ctx上下文对象,然后再中间件1中,next后面输出ctx.a
,是可以得到axios所获取的内容的。
参数的传递也是非常方便,得力于洋葱模型。
版权申明
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。
相关推荐
生成器 Generator
简介Promise的出现,我们可以将回调进行反客为主,不在受回调调用者的限制(控制反转和信任问题),但是它并没有解决异步编程导致的代码顺序问题,有没有一种方式,可以让我们的代码虽然是异步的,但是书写顺序却是同步的,这样完全符合我们人类大脑理解方式?console.log("同步的1"); axios({ ... }).then(() => { console.log("异步的回调,如果需要等待结果处理,代码只能写在这个回调里") }); console.log("同步的2")很明显,如果我们希望"同步...
centos 安装最新版 node.js
[hide]先用yum 安装一次node先升级yum update 提示下载程序,回复y安装nodeyum install nodejsyum安装的node一般都不是最新的,所以我们还需要升级node的版本npm i n -g全局安装一个n插件n lts表示安装最新的稳定版!一些常用命令n ls查看已安装的node版本n rm 16.0.3删除16.0.3版本更多功能阅读插件文档:n等待安装完毕后,此时其实版本已经安装好了,但是输入node -v还是低版本,是因为centos的用户缓存问题。我们退出登录再登录就会刷新缓存了,此时的node -v输出的就是最新版本exit 退...
class 与 await结合
class As { constructor() {} then(resolve, reject) { setTimeout(() => { resolve(true); }, 3000) } } (async () => { const b = await new As(); console.log(b); })()
node npm yarn 如何同时运行多个指令
node的bash命令其实是有对应的指令字符的,但是,这个指令只能再linux,mac上才有生效,再windows是无效的,所以了解一下就行。命令说明&&顺序执行多条命令,当碰到执行出错的命令后将不执行后面的命令&并行执行多条命令||顺序执行多条命令,当碰到执行正确的命令后将不执行后面的命令|管道符windows上进行多命令"scripts": { "dev": "cross-env NODE_ENV=development webpack --env development --progress -...
koa教程2 HttpBearerAuth 传递token
前端发送token到后端,一般有三种方式:链接中携带token,query参数post这种请求body中携带token头信息携带token但是事实上http已经为我们提供了传token的方式,是一个相对来说比较安全的方式,就是HttpBearerAuth 其实他也是一个头信息来着,只是相对来说多了一点操作,在apipost软件中,我们可以直接可视化设置如果是axios,则是需要添加一个头信息:axios({ method: 'get', url: url, responseType: 'blob', headers: { 'Authoriz...

koa教程2 微信openid登录
微信的openid,前端登录时,会发送一个code码到后端,后端接收到这个code,然后配合appid、appsecret参数,像wx发送一个get请求。wx的请求地址:GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code具体查看文档:微信官方文档-服务端可以看到这段get请求有四个参数:属性类型默认值必填说明appidstring 是小程序 appIdsecretstring 是...
koa教程2 权限分级控制
在之前的代码中,我们使用了一个scope属性来表示用户的权限,但是scope是一个数字,他的阅读性是非常差的,当其他人开发的时候,可能并不知道你这个数字是什么意思?所以,我们需要将它转义成文本。为了方便判断,我们在enum.js中增加一个用户等级的对象enum.js://通用判断是否存在该类型 function isThisType(val) { let flag = false; Object.keys(this).find(key => { if (this[key] === val) { return flag = true; } })...
koa教程2 token鉴权
之前我们在token中传入我们的自定义参数,现在我们要做一个token鉴权,很简单,如果他的token合法,就可以访问这个路由,如果不合法,就打回。这个也不能说是真正意义上的鉴权,因为他还没有判断scope值,所以只能说是简单判断。做这个判断我们就可以通过中间件的形式,于是我们在middlewares目录下创建一个auth.js文件auth.js:const parseBearerToken = require("@utils/paresBearerToken"); const jwt = require("jsonwebtoken"); cons...
koa教程2 用户登录
登录类型用户注册之后,自然是用户登录,登录的时候我们需要考虑到登录的方式,往往在正式的项目里面,登录的方式可能会有很多种:账号密码登录手机号登录小程序一键登录那么我们登录的时候就要进行判断,设置一个type的属性,用于在登录时判断用户是什么类型的登录,这个由前端那边设定参数。type:1为账号密码登录,2为手机号登录,3为小程序一键登录作为判断,我们可以写一个查询表(枚举),在项目根目录创建一个lib目录,在里面创建一个enum.js文件。enum.js://通用判断是否存在该类型 function isThisType(val) { let flag = false; Obje...
node 自定义路径别名
用过vue cli的话,对路径中的@应该不陌生,他表示根目录,但是在node的环境里面,我们编写模块并引入时,并没有这种方便的写法。在node环境中,我们可以通过process.cwd()来表示根目录,但是这样的话,每次使用绝对路径引入,路径总是需要写一大堆拼接。const db = require(`${process.cwd()}/config/config`).database;这样略显麻烦,于是我们可以在全局写一个引入插件的方法:global.myRequire = function (path) { return require(`${process.cwd()}/${pa...