koa2 从入门到进阶(三)-中间件

    |     2020年2月1日   |   koa2, web前端技术   |     0 条评论   |    1070

上一篇讲了 Koa 路由、GET 传值和动态路由。这一篇看 中间件,以及 Koa 特有的 洋葱模型:请求一层层走进去,响应再一层层走出来。

中间件就是匹配路由之前或匹配完成之后要做的那串操作。Express 里中间件能拿到 req、res 和 next;Koa 类似,只是上下文收进 ctx,向下走要 await next()。不调用 next,就不会继续往下匹配。

一、中间件能做什么

  • 执行任何代码;
  • 修改请求和响应对象;
  • 终结请求-响应循环;
  • 调用堆栈中的下一个中间件。

Koa 应用里常见四类:应用级、路由级、错误处理、第三方。第三方中间件后面用到具体模块再展开。

类型 挂载方式 典型用途
应用级 app.use(async (ctx, next) => {}) 匹配任意路由,日志、鉴权、公共数据
路由级 router.get(‘/’, async (ctx, next) => {}) 同一路径上先处理再 next 到下一个处理器
错误处理 await next() 之后看 ctx.status 统一 404 / 异常页
第三方 app.use(某模块()) bodyparser、static、session 等

二、应用级中间件

不写路径的 app.use 会匹配任意路由,而且最先执行。如果不写 await next(),匹配到这里就停,不会再往下走路由。原文把这句话写反了:写了 next 才会继续,不写才会终止。

//引入 koa模块
var Koa = require('koa');
var Router = require('koa-router');

//实例化
var app = new Koa();
var router = new Router();

//匹配任何路由,如果不写next,这个路由被匹配到了就不会继续向下匹配
app.use(async (ctx, next) => {
    console.log("我是一个中间件");
    // 当前路由匹配完成以后继续向下匹配
    await next();
});

//配置路由
router.get('/', async (ctx) => {
    ctx.body = "首页";
});
router.get('/news', async (ctx) => {
    ctx.body = "新闻列页面";
});

//启动路由
app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);

访问任意已配置路由时,控制台会先打出「我是一个中间件」,再进入对应的 router.get。

三、路由级中间件

同一个路径可以挂两条 router.get('/'):第一条打日志并 await next(),第二条才给页面写 body。第一条不写 next,就不会进入第二条。

//引入 koa模块
var Koa = require('koa');
var Router = require('koa-router');

//实例化
var app = new Koa();
var router = new Router();

//配置路由
router.get('/', async (ctx, next) => {
    console.log("控制台打印");
    // 当前路由匹配完成以后继续向下匹配
    await next();
});
router.get('/', async (ctx) => {
    ctx.body = "首页";
});
router.get('/news', async (ctx) => {
    ctx.body = "新闻列页面";
});

//启动路由
app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);

四、错误处理中间件

页面响应会带 status。Koa 默认没匹配到路由时 status 仍是 404。把判断写在 await next() 之后,等下游路由跑完再看结果:匹配到 /news 就正常显示;少写一个 s 变成 /new,就可以给前端返回「404 页面」。

//引入 koa模块
var Koa = require('koa');
var Router = require('koa-router');

//实例化
var app = new Koa();
var router = new Router();

//匹配任何路由,如果不写next,这个路由被匹配到了就不会继续向下匹配
app.use(async (ctx,next)=>{
    await next();
    //如果页面找不到
    if(ctx.status==404){
        ctx.status = 404;
        ctx.body="404 页面"
    }
});

//配置路由
router.get('/', async (ctx) => {
    ctx.body = "首页";
});
router.get('/news', async (ctx) => {
    ctx.body = "新闻列页面";
});

//启动路由
app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);

if 能不能放到 next 之前?放到前面时,下游还没跑,status 往往仍是默认 404,会直接拦住,匹配不到后面的路由。这正是下一节洋葱模型要解释的。

五、洋葱模型:先进后出

Koa 常被称为第二代 Node Web 框架,最大特点就是中间件流程控制:先逐级处理 request,匹配完路由后再逐级处理 response。登录系统也是这个思路——先把账号密码送进后台,数据库匹配完再处理,处理完再返回前端。

//引入 koa模块
var Koa = require('koa');
var Router = require('koa-router');

//实例化
var app = new Koa();
var router = new Router();

app.use(async (ctx, next) => {
    console.log('1、这是第一个中间件01');
    await next();
    console.log('5、匹配路由完成以后又会返回来执行中间件');
});

app.use(async (ctx, next) => {
    console.log('2、这是第二个中间件02');
    await next();
    console.log('4、匹配路由完成以后又会返回来执行中间件');
});

router.get('/', async (ctx) => {
    console.log('3、匹配到了news这个路由');
    ctx.body = "首页";
});

//启动路由
app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);

访问 localhost:3000/ 时,控制台顺序是 1 → 2 → 3 → 4 → 5。所以错误处理要把 status 判断放在 next 之后:等路由跑完(或没匹配上)再决定要不要输出 404。

一句话总结:Koa 中间件靠 await next() 往下传;不写 next 就停。洋葱模型先进后出,404 判断必须放在 next 之后。

转载请注明来源:koa2 从入门到进阶(三)-中间件
本文链接地址:https://ai.zhousir.top/?p=2379
回复 取消