koa的使用和中间件

引言

近期重温了一下 node ,对 node 的 koajs 技术框架进行了学习,在此概括了一些使用过程当中的一些理解和 Koa 主要机制的解析。node

缘由

咱们为何要使用 Koa 框架呢,当咱们使用 node 原生 http 模块编写后端服务尤为是在开发复杂的业务逻辑时,原生包每每难以维护后续需求迭代。此时咱们能够选择一个合适的 Web 框架来支持开发。web

概述

Koa 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。Koa 是由 Express 原班人马(TJ Holowaychuk)开发,经过使用组合各类更高级的异步流程控制中间件,来免除繁琐的回调函数嵌套,并极大提高常见错误的处理效率。Koa 做为 Web 开发微框架,能够用于传统 Web 应用开发、做为服务器端接口、做为独立的API层、网关等等场景。后端

基本使用

咱们来看一个基本 Demo 的使用:设计模式

const Koa = require("koa");
const app = new Koa();
app.use(async (ctx, next) => {
  // 咱们在这里定义一个日志,咱们的业务逻辑能够写在next()以前和以后
  // 在返回时间以前设置一个时间戳
  const start = Date.now();
  // next()进入下一个use中
  await next();
  // 在返回时间以后再返回一个时间戳
  const end = Date.now();
  console.log(`请求${ctx.url} 耗时${parseInt(end - start)}ms`);
});
app.use((ctx, next) => {
  // 咱们等待120毫秒后返回 此处能够作相关业务逻辑处理
  const expire = Date.now() + 120;
  while (Date.now() < expire) {
    ctx.body = {
      name: "myName",
    };
  }
});
app.listen(3000, () => {
  console.log("start...");
});
复制代码

打印结果:数组

image

执行流程为:服务器

image

对于 Koa 框架,咱们这里不过多讲解官方的文档和源码的解析,有兴趣的同窗能够在 Koa 官网进行了解或者看一下这篇对 Koa 源码解析的文章 jelly.jd.com/article/5f8…markdown

Koa 的目标是用更简单化、流程化、模块化的方式实现回调的业务逻辑,为了实现这个目的,Koa 使用了上下文和中间件的机制,为了更好地理解这两个机制,咱们来简单手动实现一下。app

上下文

koa 为了可以简化 API ,引入上下文 context 概念,将原始请求对象 req 和响应对象res封装并挂载到 context 上,而且在 context 上设置 getter 和 setter ,从而简化操做。框架

image

首先咱们建立 request、response、context文件,并在其中加入 get 和 set 属性。koa

// request.js
module.exports ={
    get url(){
        return this.req.url
    },
    get method(){
        return this.req.method.toLowerCase()
    }
    // ...
}
复制代码
// response.js
module.exports = {
    get body(){
        return this._body
    },
    set body(val){
        this._body = val
    }
    // ...
}
复制代码
// context.js
module.exports = {
    get url() {
        return this.request.url
    },
    get body() {
        return this.response.body
    },
    set body(val){
        this.response.body = val
    },
    get method() {
        return this.request.method
    }
    // ...
}
复制代码

咱们将 ctx 把 res、req、response、request 进行相互挂载起来,进行相互关联上下文。

createContext(req, res) {
  const ctx = Object.create(context);
  ctx.request = Object.create(request);
  ctx.response = Object.create(response);
  ctx.req = ctx.request.req = req;
  ctx.res = ctx.response.res = res;
  return ctx;
}
复制代码

而后在createServer中将req,res传入createContext函数建立上下文。

const server = http.createServer(async (req, res) => {
  //...
  let ctx = this.createContext(req, res);
  //...
复制代码

中间件

那么什么是中间件呢,中间件是 Koa 框架的核心扩展机制,主要用于抽象 HTTP 请求过程,在单一请求响应过程当中加入中间件,能够更好地应对复杂的业务逻辑。在 HTTP 请求的过程当中,中间件至关于一层层的滤网,每一个中间件在HTTP处理过程当中经过改写请求和响应数据、状态,实现相应业务逻辑。Koa 中间件机制就是函数式组合概念,将一组须要顺序执行的函数复合为一个函数,外层函数的参数实际是内层函数的返回值。咱们能够将中间件视为设计模式中的责任链模式。

经过以上执行流程,咱们能够看出中间件的执行顺序,流程是一层层的打开,而后一层层的闭合,就像剥洋葱同样,早期的 Python 为这种执行方式起了一个很好听的名字,洋葱模型。

image

实现这种洋葱圈机制的中间件有不少种实现思路,咱们用递归的思想来实现一下。 咱们首先声明一个 compose 合成函数,该函数返回一个函数的组合并传入咱们的上下文 ctx 对象,而后声明须要每次执行的异步函数并返回函数中返回第一层的执行承诺,咱们判断若是取到的本层函数为空那么返回一个空的承诺,不然返回执行本次方法自己的执行函数并传入下一个函数的执行,咱们来看一下代码

// myKoa
compose(middlewares) {
    return function (ctx) {
      return dispatch(0);
      // i=>表示返回哪一个异步函数
      function dispatch(i) {
        // 取出第一个洋葱圈
        let fn = middlewares[i];
        // 下面的洋葱圈自己都是异步函数,咱们只能返回承诺Promise对象,经过地柜方式
        if (!fn) {
          return Promise.resolve();
        }
        return Promise.resolve(
          // 执行本次方法自己 每次执行的放入的next对象是下一层执行承诺
          fn(ctx, function next() {
            // 返回下一层洋葱圈 执行下一层
            return dispatch(i + 1);
          })
        );
      }
    };
  }
复制代码

咱们在 constructor 构造方法中初始化一个数组 middlewares

// myKoa
  constructor() {
    this.middlewares = [];
  }
复制代码

而后在 createServer 中初始化 compose 组合函数,而后传入 ctx 上下文对象并执行该组合函数。

// myKoa
// ...
const server = http.createServer(async (req, res) => {
  //...
  const fn = this.compose(this.middlewares);
  await fn(ctx);
  //...
复制代码

最后咱们在 use 方法中接受每次传过来的函数并将其存到 middlewares数组中

// myKoa
use(middleware) {
  this.middlewares.push(middleware);
}
复制代码

咱们测试一下:

const MyKoa = require("./myKoa");
const app = new MyKoa();
const delay = () => new Promise((resolve) => setTimeout(() => resolve(), 2000));
app.use(async (ctx, next) => {
  ctx.body = "1";
  await next();
  ctx.body += "5";
});
app.use(async (ctx, next) => {
  ctx.body += "2";
  await delay();
  await next();
  ctx.body += "4";
});
app.use(async (ctx, next) => {
  ctx.body += "3";
});
app.listen(3000, () => {
  console.log("start...");
});
复制代码

image

myKoa.js完整源码;

const http = require("http");
const context = require("./context");
const request = require("./request");
const response = require("./response");
class MyKoa {
  constructor() {
    this.middlewares = [];
  }
  listen(...args) {
    // 建立http server
    const server = http.createServer(async (req, res) => {
      // 建立上下文
      let ctx = this.createContext(req, res);
      const fn = this.compose(this.middlewares);
      await fn(ctx);
      // 数据响应
      res.end(ctx.body);
    });
    // 启动监听
    server.listen(...args);
  }
  use(middleware) {
    this.middlewares.push(middleware);
  }
  createContext(req, res) {
    const ctx = Object.create(context);
    ctx.request = Object.create(request);
    ctx.response = Object.create(response);
    ctx.req = ctx.request.req = req;
    ctx.res = ctx.response.res = res;
    return ctx;
  }

  compose(middlewares) {
    return function (ctx) {
      return dispatch(0);
      // i=>表示返回哪一个异步函数
      function dispatch(i) {
        // 取出第一个洋葱圈
        let fn = middlewares[i];
        // 下面的洋葱圈自己都是异步函数,咱们只能返回承诺Promise对象,经过递归方式
        if (!fn) {
          return Promise.resolve();
        }
        return Promise.resolve(
          // 执行本次方法自己 每次执行的放入的next对象是下一层执行承诺
          fn(ctx, function next() {
            // 返回下一层洋葱圈 执行下一层
            return dispatch(i + 1);
          })
        );
      }
    };
  }
}

module.exports = MyKoa;

复制代码

以上咱们实现了一个简单的中间件洋葱圈模型机制。Koa 中间件能够对请求和响应同时进行拦截,这是Web框架里少有的功能,其余 Web 框架只对请求进行拦截,不对响应进行拦截好比 Express,因此在中间件机制上,Koa 是占有优点的。固然 Koa 的这种机制相比于 Express 会更复杂一些,中间件数量增多叠加起来以后,请求和响应的拦截就会造成相似回形针同样的调用。

image

总结

koa2 框架将会是是 node.js Web开发方向大势所趋的普及框架,本文和你们一块儿看了一下 Koa 的基本使用,简单实现了一下中间件机制和上下文。以上就是本文的所有内容,但愿对你们的学习有所帮助

相关文章
相关标签/搜索