Nodejs進階:Express常用中介軟體body-parser實現解析
原文連結
body-parser
是非常常用的一個express
中介軟體,作用是對post請求的請求體進行解析。使用非常簡單,以下兩行程式碼已經覆蓋了大部分的使用場景。
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
本文從簡單的例子出發,探究body-parser
的內部實現。至於body-parser
如何使用,感興趣的同學可以參考官方文件。
入門基礎
在正式講解前,我們先來看一個POST請求的報文,如下所示。
POST /test HTTP/1.1
Host: 127.0.0.1:3000
Content-Type : text/plain; charset=utf8
Content-Encoding: gzip
chyingp
其中需要我們注意的有Content-Type
、Content-Encoding
以及報文主體:
- Content-Type:請求報文主體的型別、編碼。常見的型別有
text/plain
、application/json
、application/x-www-form-urlencoded
。常見的編碼有utf8
、gbk
等。 - Content-Encoding:宣告報文主體的壓縮格式,常見的取值有
gzip
、deflate
、identity
。 - 報文主體:這裡是個普通的文字字串
chyingp
。
body-parser主要做了什麼
body-parser
實現的要點如下:
- 處理不同型別的請求體:比如
text
、json
、urlencoded
等,對應的報文主體的格式不同。 - 處理不同的編碼:比如
utf8
、gbk
等。 - 處理不同的壓縮型別:比如
gzip
、deflare
等。 - 其他邊界、異常的處理。
一、處理不同型別請求體
為了方便讀者測試,以下例子均包含服務端、客戶端程式碼,完整程式碼可在筆者github上找到。
解析text/plain
客戶端請求的程式碼如下,採用預設編碼,不對請求體進行壓縮。請求體型別為text/plain
。
var http = require('http');
var options = {
hostname: '127.0.0.1' ,
port: '3000',
path: '/test',
method: 'POST',
headers: {
'Content-Type': 'text/plain',
'Content-Encoding': 'identity'
}
};
var client = http.request(options, (res) => {
res.pipe(process.stdout);
});
client.end('chyingp');
服務端程式碼如下。text/plain
型別處理比較簡單,就是buffer的拼接。
var http = require('http');
var parsePostBody = function (req, done) {
var arr = [];
var chunks;
req.on('data', buff => {
arr.push(buff);
});
req.on('end', () => {
chunks = Buffer.concat(arr);
done(chunks);
});
};
var server = http.createServer(function (req, res) {
parsePostBody(req, (chunks) => {
var body = chunks.toString();
res.end(`Your nick is ${body}`)
});
});
server.listen(3000);
解析application/json
客戶端程式碼如下,把Content-Type
換成application/json
。
var http = require('http');
var querystring = require('querystring');
var options = {
hostname: '127.0.0.1',
port: '3000',
path: '/test',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Encoding': 'identity'
}
};
var jsonBody = {
nick: 'chyingp'
};
var client = http.request(options, (res) => {
res.pipe(process.stdout);
});
client.end( JSON.stringify(jsonBody) );
服務端程式碼如下,相比text/plain
,只是多了個JSON.parse()
的過程。
var http = require('http');
var parsePostBody = function (req, done) {
var length = req.headers['content-length'] - 0;
var arr = [];
var chunks;
req.on('data', buff => {
arr.push(buff);
});
req.on('end', () => {
chunks = Buffer.concat(arr);
done(chunks);
});
};
var server = http.createServer(function (req, res) {
parsePostBody(req, (chunks) => {
var json = JSON.parse( chunks.toString() ); // 關鍵程式碼
res.end(`Your nick is ${json.nick}`)
});
});
server.listen(3000);
解析application/x-www-form-urlencoded
客戶端程式碼如下,這裡通過querystring
對請求體進行格式化,得到類似nick=chyingp
的字串。
var http = require('http');
var querystring = require('querystring');
var options = {
hostname: '127.0.0.1',
port: '3000',
path: '/test',
method: 'POST',
headers: {
'Content-Type': 'form/x-www-form-urlencoded',
'Content-Encoding': 'identity'
}
};
var postBody = { nick: 'chyingp' };
var client = http.request(options, (res) => {
res.pipe(process.stdout);
});
client.end( querystring.stringify(postBody) );
服務端程式碼如下,同樣跟text/plain
的解析差不多,就多了個querystring.parse()
的呼叫。
var http = require('http');
var querystring = require('querystring');
var parsePostBody = function (req, done) {
var length = req.headers['content-length'] - 0;
var arr = [];
var chunks;
req.on('data', buff => {
arr.push(buff);
});
req.on('end', () => {
chunks = Buffer.concat(arr);
done(chunks);
});
};
var server = http.createServer(function (req, res) {
parsePostBody(req, (chunks) => {
var body = querystring.parse( chunks.toString() ); // 關鍵程式碼
res.end(`Your nick is ${body.nick}`)
});
});
server.listen(3000);
二、處理不同編碼
很多時候,來自客戶端的請求,採用的不一定是預設的utf8
編碼,這個時候,就需要對請求體進行解碼處理。
客戶端請求如下,有兩個要點。
- 編碼宣告:在
Content-Type
最後加上;charset=gbk
- 請求體編碼:這裡藉助了
iconv-lite
,對請求體進行編碼iconv.encode('程式猿小卡', encoding)
var http = require('http');
var iconv = require('iconv-lite');
var encoding = 'gbk'; // 請求編碼
var options = {
hostname: '127.0.0.1',
port: '3000',
path: '/test',
method: 'POST',
headers: {
'Content-Type': 'text/plain; charset=' + encoding,
'Content-Encoding': 'identity',
}
};
// 備註:nodejs本身不支援gbk編碼,所以請求傳送前,需要先進行編碼
var buff = iconv.encode('程式猿小卡', encoding);
var client = http.request(options, (res) => {
res.pipe(process.stdout);
});
client.end(buff, encoding);
服務端程式碼如下,這裡多了兩個步驟:編碼判斷、解碼操作。首先通過Content-Type
獲取編碼型別gbk
,然後通過iconv-lite
進行反向解碼操作。
var http = require('http');
var contentType = require('content-type');
var iconv = require('iconv-lite');
var parsePostBody = function (req, done) {
var obj = contentType.parse(req.headers['content-type']);
var charset = obj.parameters.charset; // 編碼判斷:這裡獲取到的值是 'gbk'
var arr = [];
var chunks;
req.on('data', buff => {
arr.push(buff);
});
req.on('end', () => {
chunks = Buffer.concat(arr);
var body = iconv.decode(chunks, charset); // 解碼操作
done(body);
});
};
var server = http.createServer(function (req, res) {
parsePostBody(req, (body) => {
res.end(`Your nick is ${body}`)
});
});
server.listen(3000);
三、處理不同壓縮型別
這裡舉個gzip
壓縮的例子。客戶端程式碼如下,要點如下:
- 壓縮型別宣告:
Content-Encoding
賦值為gzip
。 - 請求體壓縮:通過
zlib
模組對請求體進行gzip壓縮。
var http = require('http');
var zlib = require('zlib');
var options = {
hostname: '127.0.0.1',
port: '3000',
path: '/test',
method: 'POST',
headers: {
'Content-Type': 'text/plain',
'Content-Encoding': 'gzip'
}
};
var client = http.request(options, (res) => {
res.pipe(process.stdout);
});
// 注意:將 Content-Encoding 設定為 gzip 的同時,傳送給服務端的資料也應該先進行gzip
var buff = zlib.gzipSync('chyingp');
client.end(buff);
服務端程式碼如下,這裡通過zlib
模組,對請求體進行了解壓縮操作(guzip)。
var http = require('http');
var zlib = require('zlib');
var parsePostBody = function (req, done) {
var length = req.headers['content-length'] - 0;
var contentEncoding = req.headers['content-encoding'];
var stream = req;
// 關鍵程式碼如下
if(contentEncoding === 'gzip') {
stream = zlib.createGunzip();
req.pipe(stream);
}
var arr = [];
var chunks;
stream.on('data', buff => {
arr.push(buff);
});
stream.on('end', () => {
chunks = Buffer.concat(arr);
done(chunks);
});
stream.on('error', error => console.error(error.message));
};
var server = http.createServer(function (req, res) {
parsePostBody(req, (chunks) => {
var body = chunks.toString();
res.end(`Your nick is ${body}`)
});
});
server.listen(3000);
寫在後面
body-parser
的核心實現並不複雜,翻看原始碼後你會發現,更多的程式碼是在處理異常跟邊界。
另外,對於POST請求,還有一個非常常見的Content-Type
是multipart/form-data
,這個的處理相對複雜些,body-parser
不打算對其進行支援。篇幅有限,後續章節再繼續展開。
歡迎交流,如有錯漏請指出。
相關推薦
Nodejs進階:Express常用中介軟體body-parser實現解析
原文連結 body-parser是非常常用的一個express中介軟體,作用是對post請求的請求體進行解析。使用非常簡單,以下兩行程式碼已經覆蓋了大部分的使用場景。 app.use(bodyParser.json()); app.use(bodyParser.urle
[轉] Nodejs 進階:Express 常用中間件 body-parser 實現解析
tree define pan iconv 不同 erro unzip body message 寫在前面 body-parser是非常常用的一個express中間件,作用是對post請求的請求體進行解析。使用非常簡單,以下兩行代碼已經覆蓋了大部分的使用場景。 app.
Nodejs進階:基於express+multer的文件上傳
ora all server and end 文件類型 類型 array body 安裝組件 npm install express multer --save 服務端代碼server.js var Express = re
nodejs進階:密碼加鹽:隨機鹽值
nod sharp class oms word blog 輸出 arp har demo var crypto = require(‘crypto‘); function getRandomSalt(){ return Math.random().toStri
NodeJS學習筆記 進階 (11)Nodejs 進階:調試日誌打印:debug模塊
-c clas a* deb urn uid 0.11 log 打印 前言 在node程序開發中時,經常需要打印調試日誌。用的比較多的是debug模塊,比如express框架中就用到了。下文簡單舉幾個例子進行說明。文中相關代碼示例,可在這裏找到。 備註:node在0.11
Node 進階:express 預設日誌元件 morgan 使用筆記
1.安裝 npm install express morgan 2.使用案例(預設) var express = require('express'); var app = express(); var morgan = require('morgan'); app.use(morga
express常用中介軟體
整理一下工作中經常使用到的Express中介軟體 config-lite: 讀取配置檔案 express-session: session 中介軟體 connect-mongo: 將 session 持久化儲存於 mongodb,結合 express-sess
程式設計師進階:怎麼成為一個軟體架構師?
作者:程式設計小丫 來源:CSDN部落格 序:的確沒想到隨手寫的東西有那麼多的回覆,不管怎樣還是挺高興的。在這裡謝謝大家的關注了。其實做了這麼多年的技術腦子裡總會跳出很多的想法,但很少有時間靜下來仔細地思考思考,寫寫部落格也算是一種自我歸納和總結吧。 “軟體架構師”這個名詞也不知是什麼時候
SpringBoot2基礎,進階,資料庫,中介軟體等系列文章目錄分類
一、文章分類 1、入門基礎 SpringBoot2:環境搭建和RestFul風格介面 2、日誌管理 SpringBoot2:配置L
我的Android進階之旅------>Android自定義View來實現解析lrc歌詞並同步滾動、上下拖動、縮放歌詞的功能
前言 最近有個專案有關於播放音樂時候,關於歌詞有以下幾個功能: 1、實現歌詞同步滾動的功能,即歌曲播放到哪句歌詞,就高亮地顯示出正在播放的這個歌詞; 2、實現上下拖動歌詞時候,可以拖動播放器的進度。即可以不停地上下拖動歌詞,
nodejs常用框架express的中介軟體 及app.use 和 app.get 方法
用node開發專案,express是常用的框架,下面介紹下核心用法中介軟體和 app的use、get方法: 中介軟體的實現很簡單: // 一個簡單的中介軟體 function middleware(req, res, next){ // req 引數可以接受一些請求的引數(req.q
NodeJS學習筆記 進階 (8)express+session實現簡易身份認證(ok)
fin dir 認證 ole opts ats ssi oda 添加 章節概覽 morgan是express默認的日誌中間件,也可以脫離express,作為node.js的日誌組件單獨使用。本文由淺入深,內容主要包括: morgan使用入門例子 如何將日誌保存到本地文件
原 Android進階:步驟三:Android常用框架:Picasso圖片框架
轉載: 作者:依然範特稀西 連結:https://www.jianshu.com/p/c68a3b9ca07a Android 中有幾個比較有名的圖片載入框架,Universal ImageLoader、Picasso、Glide和Fresco。它們各有優點,以前一直用的是ImageLoader
原 Android進階:步驟三:Android常用框架:OkHttp網路操作框架
Okio & OkHttp 課程目標 掌握I/O操作的方法 掌握傳輸資料的方法 學習內容 Okio簡介 Okio的核心類 OkHttp簡介 OkHttp核心類 程式碼實踐 一、Okio簡介 什
【學習筆記】String進階:StringBuffer類(線程安全)和StringBuilder類
n) static this util double 字符串 對象 ice 單線程 一、除了使用String類存儲字符串之外,還可以使用StringBuffer類存儲字符串。而且它是比String類更高效的存儲字符串的一種引用數據類型。 優點: 對字符串進行連接操作時,
python開發函數進階:命名空間,作用域,函數的本質,閉包,內置方法(globales)
問題 總結 加載 自己的 ger 作用域 範圍 沒有 概念 一,命名空間 #局部命名空間#全局命名空間#內置命名空間 #三者的順序#加載順序 硬盤上——內存裏#內置-->全局(從上到下順序加載進來的)-->局部(調用的時候加載) 1 #!/usr/bin/
python開發函數進階:裝飾器
for 中國 eas login please 函數 功能 log 原則 一,裝飾器本質 閉包函數 功能:就是在不改變原函數調用方式的情況下,在這個函數前後加上擴展功能 二,設計模式 開放封閉原則 *對擴展是開放的 *對修改是封閉的 三,代碼解釋 1 #!/
[您有新的未分配科技點]博弈論進階:似乎不那麽恐懼了…… (SJ定理,簡單的基礎模型)
裏的 如果 cnblogs 經典 ant 控制 nim osi 取石子 這次,我們來繼續學習博弈論的知識。今天我們會學習更多的基礎模型,以及SJ定理的應用。 首先,我們來看博弈論在DAG上的應用。首先來看一個小例子:在一個有向無環圖中,有一個棋子從某一個點開始一直向它的出點
python開發函數進階:遞歸函數
bre for 自己 lis 一次 技術 結束 函數 ont 一,什麽叫遞歸 #遞歸#在一個函數裏調用自己#python遞歸最大層數限制 997#最大層數限制是python默認的,可以做修改#但是我們不建議你修改 例子和尚講故事 1 #!/usr/bin/env pyt
python開發函數進階:可叠代的&叠代器&生成器
== ict turn lena log 中新 odin 使用 def 一,可叠代的&可叠代對象 1.一個一個的取值就是可叠代的 iterable#str list tuple set dict#可叠代的 ——對應的標誌 __iter__ 2.判斷一個變量