前言
這是一份未整理的 Node.js 學習筆記
正文
安裝
- 在 CentOS 7 上安裝 Node.js 和 NPM
NodeSource 是一家致力於提供企業級Node 支持的公司,他們為Linux 發行版維護一致更新的Node.js 軟件倉庫。
要從CentOS 7 系統上的NodeSource 軟件倉庫安裝Node.js 和npm ,請按照下列步驟操作:
添加NodeSource yum 軟件倉庫
Node.js 的當前LTS 版本是10.x 版。如果你想安裝的版本8 只吧下面的命令中setup_10.x 更改為setup_8.x 。
運行以下curl命令將NodeSource yum軟件倉庫添加到您的系統:curl -sL https://rpm.nodesource.com/setup_10.x | bash -
安裝Node.js 和npm
啟用NodeSource 軟件倉庫後,通過以下命令安裝Node.js 和npm :yum install nodejs
驗證Node.js 和npm 安裝
node -v
npm -v
- 如何使用NVM 安裝Node.js 和npm
NVM (Node 版本管理器)是一個bash 腳本,用於管理多個活動的Node.js 版本。NVM 允許我們安裝和卸載任何特定的Node.js 版本,這意味著我們可以擁有任何數量的Node.js 版本供我們使用或測試。curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash && export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
要在CentOS 系統上使用NVM 安裝Node.js 和npm ,請按照下列步驟操作:
使用 fs module
- fs.writeFileSync
const fs = require('fs);
在 Node.js 裏頭,如果要引用一個 module ,要用一個變數引用,然後之後就可以使用它
例如上面的 function ,是將fs.writeFileSync('hello.txt', 'Hello fromNode.js');
Hello fromNode.js
寫進hello.txt
這個檔案
### 建立一個最簡單的 server
首先,我們先引用 `http` module,當我們要調用本地 module 時,我們可以指定路徑,像是 `./http` ,但我們要調用 global 的 module 時,我們不加任何路徑, 如下
```javascript
const http = require('http');
接下來,我們利用剛剛引用的 http
module 來建立一個 server ,如下:
const server = http.createServer((req, res) => { |
or
const server = http.createServer(function(req, res){ |
or
function rqListener(req, res) { |
最後,我們雖然已經建立了 server ,但是我們還沒有指定它的位址。 我們指定 3000 port 給這個 server ,如下:
server.listen(3000); |
此時,我們可以從瀏覽器,輸入 localhost:3000
來拜訪這個 server
停止這個 loop
const http = require('http'); |
從 request 中取得我們想要的資訊
舉例來說,我們要取得 url
, method
, 以及 header
三項資訊,如下:
const http = require('http'); |
下圖,我們可以看到我們特別指定的三項資訊:
設定 response
我們可以在 server 中,指定 response ,如下:
const http = require('http'); |
然後打開開發者工具,我們可以看到我們剛剛設定的 header
然後 response 的地方可以看到我們剛剛設定的 response
簡易的 request routing
我們可以指定觸發特定 response 的 url ,當 client 呼叫這個 url 時,就會觸發我們指定的 response ,反之,則觸發另外的 response
const http = require('http'); |
由上面的 code 可以看到,我們指定 req.url
必須要絕對等於 /
才會觸發條件內,我們指定的 response 如下:
當我們按下 send ,會執行 post
method, action /message
,如下:
因為 action 的關係,會嘗試拜訪 message
url ,而因為這個 url 並不符合我們設定的條件,所以會執行預設 response
簡單的 redirect request
現在,我們要簡易的 redirect 我們的 request ,如下:
const http = require('http'); |
從上面的 code 可以看到,我們新增了第二個 if statement。
如果 url 等於 //message
以及 method 等於 post
,雙重條件都符合之下,就會觸發我們設定的條件
我們使用了之前我們曾經使用的 fs
module ,如果條件觸發,我們就會將 DUMMY
寫入一個叫做 message.txt
的檔案
接著回傳 status code 302
最後回導到 /
在 res.end()
之後,我們不可以在 define 新的 res ,否則就會出現錯誤,因為這邊我們要使用 return ,後續的代碼就不會再執行
Parsing request bodies
本章節,我們將解析 request 裡頭的 body 資料
並且初次接觸到了 stream 以及 buffer 的概念。
首先,我們先設定一個事件。 當接收到 data
時,觸發一個 function 並且帶入 chunk , chunk 是資料的最小單位。
接著我們使用了 console.log
來把 chunk 印出來!
同時,我們建立一個 body 常數 array ,並且將每一次觸發 data 事件時,我們都將 chunk 丟到這個 array 裏頭, 代碼如下:
const body = []; |
接著,我們在建立一個事件,當 request 接收完成,我們在定義一個常數,叫做 parsedBody , 至於這個常數的內容,我們使用 buffer 物件,來將 body array 裡頭的 chunk 都串起來,然後轉換成 string 。
最後,我們使用 console.log
把常數 parsedBody 印出來,代碼如下:
req.on('end', () => { |
結果如下:
接下來,我們在定義一個常數 message ,它的內容是用 ‘=’ 來將常數 parsedBody 分隔,變成一個 array ,然後我們取 [1] ,就是 array 中的第二項資料。
然後,我們將這個常數 message 一方面利用 console.log
印出來,一方面利用 fs
module 來寫到一個叫做 message.txt
的檔案中。
代碼如下:
req.on('end', () => { |
至此, 此 episode 告一段落,最後全部的 code 如下:
const http = require('http'); |
了解事件驅動代碼的執行
本章節介紹了事件驅動代碼的執行規則以及順序
舉例來說,如果我們對目前的代碼做了一些調整,如下:
req.on('end', () => { |
首先,在一開始我們就引用了 http
module 以及 fs
module ,然後我們利用 http
module 來建立一個 server ,並且讓這個 server 聽 3000 port 。
當有任何 requets 呼叫這個 server 時,都會觸發這個 server 。我們帶入 request 以及 response , 在 server 內可以用。
首先,我們定義發請求的 url 為常數 url
, 再來,我們定義發請求的方法為常數 method
如果常數 url
等於 /
時,會觸發一系列的 response ,並且 return res.end();
做結束。
如果常數 url
等於 /message
且常數 method
等於 POST
的話,定義常數 body
為 array。
接下來進入事件驅動, 當開始解析 request 時,我們帶入 chunk ,印出 chunk ,並且將 chunk 放入一個叫做 body 的 array 常數
另外一個事件,當 request 解析完成後, 定義一個常數叫做 parsedBody ,它是利用 buffer
物件來將在常數 body
內的所有 chunk 串連起來,然後變成 string
在定義一個常數叫做 message , 首先, 常數 parsedBody 是一個 string ,我們將這個 string 用 =
為分隔點,將這個 string 變成 array 之後,取 [1] ,就是這個 array 的第二項資料,這個就是常數 message 的值
接下來,我們利用一開始引用的 fs
module , 將常數 message 的內容寫入一個叫做 message.txt
的檔案。
接下來,定義 response 的 status code 為 302
定義 response 跳轉的 location 為 /
最後, return res.end();
出了事件驅動之後,是定義 header ,然後定義另外一些 html 的 response , 最後是 res.end();
Server 內的執行部分到此做一個結尾。
由於 js 的事件驅動屬性,事件 end
並不會先被執行,反之,後面的代碼會先被執行。
所以這個更動會造成一個錯誤,那就是當 res.end();
已經被執行了,才開始執行 end
事件內的 setHeader
以及 statusCode
,這樣就會造成如下的錯誤
如果,我們在 end
事件之下加了 return ,那錯誤就不會出現 , 修改代碼如下:
req.on('end', () => { |
因為 end
事件之下的 res.setHeader('Content-Type', 'text/html');
就不會被執行了。
注意! 在 return 的當下,其實 end
的監聽事件是還沒有被執行的,但是當 server
裡頭的動作執行完畢之後, request
被解析完成,觸發了 end
的監聽事件,然後才開始執行這個事件裡頭的動作。
至此,此 Episode 告一段落,截至目前的完整程式碼如下:
const http = require('http'); |
Blocking and Non-Blocking Code
所以,fs.writeFile 跟 fs.writeFileSync 差在哪?
fs.writeFileSync 會待這個檔案寫入的任務完成之後,才會繼續向後執行,而 fs.writeFIle 會異步執行,儘管檔案寫入的任務還沒完成,程式一樣會繼續向後執行,並且,我們可以在任務完成時執行一項 callback ,修改代碼如下:
req.on('end', () => { |
從以上的代碼來看,當程式執行到寫入檔案那一行, fs.writeFile
,程式不會停下來等待 fs.writeFile
執行完畢,反之,程式會繼續往下跑 , 而當 fs.writeFile
執行完畢後,會觸發我們設定的 callback
,進而執行以下的代碼
res.statusCode = 302; |
簡述事件迴圈
本章節主要參閱官方文件 , 以及這位大大的文章 。
在本章節中,主要是搞懂 Node.js 中事件迴圈的概念。
- Node.js 的架構圖
上圖可以看到,除了 V8 Engine , Node.js 使用了 libuv
來處理 I/O 的部分,提供了 asynchronous 以及 Non-Blocking API 以及事件迴圈 , 下面提到的事件迴圈,主要與 libuv
有關。
- 什麼是事件迴圈?
事件迴圈,藉由將工作量分擔給 Kernel 來處理,使 Node.js 得以做非阻塞 I/O 的操作,儘管 JsvaScript 是單線程的。
因為目前新型的 Kernel 都是多線程的,它們可以在背景運行多個程序。當其中一個程序完成了, Kernel 會通知 Node.js ,所以 Node.js 會調整將適合的 callback 加到 poll 階段的 queue 當中 ,這些 callback 最終將會被執行。
深談事件迴圈
以下是事件迴圈各個階段圖,以及運行順序
每個階段都有自己的 先進先出
的要被執行的 callback queue 。
每個階段都有自己特別的運行方式,一般來說,當事件迴圈跑到一個特定的階段,事件迴圈將會執行這個特定階段裡頭的操作,然後執行它的 callback ,這個執行的動作會重複,直到該階段內的 callback 都被執行完畢了,或者已經達到最大的執行數量。
當 queue 裡頭的工作都被處理完了,或者已達最大執行數量限制,事件迴圈會進入下一個階段,反覆循環。
因為上述提到的這些程序很有可能排定更多的程序,且由 poll
階段處理的事件將被 kernel
佇列著 , 所以 poll
事件可以在被佇列的同時也被執行。 造成的結果是,一個耗時較長的 callback , 會允許 poll
階段執行的久一點,甚至讓 timer
階段的工作等待。
各階段概述
- timers: 這個階段主要處理
setTimeout()
以及setInterval()
排程的 callback - I/O callbacks: 除了
timers
,setImmediate()
,close
之外的多數類型 - idle, prepare: 只供內部使用
- poll: 取回新的 I/O 事件; 某些情況, node 將會阻塞在這裡
- check:
setImmediate()
callbacks 將會在這階段被觸發 - close callbacks: socket, on …
libuv 各階段詳述
timers:
簡單來說, timers
階段將處理 setTimeout()
以及 setInterval()
的工作。 timers
並不保證可以準確地在給予的時間點執行 callback , 反之 ,給予的時間更像是一個最低的門檻,唯有過了這個給予的時間點, callback 才會被執行,這視乎當時的工作狀態。 系統的排程或者是其他 callback 的運行都可能會延遲 timers
執行的確切時間。總而言之,過了指定的時間點之後, timers
會盡可能地盡快執行排程的 callback
可以看看以下的範例:
var fs = require('fs'); |
從上面的範例中可以看到, setTimeout
任務原定 100 ms 之後被執行,但是 someAsyncOperation
任務花了 0 + 200 ms ,當執行這個任務時,事件迴圈正處在 poll
階段,所以在一個循環中, 需等待 poll
階段中的任務完全處理完畢,或者達到最大處理數量限制。
所以在上面的範例中,需等待 poll
階段的任務 someSyncOperation
以及 anotherSyncOperation
被執行完畢,總共花費 400 ms 左右, 之後才會執行 setTimeout()
的任務。
I/O callbacks
這個階段主要執行系統端操作的 callbacks, 像是 TCP 錯誤。
舉例來說,當試圖連接時,如果一個 TCP socket 接收到 ECONNREFUSED
, 某個 *nix
系統想要等待並回報錯誤,這些都會在 I/O callbacks
階段被佇列。
poll
poll
階段有兩種主要功能:
- 替時間點已經到的
timers
執行腳本 - 處理
poll
queue 當中的事件
當事件進入 poll
階段,且沒有 timers
排程事件 , 下面兩件事中,其中一件會發生:
- 如果
poll
階段不為空,事件迴圈將會執行佇列中的所有 callbacks ,又或者達到最大 callbacks 處理上限 - 如果
poll
階段為空,以下兩件事中,其中一件會發生:- 如果腳本已經被
setImmediate()
排程,事件迴圈將會結束poll
階段,並且繼續進入到check
階段來處理該佇列中的排程 - 如果腳本沒有
setImmediate()
的排程,那事件迴圈將會等待新的事件被加入到佇列,然後立即處理他們
- 如果腳本已經被
一旦 poll
循環為空,事件迴圈將會檢查 timer
中有沒有可以執行的 callback。 如果有一個或多個可以執行了, 事件迴圈會回去執行 timer
階段的 callback
check
這個階段允許在 poll
階段完成後,立即執行 callback。
如果 poll
階段處於空轉,或者已經有 setImmediate()
的排程,事件迴圈將會繼續進入到 check
階段,而不會等待。
setImmediate()
事實上,是一個很特別的 timer
階段,它跟 timer
在事件迴圈內跑在不同的階段。 它使用 libuv
API ,這個 API 排程 callback 使之在 poll
階段結束後被執行
通常,事件迴圈會停在 poll
階段等待新的 request 或 connection ,但是當 setImmediate()
有排程,且 poll
階段處於空轉, 那事件迴圈將會結束 poll
階段,並且進入 check
階段
close callbacks
如果一個 socket 或 handle 忽然被關閉, close
事件將會被置於這個階段,除非我們指定 process.nextTick
來執行它
setImmediate() vs setTimeout()
setImmediate()
和 setTimeout()
很類似,但根據被呼叫的時機不一樣,行為也不同。
setImmediate()
被設計為,一旦poll
階段結束時執行setTimeout()
排程任務,在特定的時間之後執行
兩者之間執行的順序,根據被呼叫時的情況而有所不同。如果兩者都在主模組的時候被呼叫,那順序將由當時的程序的表現所決定,意思就是說,順序無法預測。
範例如下:
// timeout_vs_immediate.js |
然而,如果兩者是在 I/O cycle 中被呼叫,那 sedImmediate()
將會優先於 setTimeout()
// timeout_vs_immediate.js |
對比 setTimeout()
, 使用 setImmediate()
的主要優勢為,如果在 I/O cycle
中, setImmediate()
將會被優先執行,不管 setTimeout()
有幾個
process.nextTick()
理解 process.nextTick()
你可能已經注意到, process.nextTick()
並沒有被顯示在圖表上,儘管它也是 asynchronous API 的一部分。 這是因為 process.nextTick()
技術上來說不算是事件迴圈的一部分。 nextTickQueue
將會在目前操作完成後,立即被執行,不管目前是在事件迴圈內的哪一個循環。
看看我們的圖表,不管在什麼時候,只要你在特定的階段呼叫 process.nextTick()
, 所以經由 process.nextTick()
送出的 callbacks 將會在事件迴圈啟動下一個階段之前全部都處理完畢。 這樣的模式可能會造成一些不好的情況發生,因為如果你遞迴的使用 process.nextTick()
callback ,就會造成所謂的 I/O 飢餓
,事件迴圈將會無法進入 poll
階段
為什麼這樣的行為會被容許?
你可能會想,為什麼這樣的行為在 Node.js 終會被容許? Node.js 部分的設計哲學是, API 總是異步的,不管是否必要,可以參考以下範例:
// this has an asynchronous signature, but calls callback synchronously |
如果我們執行上面的代碼,會出現輸出如下:
因為 someAsyncApiCall
並沒有做任何異步的動作,照同步的流程跑到 console.log
時, bar 還沒有被定義
如果我們將代碼改成以下:
function someAsyncApiCall (callback) { |
可以得到以下的輸出:
如上所述, process.nextTick()
的執行時間,是在當前的階段內所有的工作都完成了,在進入下個階段之前,會將所有的 process.nextTick()
處理完畢。
在上面的例子中, process.nextTick()
會等到所有在此階段的代碼都被執行完畢,也就是待 var bar = 1
執行後,才去執行這個 callback
,所以不會出現 undefined 的情況。
請注意!這沒有最大處理數量限制,所以如果利用 process.nextTick()
指派遞迴任務,那就會造成 I/O 飢餓
情況, 事件迴圈將無法接收到新的 request
一個 tick 到底是多長?
一個 tick 的時間長度,是 Event Loop 繞完一圈,把所有 queues 中的 callbacks 依序且同步地執行完,所消耗的總時間。因此,一個 tick 的值是不固定的。可能很長,可能很短,但我們希望它能盡量地短。
process.nextTick() vs setImmediate()
千萬不要被這兩個階段的命名搞混了!
process.nextTick():
在當前階段結束前執行完畢setImmediate():
在下一個階段,或者下一個事件迴圈的 tick 中執行
基本上,這兩個命名應該是要互換。 process.nextTick()
比 setImmediate()
更快地被觸發。
這算是一個很難更動的部分,因為當初命名錯誤之後,隨時時間的推移,越來越多 npm 的 package 都是使用這樣的命名,所以一旦這命名變更了,影響會非常的大。
官方文件上建議開發者,在任何情況中,都使用 setImmediate()
,因為它可以更簡單的被邏輯思考,然後在不同的環境上,有著更廣的相容性。
Promise
從下面的原始碼可以看到 Promise
, 或者又稱為 microtasks
的執行優先順序
依照原始碼的執行順序來看,在一個階段結束之前,process.nextTick()
會先被執行,緊接著, 執行 Promise
。
startup.processNextTick = function() { |
事件迴圈總結
順序:
timers → I/O callbacks → idle, pare → poll → check → close callbacks → timers … 往復循環順序細節
timers
設定的時間過了之後,才會被’盡快’的執行。
如果poll
階段內還有工作還沒做完,會先做完,才會執行timers
的工作,所以可能會延遲- 當處於
I/O
程序中,比如說,fs
模組中,setImmediate()
順序一定大於setTimeout()
,因為check
階段緊接在poll
階段之後 - 當處於主要模組中,
setImmediate()
以及setTimeout
的優先順序,取決於運行狀況,這個狀態下,次序無法確定 process.nextTick()
將在當前階段的工作結束前,在進入下一個階段之前執行, 所以他的優先性是第一名的promise
的執行次序緊接在process.nextTick()
之後,也是在當前階段結束前執行完畢
Express.js
建立一個 app server
安裝 npm
npm install --save
安裝 express
npm install --save express
安裝 nodemon
npm install --save-dev nodemon
將 npm start script 設為 nodemon
Set script as nodemon fileName.js
指定 status code
res.status (statusCode);
Promise
以下的範例中, function test 中,我們 return 了一個 Promise
,如果帶入 test function 中的 argument 是 1 ,那就走 resolve
路線 , 而除了 1 之外所有的 argument, 都走 reject
路線。
在 function main 中, 我們使用了 function test, 並帶入 argument 1, 個人覺得這有點像是 PHP 當中的 ternary
用法。
當 argument 等於我們在 promise
當中指定的 1 時,走 resolve 路線, 而 then
就是當 promise 為 resolve 路線時該做的事。
當 argument 等於是除了 1 之外的任何數,也就是會走 promise
當中的 reject 路線, 此時將會執行 catch
的動作。
我們在 promise 當中指定,當走 resolve
路線時,輸出為字串 Success, 所以在 then
的 closure 當中,被帶入的 argument 就是 Success
反之,當走 reject
路線時,輸出字串為 Error, 所以在 catch
的 closure 當中,被帶入的 argument 則為 Error
function test(number) { |
建立 Datastore Model
// 從 google SDK 引用 Datastore function |
建立一個 Controller
|
Route
var express = require('express'); |
Root address
// 找一個地方新增一個檔案,輸入以下的 code |
若我們 console.log 上面 exports 的值,可以得到該專案下的 root 位址
Path
利用 path module 來指定路徑
// p 會等於專案根目錄下, data 資料夾之下的一個叫做 `products.json` 的檔案 |
object.assign
- 可用來複製或覆蓋目標物件
let exampleObject = {a:1, b:2, c:3, c:4};
let copy = object.assign({},
exampleObject,
{a:4, b:4, c:4, d:4});
test
- 用來確認該 string 是否符合該 regex patten
var str = "Hello world!";
// look for "Hello"
var patt = /Hello/g;
var result = patt.test(str);
// result = true
時間
var moment = require('moment-timezone'); |
同時異步發多請求,並待全部有結果後繼續
// 需安裝兩個套件 `request-promise` 以及 `p-limit` |
dotenv
安裝
npm
npm install dotenv |
yarn
yarn add dotenv |
建立 .env 檔
.env
touch .env |
- require 語法, 會自動去讀 .env 檔
require('dotenv').config();
customName.env
touch custom.env |
- require 語法, 需要額外指定
require('dotenv').config({ path: 'custom.env' });
使用方法
直接使用 process.env
let DB_AUTH = process.env.DB_AUTH; |
使用 multer 上傳檔案
安裝
npm install multer --save |
定義 storage
在 /Storages/local.js 輸入以下 code
const multer = require('multer'); |
建立 middleware
在 server.js require 上面定義的 storage 以及 multer
const multer = require('multer'); |
將 upload 加到 route
App.post('/logParsing/upload', [upload.single('log'), LogParsingController.validate('upload')], LogParsingController.upload); |
Error Handing
在 Controller 中自定義 error handing, 也可建立一個 middleware 來驗。 注意: err, req, res, next
的順序至關重要!!
upload(err, req, res, next) { |
validator
安裝
npm install express-validator --save |
引用模組
在 controller 引用模組
const { body } = require('express-validator'); |
設立規則
在 controller 定規則, 新增一個 method
validate(method){ |
驗證並回報錯誤
在 controller 中定義錯誤並設定回報格式
const errors = validationResult(req); |
使用 validator
以 middleware 的方式使用 validator
App.post('/netdatadb/sum', NetdataController.validate('getSumData'), NetdataController.getSumData); |
結果
若有錯誤, 結果如下:
{ |
serve static file in Express
app.use('/static', express.static(path.join(__dirname, 'public'))) |
path.join(__dirname, ‘public’): 使用絕對路徑讀取檔案
以下為請求的路徑範例:
http://localhost:3000/static/images/kitten.jpg |
Error Handing in Express
同步的錯誤需要被 catch 嗎? 不需要
對 controller 的 error handler 的 error catch?
exports.catchErrors = (fn) => {
return function(req, res, next) {
return fn(req, res, next).catch(next);
};
};next()
與next(err)
的分別?next()
是將當前的 middleware 終止, 並導向下一個 middleware, 而next(err)
則是會直接導向 err handler異步錯誤需要特別被 catch 嗎? 需要哦!
下面的代碼中, next 的位置是怎麼樣的一種寫法? 將 next 置於 callback 的位置, 當沒有錯時, 跳往下一個 handler, 而當有錯誤時, 將錯誤導向 error handler
app.get('/', [
function (req, res, next) {
fs.writeFile('/inaccessible-path', 'data', next)
},
function (req, res) {
res.send('OK')
}
])在 asynchronous 中, 如何處理 error? 若出錯, 要將 err 放到 next() 中帶往 error handler
下面的代碼中, 處理 error 的邏輯是什麼? 當 fs.readFile 沒錯時, 跳往下一個 handler, 如果有錯, 跳往 error handler
app.get('/', [
function (req, res, next) {
fs.readFile('/maybe-valid-file', 'utf-8', function (err, data) {
res.locals.data = data
next(err)
})
},
function (req, res) {
res.locals.data = res.locals.data.split(',')[1]
res.send(res.locals.data)
}
])在 production 環境中, Express 會將 stack trace 送往客戶端嗎? 不會
如果已經開始回 response 了才遇到錯誤, 比如說正在串流到客戶端, Express 預設的 error handler 會結束掉連線嗎? 會哦
在使用自定義 error handler 時, 若要避免正在回 response 時遇到錯誤, 要注意什麼? 要檢查 header 是否已經傳送了, 若是, 則要將錯誤導向 Express 預設 error handler 來中止連線
以下代碼是什麼樣的錯誤處理邏輯? 如果 header 已經傳送了, 將錯誤傳給 Express 預設 error handler 來中止連線
function errorHandler (err, req, res, next) {
if (res.headersSent) {
return next(err)
}
res.status(500)
res.render('error', { error: err })
}什麼情況之下, 即使已經有建立自定義 error handler 了, Express 還是會將 error 送到預設的 error handler? 當 next(err) 被呼叫了一次以上
定義一個 error-handling middleware, 需要幾個參數? 四個
如何在自訂的 error handler 中, 在回應 client 之前, log stack trace?
console.error(err.stack)
怎麼寫一個最簡單的 log error 的 middleware?
function logErrors (err, req, res, next) {
console.error(err.stack)
next(err)
}請敘述下面的 error handler 邏輯 若請求有使用 xhr 的話, 直接回應指定錯誤訊息, 若無, 導向預設 error handler (因為已經呼叫兩次)
function clientErrorHandler (err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' })
} else {
next(err)
}
}如果我有兩組以上的 app.get(‘/‘, [fn1, fn2, fn3]), 現在我已經執行了 fn1, 我想要跳掉 fn2, fn3 到下一個 app.get(‘/‘, [fn4, fn5, fn6]), 該怎麼做?
fn1(req, res, next){
//do something
next('route')
}
留言