# 第五堂主線任務與講義
* 錄影
* [線上講義](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 來捕捉預期外的錯誤