---
# System prepended metadata

title: ' 第十一堂：Express middleware 異常狀態處理'
tags: [2025 Node.js 後端工程師培訓營]

---

# 第十一堂：Express middleware 異常狀態處理

- 錄影
- API 應用可參考[線稿圖](https://miro.com/app/board/uXjVLbiDml0=/)
- API 開發規格請依照 [此 API 文件](https://liberating-turtle-5a2.notion.site/38e4dd6775894e94a8f575f20ca5b867?v=17c6a246851881208205000cacf67220)
- db 欄位設計請依照 [此資料庫欄位圖片](https://firebasestorage.googleapis.com/v0/b/hexschool-courses.appspot.com/o/hex-website%2Fnode%2F1739179853094-fitness_5.png?alt=media&token=a65de209-3ae6-4263-bdc0-d4f08638ff20) 定義
## 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);
```



## 中場休息
* 提醒分組表單填寫


## 下半場


## 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 = "驗證失敗"
```

## express 404
```jsx=
// 404 錯誤處理
app.use(function(req, res, next) {
  res.status(404).json({
    status: 'error',
    message: '無此路由資訊',
  });
});

// Express 錯誤處理
//當有錯誤被 throw 或在 next(err) 時，會自動進入到這個函式，並在此將 err.message 回傳給前端。
app.use(function(err, req, res, next) {
  res.status(500).json({
    err: err.message,
  });
});
```

## 資料表與 router 更新
> 1. 調整 course.js 實體後，執行 `npm run clean`，
> 2. 註冊 > 變更為教練身份 > 新增技能> 新增課程 > 檢視所有課程

1.建立是否為教練身份的 middleware
2.取得課程 API
3. 錯誤種類
    * App 層(app.js)
    * 伺服器層(bin/www.js)

## entities > Course.js
```
const { EntitySchema } = require('typeorm')

module.exports = new EntitySchema({
  name: 'Course',
  tableName: 'COURSE',
  columns: {
    id: {
      primary: true,
      type: 'uuid',
      generated: 'uuid'
    },
    user_id: {
      type: 'uuid',
      nullable: false
    },
    skill_id: {
      type: 'uuid',
      nullable: false
    },
    name: {
      type: 'varchar',
      length: 100,
      nullable: false
    },
    description: {
      type: 'text',
      nullable: false
    },
    start_at: {
      type: 'timestamp',
      nullable: false
    },
    end_at: {
      type: 'timestamp',
      nullable: false
    },
    max_participants: {
      type: 'integer',
      nullable: false
    },
    meeting_url: {
      type: 'varchar',
      length: 2048,
      nullable: false
    },
    created_at: {
      type: 'timestamp',
      createDate: true,
      nullable: false
    },
    updated_at: {
      type: 'timestamp',
      updateDate: true,
      nullable: false
    }
  },
  relations: {
    User: {
      target: 'User',
      type: 'many-to-one',
      joinColumn: {
        name: 'user_id',
        referencedColumnName: 'id',
        foreignKeyConstraintName: 'courses_user_id_fk'
      }
    },
    Skill: {
      target: 'Skill',
      type: 'many-to-one',
      joinColumn: {
        name: 'skill_id',
        referencedColumnName: 'id',
        foreignKeyConstraintName: 'courses_skill_id_fk'
      }
    }
  }
})
```

## 示意：middlewares > isCoach.js
```jsx=
const FORBIDDEN_MESSAGE = '使用者尚未成為教練'
const PERMISSION_DENIED_STATUS_CODE = 401

function generateError (status = PERMISSION_DENIED_STATUS_CODE, message = FORBIDDEN_MESSAGE) {
  const error = new Error(message)
  error.status = status
  return error
}

module.exports = (req, res, next) => {
  // 週四會分享的驗證教練邏輯
  // if (!req.user || req.user.role !== 'COACH') {
  //   next(generateError())
  //   return
  // }
  next()
}
```

### 示意：middlewares > auth.js
```jsx=
const isLogin = function(req, res, next) {
  console.log("確認是否為登入");
  // 判斷使用者是否登入的邏輯
  // 檢查 session 或 token

  next();  // 若登入檢查通過，呼叫 next() 進入下一步
};

module.exports = isLogin;
```

## routes > courses.js


```jsx=
const express = require('express')
const { IsNull } = require('typeorm')

const router = express.Router()
const config = require('../config/index')
const { dataSource } = require('../db/data-source')
const logger = require('../utils/logger')('Course')


router.get('/', async (req, res, next) => {
  try {
    const courses = await dataSource.getRepository('Course').find({
      select: {
        id: true,
        name: true,
        description: true,
        start_at: true,
        end_at: true,
        max_participants: true,
        User: {
          name: true
        },
        Skill: {
          name: true
        }
      },
      relations: {
        User: true,
        Skill: true
      }
    })
    res.status(200).json({
      status: 'success',
      data: courses.map((course) => {
        return {
          id: course.id,
          name: course.name,
          description: course.description,
          start_at: course.start_at,
          end_at: course.end_at,
          max_participants: course.max_participants,
          coach_name: course.User.name,
          skill_name: course.Skill.name
        }
      })
    })
  } catch (error) {
    logger.error(error)
    next(error)
  }
})

module.exports = router

```

### 密碼加密
* NPM：[bcrypt](https://www.npmjs.com/package/bcrypt)：密碼加解密

> npm install bcrypt
```jsx=
const bcrypt = require('bcrypt')

async function hashPassword() {
  try {
    const salt = await bcrypt.genSalt(10)
    console.log('Salt:', salt)

    const databasePassword = await bcrypt.hash('1q2w3e4r', salt)
    console.log('Hashed password:', databasePassword)

    // 登入填寫的密碼比對
    const keyPassword = '1q2w3e4r'
    const isMatch = await bcrypt.compare(keyPassword, databasePassword);
    console.log(isMatch);
  } catch (error) {
    console.error('Error:', error)
  }
}

hashPassword()
```