Node.js Egg.js框架的中間件bodyParser使用詳解 源碼解析

框架內置了 bodyParser 中間件對請求 body 解析成 object 掛載到 ctx.request.body 上。HTTP 協議中並不建議在通過 GET、HEAD 方法訪問時傳遞 body,所以我們無法在 GET、HEAD 方法中按照此方法獲取到內容。

框架對 bodyParser 設置了一些默認參數,配置好之後擁有以下特性:

  • 當請求的 Content-Type 爲 application/jsonapplication/json-patch+jsonapplication/vnd.api+jsonapplication/csp-report 時,會按照 json 格式對請求 body 進行解析,並限制 body 最大長度爲 100kb
  • 當請求的 Content-Type 爲 application/x-www-form-urlencoded 時,會按照 form 格式對請求 body 進行解析,並限制 body 最大長度爲 100kb
  • 如果解析成功,body 一定會是一個 Object(可能是一個數組)。

那麼 egg.jsbodyParser 中間件是怎麼工作的。

查看 egg.js 源代碼,中間件是引入了 koa-bodyparser
在這裏插入圖片描述
進到 koa-bodyparser 包查看代碼

// koa-bodyparser/index.js
const parse = require('co-body');
const copy = require('copy-to');

module.exports = function(opts) {
  // ······
  return async function bodyParser(ctx, next) {
    if (ctx.request.body !== undefined || ctx.disableBodyParser)
      return await next();
    try {
      const res = await parseBody(ctx);
      ctx.request.body = 'parsed' in res ? res.parsed : {};
      if (ctx.request.rawBody === undefined) ctx.request.rawBody = res.raw;
    } catch (err) {
      if (onerror) {
        onerror(err, ctx);
      } else {
        throw err;
      }
    }
    await next();
  };

  async function parseBody(ctx) {
    if (
      enableJson &&
      ((detectJSON && detectJSON(ctx)) || ctx.request.is(jsonTypes))
    ) {
      return await parse.json(ctx, jsonOpts); // eslint-disable-line no-return-await
    }

    if (enableForm && ctx.request.is(formTypes)) {
      return await parse.form(ctx, formOpts); // eslint-disable-line no-return-await
    }

    if (enableText && ctx.request.is(textTypes)) {
      return (await parse.text(ctx, textOpts)) || '';
    }

    if (enableXml && ctx.request.is(xmlTypes)) {
      return (await parse.text(ctx, xmlOpts)) || '';
    }

    return {};
  }
};

來看關鍵代碼函數 bodyParser() ,在這裏面的這句 const res = await parseBody(ctx) 進行了數據處理,判斷是否有對應類型的數據。

而這裏面的 ctx.request.is() 方法,查閱相關資料,這個方法是 koarequest 對象內置方法

這是來自官方的解釋:

request.is(types...)

檢查請求所包含的 “Content-Type” 是否爲給定的 type 值。 如果沒有 request body,返回 null。 如果沒有 content type,或者匹配失敗,返回 false。 否則返回匹配的 content-type。

// With Content-Type: text/html; charset=utf-8
ctx.is('html'); // => 'html'
ctx.is('text/html'); // => 'text/html'
ctx.is('text/*', 'text/html'); // => 'text/html'

// When Content-Type is application/json
ctx.is('json', 'urlencoded'); // => 'json'
ctx.is('application/json'); // => 'application/json'
ctx.is('html', 'application/*'); // => 'application/json'

ctx.is('html'); // => false

我們可以去查看 koa 的源碼,看看這個函數具體是什麼
在這裏插入圖片描述

// koa/lib/request.js
const typeis = require('type-is');

module.exports = {
  // ······
  /**
   * Check if the incoming request contains the "Content-Type"
   * header field, and it contains any of the give mime `type`s.
   * If there is no request body, `null` is returned.
   * If there is no content type, `false` is returned.
   * Otherwise, it returns the first `type` that matches.
   *
   * Examples:
   *
   *     // With Content-Type: text/html; charset=utf-8
   *     this.is('html'); // => 'html'
   *     this.is('text/html'); // => 'text/html'
   *     this.is('text/*', 'application/json'); // => 'text/html'
   *
   *     // When Content-Type is application/json
   *     this.is('json', 'urlencoded'); // => 'json'
   *     this.is('application/json'); // => 'application/json'
   *     this.is('html', 'application/*'); // => 'application/json'
   *
   *     this.is('html'); // => false
   *
   * @param {String|String[]} [type]
   * @param {String[]} [types]
   * @return {String|false|null}
   * @api public
   */
  is(type, ...types) {
    return typeis(this.req, type, ...types);
  },
};

這個 is 方法調用的是 typeis 方法,而這個是通過 require 導入的包 type-is

這個包的作用是檢查 request 對象中的 Content-Type 是否有指定類型的值。

如果存在該值,就通過 co-body 進行解析。

解析正常並且獲得值,就把值存在 ctx.request.body 中。

const res = await parseBody(ctx);
ctx.request.body = 'parsed' in res ? res.parsed : {};

當然如果應用並不需要默認的 bodyParser 中間件來進行請求體的解析,此時可以通過配置 enablefalse 來關閉它

// config/config.default.js
module.exports = appInfo => {
  const config = exports = {};
  config.bodyParser = {
    enable: false,
  };
  return config;
};

以上就是 bodyParseregg.js 的流程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章