摘要:主要通過處理二進制數據流,但是它并不支持字符編碼方式,需要通過模塊進行處理。最后留圖一張往期精彩回顧玩轉原理解析玩轉核心原理分析
一、前置知識
??在理解koa-bodyparser原理之前,首先需要了解部分HTTP相關的知識。
1、報文主體??HTTP報文主要分為請求報文和響應報文,koa-bodyparser主要針對請求報文的處理。
??請求報文主要由以下三個部分組成:
報文頭部
空行
報文主體
??而koa-bodyparser中的body指的就是請求報文中的報文主體部分。
2、服務器端獲取報文主體流程??HTTP底層采用TCP提供可靠的字節流服務,簡單而言就是報文主體部分會被轉化為二進制數據在網絡中傳輸,所以服務器端首先需要拿到二進制流數據。
??談到網絡傳輸,當然會涉及到傳輸速度的優化,而其中一種優化方式就是對內容進行壓縮編碼,常用的壓縮編碼方式有:
gzip
compress
deflate
identity(不執行壓縮或不會變化的默認編碼格式)
??服務器端會根據報文頭部信息中的Content-Encoding確認采用何種解壓編碼。
??接下來就需要將二進制數據轉換為相應的字符,而字符也有不同的字符編碼方式,例如對于中文字符處理差異巨大的UTF-8和GBK,UTF-8編碼漢字通常需要三個字節,而GBK只需要兩個字節。所以還需要在請求報文的頭部信息中設置Content-Type使用的字符編碼信息(默認情況下采用的是UTF-8),這樣服務器端就可以利用相應的字符規則進行解碼,得到正確的字符串。
??拿到字符串之后,服務器端又要問了:客戶端,你這一段字符串是啥意思啊?
??根據不同的應用場景,客戶端會對字符串采用不同的編碼方式,常見的編碼方式有:
URL編碼方式: a=1&b=2
JSON編碼方式: {a:1,b:2}
??客戶端會將采用的字符串編碼方式設置在請求報文頭部信息的Content-Type屬性中,這樣服務器端根據相應的字符串編碼規則進行解碼,就能夠明白客戶端所傳遞的信息了。
??下面一步步分析koa-bodyparser是如何處理這一系列操作,從而得到報文主體內容。
二、獲取二進制數據流??NodeJS中獲取請求報文主體二進制數據流主要通過監聽request對象的data事件完成:
// 示例一 const http = require("http") http.createServer((req, res) => { const body = [] req.on("data", chunk => { body.push(chunk) }) req.on("end", () => { const chunks = Buffer.concat(body) // 接收到的二進制數據流 // 利用res.end進行響應處理 res.end(chunks.toString()) }) }).listen(1234)
??而koa-bodyparser主要是對co-body的封裝,而【co-body】中主要是采用raw-body模塊獲取請求報文主體的二進制數據流,【row-body】主要是對上述示例代碼的封裝和健壯性處理。
三、內容解碼??客戶端會將內容編碼的方式放入請求報文頭部信息Content-Encoding屬性中,服務器端接收報文主體的二進制數據了時,會根據該頭部信息進行解壓操作,當然服務器端可以在響應報文頭部信息Accept-Encoding屬性中添加支持的解壓方式。
??而【row-body】主要采用inflation模塊進行解壓處理。
四、字符解碼??一般而言,UTF-8是互聯網中主流的字符編碼方式,前面也提到了還有GBK編碼方式,相比較UTF-8,它編碼中文只需要2個字節,那么在字符解碼時誤用UTF-8解碼GBK編碼的字符,就會出現中文亂碼的問題。
??NodeJS主要通過Buffer處理二進制數據流,但是它并不支持GBK字符編碼方式,需要通過iconv-lite模塊進行處理。
??【示例一】中的代碼就存在沒有正確處理字符編碼的問題,那么報文主體中的字符采用GBK編碼方式,必然會出現中文亂碼:
const request = require("request") const iconv = require("iconv-lite") request.post({ url: "http://localhost:1234/", body: iconv.encode("中文", "gbk"), headers: { "Content-Type": "text/plain;charset=GBK" } }, (error, response, body) => { console.log(body) // 發生中文亂碼情況 })
??NodeJS中的Buffer默認是采用UTF-8字符編碼處理,這里借助【iconv-lite】模塊處理不同的字符編碼方式:
const chunks = Buffer.concat(body) res.end(iconv.decode(chunks, charset)) // charset通過Content-Type得到五、字符串解碼
??前面已經提到了字符串的二種編碼方式,它們對應的Content-Type分別為:
URL編碼 application/x-www-form-urlencoded
JSON編碼 application/json
??對于前端來說,URL編碼并不陌生,經常會用于URL拼接操作,唯一需要注意的是不要忘記對鍵值對進行decodeURIComponent()處理。
??當客戶端發送請求主體時,需要進行編碼操作:
"a=1&b=2&c=3"
??服務器端再根據URL編碼規則解碼,得到相應的對象。
// URL編碼方式 簡單的解碼方法實現 function decode (qs, sep = "&", eq = "=") { const obj = {} qs = qs.split(sep) for (let i = 0, max = qs.length; i < max; i++) { const item = qs[i] const index = item.indexOf(eq) let key, value if (~index) { key = item.substr(0, index) value = item.substr(index + 1) } else { key = item value = "" } key = decodeURIComponent(key) value = decodeURIComponent(value) if (!obj.hasOwnProperty(key)) { obj[key] = value } } return obj } console.log(decode("a=1&b=2&c=3")) // { a: "1", b: "2", c: "3" }
??URL編碼方式適合處理簡單的鍵值對數據,并且很多框架的Ajax中的Content-Type默認值都是它,但是對于復雜的嵌套對象就不太好處理了,這時就需要JSON編碼方式大顯身手了。
??客戶端發送請求主體時,只需要采用JSON.stringify進行編碼。服務器端只需要采用JSON.parse進行解碼即可:
const strictJSONReg = /^[x20x09x0ax0d]*([|{)/; function parse(str) { if (!strict) return str ? JSON.parse(str) : str; // 嚴格模式下,總是返回一個對象 if (!str) return {}; // 是否為合法的JSON字符串 if (!strictJSONReg.test(str)) { throw new Error("invalid JSON, only supports object and array"); } return JSON.parse(str); }
??除了上述兩種字符串編碼方式,koa-bodyparser還支持不采用任何字符串編碼方式的普通字符串。
??三種字符串編碼的處理方式由【co-body】模塊提供,koa-bodyparser中通過判斷當前Content-Type類型,調用不同的處理方式,將獲取到的結果掛載在ctx.request.body:
return async function bodyParser(ctx, next) { if (ctx.request.body !== undefined) return await next(); if (ctx.disableBodyParser) return await next(); try { // 最重要的一步 將解析的內容掛載到koa的上下文中 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); // application/json等json type } if (enableForm && ctx.request.is(formTypes)) { return await parse.form(ctx, formOpts); // application/x-www-form-urlencoded } if (enableText && ctx.request.is(textTypes)) { return await parse.text(ctx, textOpts) || ""; // text/plain } return {}; } };
??其實還有一種比較常見的Content-type,當采用表單上傳時,報文主體中會包含多個實體主體:
------WebKitFormBoundaryqsAGMB6Us6F7s3SF Content-Disposition: form-data; name="image"; filename="image.png" Content-Type: image/png ------WebKitFormBoundaryqsAGMB6Us6F7s3SF Content-Disposition: form-data; name="text" ------WebKitFormBoundaryqsAGMB6Us6F7s3SF--
??這種方式處理相對比較復雜,koa-bodyparser中并沒有提供該Content-Type的解析。(下一篇中應該會介紹^_^)
五、總結??以上便是koa-bodyparser的核心實現原理,其中涉及到很多關于HTTP的基礎知識,對于HTTP不太熟悉的同學,可以推薦看一波入門級寶典【圖解HTTP】。
??最后留圖一張:
玩轉Koa -- koa-router原理解析
玩轉Koa -- 核心原理分析
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101073.html
POST/GET請求——常見請求方式處理 ?? iKcamp 制作團隊 原創作者:大哼、阿干、三三、小虎、胖子、小哈、DDU、可木、晃晃 文案校對:李益、大力萌、Au、DDU、小溪里、小哈 風采主播:可木、阿干、Au、DDU、小哈 視頻剪輯:小溪里 主站運營:給力xi、xty 教程主編:張利濤 視頻地址:https://www.cctalk.com/v/15114357765870 ...
摘要:前言上一篇我們遇到少年,是不是忘了的警告,這一篇我們就來解決這個問題。總結通過實現前后端分離,并且使用來更方便的模擬數據。下一篇,我們創建發布環境下的配置文件,并且看看怎么優化產出的代碼的從零開始做前端架構項目完整代碼前端架構子咻 前言 上一篇我們遇到少年,是不是忘了npm run mock?的警告,這一篇我們就來解決這個問題。 開發 一、安裝包 安裝koa和一系列的包(我們用的是ko...
摘要:用搭建前端項目用搭建后臺,給前端提供數據訪問接口項目結構用搭建的項目,紅色框中是新建的文件夾用于存放剩下的文件在寫項目中慢慢增加,最初就是這樣的之后將項目跑起來,看一下有沒有問題這里就當作沒有問題前端這里選用和搭配這里采用的是的完整 koa2+vue 用vue-cli搭建前端項目 用koa2搭建后臺,給前端提供數據訪問接口 項目結構 showImg(https://segmentf...
摘要:四路由注冊構造函數首先看了解一下構造函數限制必須采用關鍵字服務器支持的請求方法,后續方法會用到保存前置處理函數存儲在構造函數中初始化的和屬性最為重要,前者用來保存前置處理函數,后者用來保存實例化的對象。 一、前言 ??Koa為了保持自身的簡潔,并沒有捆綁中間件。但是在實際的開發中,我們需要和形形色色的中間件打交道,本文將要分析的是經常用到的路由中間件 -- koa-router。 ??...
閱讀 1207·2021-09-03 10:44
閱讀 603·2019-08-30 13:13
閱讀 2796·2019-08-30 13:11
閱讀 1967·2019-08-30 12:59
閱讀 1034·2019-08-29 15:32
閱讀 1595·2019-08-29 15:25
閱讀 987·2019-08-29 12:24
閱讀 1277·2019-08-27 10:58