數據處理流程html
下面展現下相關的代碼前端
// 根據Content-Encoding判斷是否解壓,如需則調用相應解壓函數 async function transformEncode(buffer, encode) { // ... } // charset轉碼 function transformCharset(buffer, charset) { // ... } // 根據content-type作最後的數據格式化 function formatData(str, contentType) { // ... } // 返回Promise function getRequestBody(req, res) { return new Promise(async (resolve, reject) => { const chunks = []; req.on('data', buf => { chunks.push(buf); }) req.on('end', async () => { let buffer = Buffer.concat(chunks); // 獲取content-encoding const encode = req.headers['content-encoding']; // 獲取content-type const { type, parameters } = contentType.parse(req); // 獲取charset const charset = parameters.charset; // 解壓縮 buffer = await transformEncode(buffer, encode); // 轉換字符編碼 const str = transformCharset(buffer, charset); // 根據類型輸出不一樣格式的數據,如字符串或JSON對象 const result = formatData(str, type); resolve(result); }) }).catch(err => { throw err; }) }
function getRequestBody(req, res) { return new Promise(async (resolve, reject) => { // ... } }
const chunks = []; req.on('data', buf => { chunks.push(buf); })
const contentType = require('content-type'); const iconv = require('iconv-lite'); req.on('end', async () => { let buffer = Buffer.concat(chunks); // 獲取content-encoding const encode = req.headers['content-encoding']; // 獲取content-type const { type, parameters } = contentType.parse(req); // 獲取charset const charset = parameters.charset; // 解壓縮 buffer = await transformEncode(buffer, encode); // 轉換字符編碼 const str = transformCharset(buffer, charset); // 根據類型輸出不一樣格式的數據,如字符串或JSON對象 const result = formatData(str, type); resolve(result); }
Content-Encoding可分爲四種值:gzip,compress,deflate,br,identitynode
其中git
因此咱們須要處理解壓的一共有三種數據類型github
(注意!zlib.brotliDecompress方法在Node11.7以上版本纔會支持,並且不要看到名字裏有compress就誤覺得它是用來解壓compress壓縮的數據的,實際上它是用來處理br的)npm
代碼以下,咱們對zlib.gunzip等回調類方法經過promisify轉成Promise編碼風格編程
const promisify = util.promisify; // node 11.7版本以上才支持此方法 const brotliDecompress = zlib.brotliDecompress && promisify(zlib.brotliDecompress); const gunzip = promisify(zlib.gunzip); const inflate = promisify(zlib.inflate); const querystring = require('querystring'); // 根據Content-Encoding判斷是否解壓,如需則調用相應解壓函數 async function transformEncode(buffer, encode) { let resultBuf = null; debugger; switch (encode) { case 'br': if (!brotliDecompress) { throw new Error('Node版本太低! 11.6版本以上才支持brotliDecompress方法') } resultBuf = await brotliDecompress(buffer); break; case 'gzip': resultBuf = await gunzip(buffer); break; case 'deflate': resultBuf = await inflate(buffer); break; default: resultBuf = buffer; break; } return resultBuf; }
咱們採用iconv-lite對charset進行轉碼,代碼以下json
const iconv = require('iconv-lite'); // charset轉碼 function transformCharset(buffer, charset) { charset = charset || 'UTF-8'; // iconv將Buffer轉化爲對應charset編碼的String const result = iconv.decode(buffer, charset); return result; }
來!傳送門數組
https://link.zhihu.com/?target=https%3A//www.npmjs.com/package/iconv-litepromise
具體的處理方式分三種狀況:
代碼以下
const querystring = require('querystring'); // 根據content-type作最後的數據格式化 function formatData(str, contentType) { let result = ''; switch (contentType) { case 'text/plain': result = str; break; case 'application/json': result = JSON.parse(str); break; case 'application/x-www-form-urlencoded': result = querystring.parse(str); break; default: break; } return result; }
服務端
下面的代碼你確定知道要放在哪裏了
// 省略其餘代碼 if (pathname === '/post') { // 調用getRequestBody,經過await修飾等待結果返回 const body = await getRequestBody(req, res); console.log(body); return; }
前端採用fetch進行測試
在下面的代碼中,咱們連續三次發出不一樣的POST請求,攜帶不一樣類型的body數據,看看服務端會輸出什麼
var iconv = require('iconv-lite'); var querystring = require('querystring'); var gbkBody = { data: "我是彭湖灣", contentType: 'application/json', charset: 'gbk' }; // 轉化爲JSON數據 var gbkJson = JSON.stringify(gbkBody); // 轉爲gbk編碼 var gbkData = iconv.encode(gbkJson, "gbk"); var isoData = iconv.encode("我是彭湖灣,這句話採用UTF-8格式編碼,content-type爲text/plain", "UTF-8") // 測試內容類型爲application/json和charset=gbk的狀況 fetch('/post', { method: 'POST', headers: { "Content-Type": 'application/json; charset=gbk' }, body: gbkData }); // 測試內容類型爲application/x-www-form-urlencoded和charset=UTF-8的狀況 fetch('/post', { method: 'POST', headers: { "Content-Type": 'application/x-www-form-urlencoded; charset=UTF-8' }, body: querystring.stringify({ data: "我是彭湖灣", contentType: 'application/x-www-form-urlencoded', charset: 'UTF-8' }) }); // 測試內容類型爲text/plain的狀況 fetch('/post', { method: 'POST', headers: { "Content-Type": 'text/plain; charset=UTF-8' }, body: isoData });
服務端輸出結果
{ data: '我是彭湖灣', contentType: 'application/json', charset: 'gbk' } { data: '我是彭湖灣', contentType: 'application/x-www-form-urlencoded', charset: 'UTF-8' } 我是彭湖灣,這句話採用UTF-8格式編碼,content-type爲text/plain
其實本質上來講,charset前端通常都是固定爲utf-8的, 甚至在JQuery的AJAX請求中,前端請求charset甚至是不可更改,只能是charset,可是在使用fetch等API的時候,的確是能夠更改charset的,這個工做嘗試知足一些比較偏僻的更改charset需求。
通常狀況下咱們認爲,考慮到前端發的AJAX之類的請求的數據量,是不須要作Gzip壓縮的。可是向服務器發起請求的不必定只有前端,還多是Node的客戶端。這些Node客戶端可能會向Node服務端傳送壓縮事後的數據流。 例以下面的代碼所示
const zlib = require('zlib'); const request = require('request'); const data = zlib.gzipSync(Buffer.from("我是一個被Gzip壓縮後的數據")); request({ method: 'POST', url: 'http://127.0.0.1:3000/post', headers: {//設置請求頭 "Content-Type": "text/plain", "Content-Encoding": "gzip" }, body: data })
https://github.com/penghuwan/body-parser-promise
https://www.npmjs.com/package/body-parser-promise
Koa-bodyparser https://github.com/koajs/bodyparser
【完】