Koajs中介軟體之context(三)

petruslaw發表於2019-02-16

Koajs中介軟體之定義(一)
Koajs中介軟體之next(二)
Koajs中介軟體之context(三)

第一篇文章中我們講過,“在Koa中,中介軟體是指連貫整個 Koa 應用程式,並共享資源的獨立外掛”,注意兩個詞,“連貫”與“共享資源”,與上面的程式碼一一對應,“連貫”對應“next”,“共享資源對應context”。
Koa 中通過 context 處理上下文,下面分析一下 context。

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.state = {};
    return context;
}

不停的建立呼叫 context 物件,避免對context產生影響,初始 context 物件使用 Object.create() 來克隆物件
context 中包含了幾個個主要屬性,其中比較重要的有request、response、state。
Koa 中 分別提供 request.js 與 response.js 對原生 req 與 res 物件進行了處理,使得更易操作,例如在request.js 中將 req 原始請求引數由字串型別替換為物件型別,“?a=1&b=2” <=> {a: 1, b: 2}。

delegates

為了簡化對於 context 的操作,Koa中 把 context 中需要呼叫的方法和屬性讀取,進行了委託。
有興趣的可以研究研究 delegates 這個庫。

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`);

例如我們常用的 “ctx.redirect(xxx)” 方法等同於 ctx.reponse.redirect(xxx);
例如輸出內容 ctx.body = `xxx` 等同於 ctx.response.body = `xxx`;

set & get 處理

對於context中的部分值的設定,進行了 set get 處理,例如 body 物件

get body() {
    return this._body;
},
set body(val) {
    const original = this._body;
    this._body = val;

    if (this.res.headersSent) return;

    // no content
    if (val == null) {
        if (!statuses.empty[this.status]) this.status = 204;
        this.remove(`Content-Type`);
        this.remove(`Content-Length`);
        this.remove(`Transfer-Encoding`);
        return;
    }

    // set the status
    if (!this._explicitStatus) this.status = 200;

    // set the content-type only if not yet set
    const setType = !this.header[`content-type`];

    // string
    if (typeof val == `string`) {
        if (setType) this.type = /^s*</.test(val) ? `html` : `text`;
        this.length = Buffer.byteLength(val);
        return;
    }

    // buffer
    if (Buffer.isBuffer(val)) {
        if (setType) this.type = `bin`;
        this.length = val.length;
        return;
    }

    // stream
    if (typeof val.pipe == `function`) {
        onFinish(this.res, destroy.bind(null, val));
        ensureErrorHandler(val, this.ctx.onerror);

        // overwriting
        if (original != null && original != val) this.remove(`Content-Length`);

        if (setType) this.type = `bin`;
        return;
    }

    // json
    this.remove(`Content-Length`);
    this.type = `json`;
}

當我們通過 ctx.body 設定輸出內容的時候實際上在 Koa 內部進行了一系列處理,例如設定 body 未 null 的情況下,會將 Http 返回狀態設定為204。

總體來說,Koa 中關於 context 的原始碼初看比較複雜,實際上是非常簡潔易明的。

參考資料

Mdn 類
Koa 官網

相關文章