国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

koa源碼分析系列(一)

Atom / 399人閱讀

摘要:很明顯是一個構造函數。默認為根據原生的對象生成一個對象回調函數處理服務器響應可以看到,方法返回的函數就是方法所需要的回調函數。

koa 是什么這里不介紹了,這里通過一個小例子結合源碼講一講它的實現。

koa 源碼結構

通過 npm 安裝 koa(v2.2.0) 后,代碼都在 lib 文件夾內,包括 4 個文件,application.js, context.js, request.js, response.js。

application.js 包含 app 的構造以及啟動一個服務器

context.js app 的 context 對象, 傳入中間件的上下文對象

request.js app 的請求對象,包含請求相關的一些屬性

response.js app 的響應對象,包含響應相關的一些屬性

本文主要關于 application.js 。

先看一個最簡單的例子

// app.js
const Koa = require("koa")
const app = new Koa()

app.use(ctx => {
    ctx.body = "hello world"
})

app.listen(3000)

然后通過 node app.js 啟動應用,一個最簡單的 koa 服務器就搭建好了,瀏覽器訪問 http://localhost:3000,服務器返回一個 hello world 的響應主體。

源碼分析

接下來通過源碼看看這個服務器是怎么啟動的。

const app = new Koa(), 很明顯 Koa 是一個構造函數。

module.exports = class Application extends Emitter {}

Application 類繼承了 nodejs 的 Events 類,從而可以監聽以及觸發事件。

看一下構造函數的實現。

constructor() {
    super();

    this.proxy = false;
    this.middleware = [];
    this.subdomainOffset = 2;
    this.env = process.env.NODE_ENV || "development";
    this.context = Object.create(context);
    this.request = Object.create(request);
    this.response = Object.create(response);

構造函數定義了一些 app 的實例屬性,包括 proxy, middleware, subdomainOffset, env, context, request, response等。

至此 我們就生成了一個 app 的koa實例。

接下來就該用 app.use(middleware) 來使用中間件了。

use(fn) {
    if (typeof fn !== "function") throw new TypeError("middleware must be a function!");
    if (isGeneratorFunction(fn)) {
      deprecate("Support for generators will be removed in v3. " +
                "See the documentation for examples of how to convert old middleware " +
                "https://github.com/koajs/koa/blob/master/docs/migration.md");
      fn = convert(fn);
    }
    debug("use %s", fn._name || fn.name || "-");
    this.middleware.push(fn);
    return this;
  }

首先會驗證傳入的參數是否是一個函數。如果不是一個函數,會報錯。之后如果傳入的函數是一個generator 函數,那么會將這個函數轉化為一個 async 函數。使用的是 koa-convert 模塊, 這是一個很重要的模塊,能將很多 koa1 時代下的中間件轉化為 koa2 下可用的中間件。并且注意到

Support for generators will be removed in v3.

在 koa3 中,將默認不支持 generator 函數作為中間件。

之后將傳入的中間件函數推入 middleware 數組中,并且返回 this 以便鏈式調用。

app.use() 只是定義了一些要使用的中間件,并將它們放入 middleware 數組中,那么怎么使用這些中間件。來看看 app.listen 方法。

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

app.listen 算是 node 原生 listen 方法的語法糖。通過 app.callback 方法生成一個 http.createServer 方法所需要的回調函數,然后再調用原生 http server 的 listen 方法。事實上也可以發現,app 的 listen 方法接收 http server 的 listen 方法一樣的參數。

那么再看看 app 的 callback 這個方法了,也是最重要的一個方法。

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

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

    const handleRequest = (req, res) => {
      res.statusCode = 404; // 默認為 404 
      const ctx = this.createContext(req, res);
      // 根據 node.js 原生的 req, res 對象生成一個 ctx 對象
      const onerror = err => ctx.onerror(err);
      // onerror 回調函數
      const handleResponse = () => respond(ctx);
      // 處理服務器響應
      onFinished(res, onerror);
      return fn(ctx).then(handleResponse).catch(onerror);
    };

    return handleRequest;
  }

可以看到,callback 方法返回的 handleRequest 函數就是 http.createServer 方法所需要的回調函數。

callback 函數內,首先通過 koa-compose 模塊將所有的中間件合并成一個中間件函數,以供 app.use 方法調用。隨后監聽一個 error 事件,onerror 作為默認的錯誤處理函數。

onerror(err) {
    assert(err instanceof Error, `non-error thrown: ${err}`);

    if (404 == err.status || err.expose) return;
    if (this.silent) return;
    const msg = err.stack || err.toString();
    console.error();
    console.error(msg.replace(/^/gm, "  "));
    console.error();
  }

onerror 函數只是僅僅輸出 error.stack 作為錯誤信息。

handleRequest 函數內完成了對請求的處理以及對響應結果的返回。首先 app.createContext 方法生成一個 ctx 供中間件函數 fn 調用。

createContext(req, res) {
    const context = Object.create(this.context);
    const request = context.request = Object.create(this.request);
    // request 屬性
    const response = context.response = Object.create(this.response);
    // response 屬性
    context.app = request.app = response.app = this;
    // request 和 response 上獲得 app 屬性,指向這個 app 實例
    context.req = request.req = response.req = req;
    // req 屬性,req 是原生 node 的請求對象
    context.res = request.res = response.res = res;
    // res 屬性,res 是原生 node 的響應對象
    request.ctx = response.ctx = context;
    // request 和 response 上獲得 ctx 屬性,指向 context 對象
    request.response = response;
    response.request = request;
    // request 和 response 互相指向對方
    context.originalUrl = request.originalUrl = req.url;
    // 獲得 originalUrl 屬性,為原生 req 對象的 url 屬性
    context.cookies = new Cookies(req, res, {
      keys: this.keys,
      secure: request.secure
    }); // cookie 屬性
    request.ip = request.ips[0] || req.socket.remoteAddress || "";
    // ip 屬性
    context.accept = request.accept = accepts(req);
    // accept 屬性,是個方法,用于判斷 Content-Type 
    context.state = {};
    // context.state 屬性,用于保存一次請求中所需要的其他信息
    return context;
  }

所以,createContext 方法將一些常用的屬性,如 resquest , response, node 原生 req, res 掛載到 context 對象上。

再來看這句話 const handleResponse = () => respond(ctx).

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]) { // 這里具體是指 204 205 304 三種
    // strip headers
    ctx.body = null;
    return res.end();
  }

  if ("HEAD" == ctx.method) { // 如果是 `HEAD` 方法
    if (!res.headersSent && isJSON(body)) {
      ctx.length = Buffer.byteLength(JSON.stringify(body));
    }
    return res.end();
  }
  // status body
  if (null == body) { // 如果沒有設置 body , 設置ctx.message 為 body。當然默認是 Not Found ,因為 status 默認是 404
    body = ctx.message || String(code);
    if (!res.headersSent) {
      ctx.type = "text";
      ctx.length = Buffer.byteLength(body);
    }
    return res.end(body);
  }
  // 以下根據 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);
}

所以 respond 函數的作用就是,根據傳入的 ctx 對象的 body ,method 屬性來決定對 request 處理的方式以及如何 response。

onFinished(res, onerror)

首先看看 onFinished(res, listener) 函數的介紹

Attach a listener to listen for the response to finish. The listener will be invoked only once when the response finished. If the response finished to an error, the first argument will contain the error. If the response has already finished, the listener will be invoked.

也就是當服務端響應完成后,執行 listener 回調函數,如果響應過程中有錯誤發生,那么 error 對象將作為 listen 回調函數的第一個參數,因此 onFinished(res, onerror) 表示 當 koa 服務器發送完響應后,如果有錯誤發生,執行 onerror 這個回調函數。

return fn(ctx).then(handleResponse).catch(onerror)。來看看這一句,fn 之前說過了,是所有的中間件函數的 “集合”, 用這一個中間件來表示整個處理過程。同時 fn 也是一個 async 函數,執行結果返回一個 promise 對象,同時 handleResponse 作為其 resolved 函數,onerror 是 rejected 函數。

總結

總結一下,application.js 描述了 一個 koa 服務器(實例)生成的整個過程。

new Koa() 生成了一個 koa 實例

app.use(middleware) 定義了這個 app 要使用的中間件

app.listen 方法,通過 callback 將合并后的中間件函數轉化成一個用于 http server.listen 調用的回調函數,之后調用原生的 server.listen 方法。

全文完

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/82772.html

相關文章

  • B站Up主-山地人-這位老哥2019年的前端自學計劃進展如何?——講個B站Up主自學前端85天的故

    摘要:前言自從上次在掘金發布年山地人的前端完整自學計劃講一個站主山地人的天前端自學故事以來,一眨眼山地人老哥在站做主已經有天了。所以這個體系里的一些框架包括也是山地人年自學計劃的一部分。月底,山地人老哥開啟了的兩個專題。 前言 自從上次在掘金發布【2019年山地人的前端完整自學計劃——講一個B站UP主山地人的40天前端自學故事】 以來,一眨眼山地人老哥在B站做Up主已經有85天了。 時隔一個...

    cocopeak 評論0 收藏0
  • 中間件執行模塊koa-Compose源碼分析

    摘要:原文博客地址,歡迎學習交流點擊預覽讀了下的源碼,寫的相當的精簡,遇到處理中間件執行的模塊決定學習一下這個模塊的源碼。當在下游沒有更多的中間件執行后,堆棧將展開并且每個中間件恢復執行其上游行為。 原文博客地址,歡迎學習交流:點擊預覽 讀了下Koa的源碼,寫的相當的精簡,遇到處理中間件執行的模塊koa-Compose,決定學習一下這個模塊的源碼。 閱讀本文可以學到: Koa中間件的加載...

    imtianx 評論0 收藏0
  • express分析和對比

    摘要:前言目前最新版本是所以本文分析也基于這個版本。源碼分析直接切入主題由于目前是一個獨立的路由和中間件框架。所以分析的方向也以這兩個為主。源碼去年的時候有分析過現在對比分析思考下。 前言 目前express最新版本是4.16.2,所以本文分析也基于這個版本。目前從npm倉庫上來看express使用量挺高的,express月下載量約為koa的40倍。所以目前研究下express還是有一定意義...

    mmy123456 評論0 收藏0
  • Express與Koa中間件機制分析

    摘要:目前使用人數眾多。通過利用函數,幫你丟棄回調函數,并有力地增強錯誤處理。這個系列的博客主要講解和的中間件機制,本篇將主要講解的中間件機制。其中間件機制的核心為內部方法的實現。 提到 Node.js 開發,不得不提目前炙手可熱的2大框架 Express 和 Koa。 Express 是一個保持最小規模的靈活的 Node.js Web 應用程序開發框架,為 Web 和移動應用程序提供一組...

    zilu 評論0 收藏0
  • 深入探析koa之中間件流程控制篇

    摘要:到此為止,我們就基本講清楚了中的中間件洋蔥模型是如何自動執行的。 koa被認為是第二代web后端開發框架,相比于前代express而言,其最大的特色無疑就是解決了回調金字塔的問題,讓異步的寫法更加的簡潔。在使用koa的過程中,其實一直比較好奇koa內部的實現機理。最近終于有空,比較深入的研究了一下koa一些原理,在這里會寫一系列文章來記錄一下我的學習心得和理解。 在我看來,koa最核心...

    fuchenxuan 評論0 收藏0

發表評論

0條評論

Atom

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<