# 第五堂主線任務與講義 * 錄影 * [線上講義](https://whimsical.com/NJzhqQpRX1YcogzPz6ro5e) * [線上範例程式碼](https://github.com/gonsakon/express-week4-sample) ## express middleware * [middleware 中介介紹](https://expressjs.com/zh-tw/guide/using-middleware.html) ### app.use ``` =JavaScript const express = require('express'); const app = express(); app.get('/',function(req,res){ res.status(200).json({ "status":"success", "message":"你目前造訪到首頁" }) }) app.use(function(req,res,next){ console.log('有人進來了'); kk(); next(); }) app.use(function(req,res,next){ res.status(404).json({ status:"false", message:"您的路由不存在" }) }) app.use(function(err,req,res,next){ console.log(err.name); res.status(500).json({ "err": err.name }) }) // 監聽 port var port = process.env.PORT || 3000; app.listen(port); ``` ### error 處理 * [官網資訊](https://expressjs.com/zh-tw/guide/error-handling.html) ```=JavaScript const express = require('express'); const app = express(); app.get('/',function(req,res){ res.status(200).json({ "status":"success", "message":"你目前造訪到首頁" }) }) app.use(function(req,res,next){ console.log('有人進來了'); next(); }) app.get('/user',function(req,res){ res.status(200).json({ "status":"success", "message":"你目前造訪到 user 頁面" }) }) app.use(function(req,res,next){ res.status(404).json({ status:"false", message:"您的路由不存在" }) }) app.use(function(err,req,res,next){ console.log(err.name); res.status(500).json({ "err": err.name }) }) // 監聽 port const port = process.env.PORT || 3003; app.listen(port); ``` ## 中介設計 ```=javascript const express = require('express'); const app = express(); const checkKeyword = function(req,res,next){ if(req.query.q){ next() }else{ res.status(400).json({ "message":`您並未輸入關鍵字` }) } } app.get('/search',checkKeyword,function(req,res){ res.status(200).json({ "status":"success", "keyword":`你搜尋到的是${req.query.q}` }) }) // 監聽 port const port = process.env.PORT || 3003; app.listen(port); ``` ## 中場休息 ## 下半場 1. 404、express 全域錯誤補捉設定 2. 補捉程式錯誤、catch 補捉 3. 自訂可預期錯誤 appError 4. 增加開發狀態與上線狀態的錯誤回饋 5. 增加統一處理 asyncError 服務 ## error 處理 * [JS error 介紹](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Error) * try catch 示範 ``` JavaScript const a = new Error("姓名欄位沒有填寫") a.name = "驗證失敗" ``` ## 補捉程式錯誤 ```=javascript process.on('uncaughtException', err => { // 記錄錯誤下來,等到服務都處理完後,停掉該 process console.error('Uncaughted Exception!') console.error(err); process.exit(1); }); ``` ## 補捉未處理的 catch ``` =JavaScript // 未捕捉到的 catch process.on('unhandledRejection', (reason, promise) => { console.error('未捕捉到的 rejection:', promise, '原因:', reason); // 記錄於 log 上 }); ``` ## appError.js 調整 ```=JavaScript const appError = (httpStatus,errMessage,next)=>{ const error = new Error(errMessage); error.statusCode = httpStatus; error.isOperational = true; return error; } module.exports = appError; ``` ## posts.js 調整 ```=JavaScript router.post('/', async function(req, res, next) { // query params if(req.body.content == undefined){ // 調整處 return next(appError(400,"你沒有填寫 content 資料")) } const newPost = await Post.create(req.body); // res.send('<h1>1234</h1>'); res.status(200).json({ status:"success", post: newPost }) }); ``` ## async 補充 * [MDN 補充](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/async_function) ``` =JavaScript async function add(a) { const data = await a+a; return data; } add(3) .then(res=>console.log(res)) .catch(err=>console.log(`error!! ${err}`)) ``` ## 重構自訂 try catch(util/handleErrorAsync.js) ``` =JavaScript const handleErrorAsync = function handleErrorAsync(func) { // func 先將 async fun 帶入參數儲存 // middleware 先接住 router 資料 return function (req, res, next) { //再執行函式,並增加 catch 條件去捕捉 // async 本身就是 promise,所以可用 catch 去捕捉 func(req, res, next).catch( function (error) { return next(error); } ); }; }; module.exports = handleErrorAsync; ``` ## 額外補充 在 Window 上面設定 development 與 production 時會有一些狀況發生 ```json "start:dev": "NODE_ENV=dev nodemon ./bin/www" ``` 因此若要讓環境變數正確運作的話,要額外安裝 [cross-env](https://www.npmjs.com/package/cross-env) 套件,並改為以下寫法才能順利執行 ```json "start:dev": "cross-env NODE_ENV=dev nodemon ./bin/www" ``` ## 本堂作業 將第四堂期中考作業加上更嚴謹的錯誤狀態: 1. 請設計一個 middleware,讓 controller 程式碼裡面沒有 try catch 2. 請透過環境變數執行指令加上 dev、production 的客製化回饋 3. 承第二點,請觀看此張[圖](https://whimsical.com/NJzhqQpRX1YcogzPz6ro5e),確保你的後端語言有客製化各種錯誤狀態,包含 NPM 的錯誤訊息客製化 4. 請透過 node.js uncaughtException、unhandledRejection 來捕捉預期外的錯誤