# 第十一堂: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()
```