express bodyparser

【 Node.js 】為什麼要使用 express bodyparser 呢?

相信很多人在 import express 的時候都知道要使用 bodyParser,否則會拿不到 request 的 body:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

app.use(bodyParser.json());

一直都知道要這樣用,但卻不太知道為什麼,再加上之前用 Node.js 寫 http server 的時候,在沒有用 bodyParser 的情況下去 console.log(req.body) 是 undefined,讓我又覺得更奇怪了,所以決定深入探討一下箇中原因!

1. init 一個迷你專案

1.1 安裝 express

$ yarn init
$ yarn add express

1.2 Build http server

安裝完 express 後來簡單架一個 http server:

// index.js
const express = require('express');

const app = express();

app.use((req, res) => {
  console.log(req.body);
  res.sendStatus(200);
});

app.listen(3000, () => {
  console.log('server is running in port 3000');
})

這是沒有 body parser 的情況,並且把 request 的 body console.log 出來,接著先把這個 server run 起來:

$ node index.js

有看到 server is running in port 3000 就代表成功了:

express bodyparser

1.3 Send http post request

再來要對這個 http server send request,來看看沒有 bodyParser 的情況下拿到的 request.body 是什麼:

express bodyparser

這邊我選擇用 Postman 做測試,你也可以選擇用 curl,記得用 Postman 的話要把 http method 選為 POST, body 的格式選擇 JSON。接著按下 Send 就會對我們的 http server send request,可以觀察 terminal 會 console 出什麼:

express bodyparser

印出的是 undefined。那加上 body parser 之後呢?

const express = require('express');

const app = express();

app.use(express.json());

app.use((req, res) => {
  console.log(req.body);
  res.sendStatus(200);
});

app.listen(3000, () => {
  console.log('server is running in port 3000');
})

這邊直接用 express.json() 取代 body parser,詳情可以參考:解決 body-parser 被標記為棄用(body-parser as deprecated),兩個寫法的效果一樣。改完 code 後重新跑 server,再 send 一次 request:

express bodyparser

結果竟然就拿得到 request 中的 body 了!

2. 深入探討 http request 中的 body

首先我們要先了解到網路傳輸最小的單位:封包,當我們用網路傳送資料的時候,會將資料拆分為封包,最後到目的地在組成原來的資料,http request 的 body 也是一樣的原理,根據 Node.js 官方 document 我們可以用 req.on 來監聽用 request 傳送進來的資料(記得將 body parser 拿掉):


const express = require('express');

const app = express();

app.use((req, res) => {
  req.on('data', chunk => {
    console.log(chunk)
  })
  req.on('end', () => {
    //end of data
  })
  res.sendStatus(200);
});

app.listen(3000, () => {
  console.log('server is running in port 3000');
})

2.1 body 的真實樣貌

接著再 send 一次 http request,觀察 termianl:

express bodyparser

結果竟然跑出一串沒看過的型別?!莫急莫慌莫害怕,Buffer 是 Node.js 用來處理二進位的物件,而 http request,其實會以 readable stream 的方式傳到 server,那什麼又是 stream 呢?可以把 stream 理解為資料流,資料不是一次一整包傳到目的地,而是拆分成數個 chunk 傳到目的地,會先放到 Buffer 中,也就成了我們 console 出的樣子。

想多了解 stream 是什麼的話,可以參考這篇:Understanding Streams in Node.js,個人覺得寫得滿完整的。

2.2 還原 body parser

那如果不用 body parser 或是 express 的 express.json() 該如何將這堆 chunk 轉換為我們的資料呢?Node.js 提供了幾種寫法:

方法一:

app.use((req, res) => {
  let data = '';
  req.on('data', chunk => {
    data += chunk;
  })
  req.on('end', () => {
    console.log(JSON.parse(data));
  })
  res.sendStatus(200);
});

方法二:

app.use((req, res) => {
  let data = [];
  req.on('data', chunk => {
    data.push(chunk);
  })
  req.on('end', () => {
    data = Buffer.concat(data).toString();
    console.log(data);
  })
  res.sendStatus(200);
});

方法三:

app.use(async (req, res) => {
  const buffers = [];
  for await (const chunk of req) {
    buffers.push(chunk);
  }
  const data = Buffer.concat(buffers).toString();
  console.log(data);
  res.sendStatus(200);
});

三種方法都可以把原本的 req.body 給 console 出來,而不管是 express.json() 或是 body parser 也都是在做這件事而已。

個人認為 stream 和 buffer 都算比較抽象的東西,很難解釋清楚,再加上個人目前也還沒有實作過,可能無法解釋的太清楚,希望哪天真的有機會接觸到再來好好整理成白話文~

3. 參考資料

解決 body-parser 被標記為棄用(body-parser as deprecated)
How bodyParser() works
Understanding Streams in Node.js
[試著把切版專案升級到 gulp4.0 吧] Day10 第一個插件:以複製檔案為例,談 node.js 的 stream 與 pipe()
What is chunk in Node.js ?
【我可以你也可以的Node.js】第十四篇 – Node’s file system – 使用串流的方式讀寫檔案
Stream | Node.js v16.7.0 Documentation

看完這篇文章是不是想換工作了呢(咦?那就千萬別錯過 2024 職涯博覽會!想換工作的,有機會在博覽會遇見更好的另一半,不想換工作的,有機會在博覽會遇見更好的工作!趕快點擊下面的 banner,拯救你的人生!!!https://s.yourator.co/jimmy

如果覺得我的文章有幫助的話,歡迎幫我的粉專按讚哦~謝謝你!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top