---
# System prepended metadata

title: 第五堂主線任務與講義
tags: [Node.js 入門班 - 2024 春季班, Node.js 直播班 - 2024 春季班]

---

---
tags: Node.js 直播班 - 2023 春季班
---


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

