成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

koa原理淺析

cnio / 3219人閱讀

摘要:應(yīng)用級(jí)別頂層處理在上面中間件執(zhí)行時(shí)看到,會(huì)自動(dòng)幫我們捕獲錯(cuò)誤并處理,如下捕獲錯(cuò)誤在中處理我們看發(fā)現(xiàn)它事實(shí)上是出發(fā)監(jiān)聽(tīng)的事件假如我們沒(méi)有定義回調(diào)怎么辦呢,也為我們定義了默認(rèn)的錯(cuò)誤處理函數(shù)方法做了判斷全文完

koa原理淺析
選取的版本為koa2
原文鏈接

koa的源碼由四個(gè)文件組成

application.js    koa的骨架
context.js        ctx的原型
request.js        request的原型
response.js       response的原型
基本用法
const Koa = require("koa");
const app = new Koa();

app.use(async ctx => {
  ctx.body = "Hello World";
});

app.listen(3000);
初始服務(wù)器

利用http模塊創(chuàng)建服務(wù)器

const app = http.createServer((req, res) => {
    ...
})  
app.listen(3000)

事實(shí)上koa把這些包在了其listen方法中

  listen(...args) {
    debug("listen");
    const server = http.createServer(this.callback());
    return server.listen(...args);
  }

顯然this.callback()返回的是一個(gè)形如下面的函數(shù)

(req, res) => {}
上下文ctx

callback方法如下

  callback() {
    const fn = compose(this.middleware);

    if (!this.listeners("error").length) this.on("error", this.onerror);

    const handleRequest = (req, res) => {
      const ctx = this.createContext(req, res);
      return this.handleRequest(ctx, fn);
    };

    return handleRequest;
  }

ctx在koa中事實(shí)上是一個(gè)包裝了request和response的對(duì)象,從createContext中可以看到起繼承自context

  createContext(req, res) {
    const context = Object.create(this.context);
    const request = context.request = Object.create(this.request);
    const response = context.response = Object.create(this.response);
    context.app = request.app = response.app = this;
    context.req = request.req = response.req = req;
    context.res = request.res = response.res = res;
    request.ctx = response.ctx = context;
    request.response = response;
    response.request = request;
    context.originalUrl = request.originalUrl = req.url;
    context.cookies = new Cookies(req, res, {
      keys: this.keys,
      secure: request.secure
    });
    request.ip = request.ips[0] || req.socket.remoteAddress || "";
    context.accept = request.accept = accepts(req);
    context.state = {};
    return context;
  }

可以看到ctx.request繼承自request,ctx.response繼承自response,查看response和request可以看到里面大都是set和get方法(獲取query,設(shè)置header)等等。并且ctx代理了ctx.request和ctx.response的方法,在源碼中可以看到

delegate(proto, "response")
  .method("attachment")
  .method("redirect")
  .method("remove")
  .method("vary")
  .method("set")
  .method("append")
  .method("flushHeaders")
  .access("status")
  .access("message")
  .access("body")
  .access("length")
  .access("type")
  .access("lastModified")
  .access("etag")
  .getter("headerSent")
  .getter("writable");

/**
 * Request delegation.
 */

delegate(proto, "request")
  .method("acceptsLanguages")
  .method("acceptsEncodings")
  .method("acceptsCharsets")
  .method("accepts")
  .method("get")
  .method("is")
  .access("querystring")
  .access("idempotent")
  .access("socket")
  .access("search")
  .access("method")
  .access("query")
  .access("path")
  .access("url")
  .getter("origin")
  .getter("href")
  .getter("subdomains")
  .getter("protocol")
  .getter("host")
  .getter("hostname")
  .getter("URL")
  .getter("header")
  .getter("headers")
  .getter("secure")
  .getter("stale")
  .getter("fresh")
  .getter("ips")
  .getter("ip");

所以我們可以直接這么寫(xiě)

ctx.url

等價(jià)于

ctx.request.url
中間件

我們?cè)倏匆幌耤allback函數(shù),觀察發(fā)現(xiàn)compose模塊十分的神奇,我暫且把它稱(chēng)為是一個(gè)迭代器,它實(shí)現(xiàn)了中間件的順序執(zhí)行

const fn = compose(this.middleware);

打印fn如下

  function (context, next) {
    // last called middleware #
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      if (i <= index) return Promise.reject(new Error("next() called multiple times"))
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(context, function next () {
          return dispatch(i + 1)
        }))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }

最初接觸koa的時(shí)候我疑惑為什么我寫(xiě)了

ctx.body = "hello world"

并沒(méi)有ctx.response.end()之類(lèi)的方法,事實(shí)上koa已經(jīng)幫我們做了處理,在handleRequest方法中

const handleResponse = () => respond(ctx);

// fnMiddleware即為上面compose之后的fn
fnMiddleware(ctx).then(handleResponse).catch(onerror)

fnMiddleware返回的是一個(gè)promise,在中間件邏輯完成后在respond函數(shù)中最終去處理ctx.body

function respond(ctx) {
  // allow bypassing koa
  if (false === ctx.respond) return;

  const res = ctx.res;
  if (!ctx.writable) return;

  let body = ctx.body;
  const code = ctx.status;

  // ignore body
  if (statuses.empty[code]) {
    // strip headers
    ctx.body = null;
    return res.end();
  }

  if ("HEAD" == ctx.method) {
    if (!res.headersSent && isJSON(body)) {
      ctx.length = Buffer.byteLength(JSON.stringify(body));
    }
    return res.end();
  }

  // status body
  if (null == body) {
    body = ctx.message || String(code);
    if (!res.headersSent) {
      ctx.type = "text";
      ctx.length = Buffer.byteLength(body);
    }
    return res.end(body);
  }

  // responses
  if (Buffer.isBuffer(body)) return res.end(body);
  if ("string" == typeof body) return res.end(body);
  if (body instanceof Stream) return body.pipe(res);

  // body: json
  body = JSON.stringify(body);
  if (!res.headersSent) {
    ctx.length = Buffer.byteLength(body);
  }
  res.end(body);
}
錯(cuò)誤處理

(非首部)中間件層處理(我瞎起的)

對(duì)于每個(gè)中間件可能發(fā)生的錯(cuò)誤,可以直接在該中間件捕獲

app.use((ctx, next) => {

    try {
        ...        
    } catch(err) {
        ...
    }

})

(首部)中間件層處理

事實(shí)上,我們只要在第一個(gè)中間件添加try... catch... ,整個(gè)中間件組的錯(cuò)誤都是可以捕獲的到的。

(應(yīng)用級(jí)別)頂層處理

app.on("error", (err) = {})

在上面中間件執(zhí)行時(shí)看到,koa會(huì)自動(dòng)幫我們捕獲錯(cuò)誤并處理,如下

      try {
        return Promise.resolve(fn(context, function next () {
          return dispatch(i + 1)
        }))
      } catch (err) {
        //  捕獲錯(cuò)誤
        return Promise.reject(err)
      }


//  在ctx.onerror中處理
const onerror = err => ctx.onerror(err);
fnMiddleware(ctx).then(handleResponse).catch(onerror)

我們看ctx.onerror發(fā)現(xiàn)它事實(shí)上是出發(fā)app監(jiān)聽(tīng)的error事件

  onerror(err) {


// delegate
    this.app.emit("error", err, this);

假如我們沒(méi)有定義error回調(diào)怎么辦呢,koa也為我們定義了默認(rèn)的錯(cuò)誤處理函數(shù)

callback方法做了判斷

  callback() {

    ...

    if (!this.listeners("error").length) this.on("error", this.onerror);

    ...
  }

全文完

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/92098.html

相關(guān)文章

  • koa2 總體流程原理淺析(一) 之 koa 啟動(dòng)服務(wù)器解析

    摘要:?jiǎn)?dòng)流程主要的啟動(dòng)流程就是下面的步引入包實(shí)例化編寫(xiě)中間件監(jiān)聽(tīng)服務(wù)器引入包引入包其實(shí)就是引入的一個(gè)繼承于原生的類(lèi)的類(lèi)其中就包含了等原型方法實(shí)例化執(zhí)行,將等對(duì)象封裝在實(shí)例中編寫(xiě)中間件首先判斷的類(lèi)型,不是方法直接拋錯(cuò)是生成器函數(shù)的話用封裝是函數(shù) 啟動(dòng)流程 koa 主要的啟動(dòng)流程就是下面的 4 步:引入 koa 包 => 實(shí)例化 koa => 編寫(xiě)中間件 => 監(jiān)聽(tīng)服務(wù)器 const koa ...

    fsmStudy 評(píng)論0 收藏0
  • koa2 總體流程原理淺析(二) 之 中間件原理

    摘要:任何一層報(bào)錯(cuò),都能用捕獲總結(jié)是一個(gè)非常輕量級(jí)的框架,只實(shí)現(xiàn)了中間件處理流程和對(duì)對(duì)象的封裝。其他的功能都由外部中間件提供。 koa 的中間件機(jī)制巧妙的運(yùn)用了閉包和 async await 的特點(diǎn),形成了一個(gè)洋蔥式的流程,和 JS 的事件流 (捕獲 -> target -> 冒泡) 相似 handleRequest(ctx, fnMiddleware) { const res ...

    zhoutk 評(píng)論0 收藏0
  • koa-router 源碼淺析

    摘要:代碼結(jié)構(gòu)執(zhí)行流程上面兩張圖主要將的整體代碼結(jié)構(gòu)和大概的執(zhí)行流程畫(huà)了出來(lái),畫(huà)的不夠具體。那下面主要講中的幾處的關(guān)鍵代碼解讀一下。全局的路由參數(shù)處理的中間件組成的對(duì)象。 代碼結(jié)構(gòu) showImg(https://segmentfault.com/img/remote/1460000007468236?w=1425&h=1772); 執(zhí)行流程 showImg(https://segmentf...

    SillyMonkey 評(píng)論0 收藏0
  • 淺析koa的洋蔥模型實(shí)現(xiàn)

    摘要:前言被認(rèn)為是第二代,它最大的特點(diǎn)就是獨(dú)特的中間件流程控制,是一個(gè)典型的洋蔥模型。這段代碼就很巧妙的實(shí)現(xiàn)了兩點(diǎn)將一路傳下去給中間件將中的下一個(gè)中間件作為未來(lái)的返回值這兩點(diǎn)也是洋蔥模型實(shí)現(xiàn)的核心。 前言 koa被認(rèn)為是第二代node web framework,它最大的特點(diǎn)就是獨(dú)特的中間件流程控制,是一個(gè)典型的洋蔥模型。koa和koa2中間件的思路是一樣的,但是實(shí)現(xiàn)方式有所區(qū)別,koa2在...

    dabai 評(píng)論0 收藏0
  • generator探幽(1)--koa中間件機(jī)制淺析

    摘要:當(dāng)運(yùn)行到時(shí),不會(huì)暫停,而是直接跳進(jìn)函數(shù)執(zhí)行函數(shù)內(nèi)的代碼。由于函數(shù)中沒(méi)有,因此會(huì)一直執(zhí)行完函數(shù)中的代碼,并返回至函數(shù)中執(zhí)行后面的代碼。 本系列旨在通過(guò)對(duì)co,koa等庫(kù)源碼的研究,進(jìn)而理解generator在異步編程中的重大作用(ps:所有代碼請(qǐng)?jiān)趎ode --harmony或者iojs環(huán)境中運(yùn)行) koa中間件的形式 相信用過(guò)koa的小伙伴一定很熟悉下面這段代碼 var app ...

    Jensen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<