框架內置了 bodyParser
中間件對請求 body 解析成 object 掛載到 ctx.request.body 上。HTTP 協議中並不建議在通過 GET、HEAD 方法訪問時傳遞 body,所以我們無法在 GET、HEAD 方法中按照此方法獲取到內容。
框架對 bodyParser 設置了一些默認參數,配置好之後擁有以下特性:
- 當請求的 Content-Type 爲
application/json
,application/json-patch+json
,application/vnd.api+json
和application/csp-report
時,會按照 json 格式對請求 body 進行解析,並限制 body 最大長度爲100kb
。 - 當請求的 Content-Type 爲
application/x-www-form-urlencoded
時,會按照 form 格式對請求 body 進行解析,並限制 body 最大長度爲100kb
。 - 如果解析成功,body 一定會是一個 Object(可能是一個數組)。
那麼 egg.js
的 bodyParser
中間件是怎麼工作的。
查看 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()
方法,查閱相關資料,這個方法是 koa
的 request
對象內置方法
這是來自官方的解釋:
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
中間件來進行請求體的解析,此時可以通過配置 enable
爲 false
來關閉它
// config/config.default.js
module.exports = appInfo => {
const config = exports = {};
config.bodyParser = {
enable: false,
};
return config;
};
以上就是 bodyParser
在 egg.js
的流程。