- Express 在處理 request 到收到 response 的這段過程,中間會經過一系列的 middleware 處理。
例如:
app.get('/todos', todoController.getAll)
app.get('/todos/:id', todoController.get)
上面的todoController.getAll
與 todoController.get
就算是 middleware
其中app.use((req, res, next) => {})
可以讓整個應用程式都可以使用這個 middleware,==加第三個參數 next
才會把控制權交給下一個 middleware==
app.use((req, res, next) => {
console.log('time:', new Date())
next()
// 加 next 才會把控制權交給下一個 middleware
// 不然就只會跑到上面的印出日期就停了
})
解析 Request 必備:body-parser
在 express 內建原本只能拿 get 方法的網址列上的 query string 資料,post 的話就要用 body-parser 才能拿到 requset body 內部的資料。
index.js
```javascript=
const bodyParser = require('body-parser')
.
.
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
// 以上的 middleware 可以幫助解析 request body
.
.
app.post('/todos', todoController.newTodo)
// 在列出所有 todo 的頁面 post 新增的 todo
app.get('/', todoController.addTodo)
// 在根目錄 input 內容
.
.
* controller
```javascript=
newTodo: (req, res) => {
const content = req.body.content
// 可以拿到 addTodo 那邊 input 的內容
// 如果沒有設定 index 那邊的 body-parser 就拿不到
todoModel.add(content, (err) => {
if(err) return console.log(err)
res.redirect('/todos') // 成功後導回'/todos'
})
},
addTodo: (req, res) => {
res.render('addTodo')
},
- model
add: (content, cb) => { db.query('insert into todos(content) values(?) ', [content], (err, results) => { if (err) return cb(err); cb(null); }); }
- view -> addTodo
```htmlembedded=Add Todo
Content:
---
## 負責管理 Session 的 Session middleware: express-session
* index.js
```javascript=
.
.
const session = require('express-session')
.
.
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}))
// 管理 session 的 middleware
.
.
.
app.get('/', todoController.addTodo)
app.get('/login', (req, res) => {
res.render('login')
})
app.post('/login', (req, res) => {
if(req.body.password === '123') {
req.session.isLogin = true
res.redirect('/')
} else {
res.redirect('/login')
}
})
app.get('/logout', (req, res) => {
req.session.isLogin = false
res.redirect('/')
})
- controller
addTodo: (req, res) => { res.render('addTodo', { isLogin: req.session.isLogin // 傳入 isLogin 參數 // 讓 addTodo 根據 isLogin 的狀態來決定要印出什麼內容 }) },
- view: login
```htmlembedded=Login
Password:
* view: addTodo
```htmlembedded=
<h1>Add Todo</h1>
<% if (isLogin) { %>
你已經登入 <a href="/logout">logout</a>
<% } else { %>
你還沒登入
<% } %>
<form method="POST" action="/todos" >
Content: <input type="text" name="content" />
<input type="submit" />
</form>
顯示錯誤訊息神器:connect-flash
錯誤訊息 flash,重整一次頁面會消失,只會顯示發生錯誤的那一次
- index.js
. . const flash = require('connect-flash'); . . app.use(flash()) // 錯誤訊息 flash,重整一次頁面會消失,只會顯示發生錯誤的那一次 . . app.get('/login', (req, res) => { res.render('login', { errorMessage: req.flash('errorMessage') // 在登入頁面拿取存在 flash 內的參數 }) }) app.post('/login', (req, res) => { if(req.body.password === '123') { req.session.isLogin = true res.redirect('/') } else { req.flash('errorMessage', 'password incorrect') // 登入失敗時設置兩參數 (key, value) res.redirect('/login') } })
- view: login
```htmlmixed=Login
<%= errorMessage %>
Password:
---
## 設定可以在 view 隨意存取的參數
* 因為在一堆地方都要傳入提到的 `isLogin`, `errorMessage` 等參數,很麻煩。
* 此時將要用的東西放入`res.locals`(類似全域變數的概念),在 view 那邊就可以直接使用。
* 在 `res.locals` 指定好的參數,view 就可以直接使用
```javascript=
app.use((req, res, next) => {
res.locals.isLogin = req.session.isLogin
res.locals.errorMessage = req.flash('errorMessage')
// 將要設定的東西放入 res.locals,view 就可以直接使用
next();
})
- 設定好之後 render 的部分就不用再傳參數給 view,在 view 就可以直接使用。
app.get('/login', (req, res) => {
res.render('login', {
errorMessage: req.flash('errorMessage')
// 在登入頁面拿取存在 flash 內的參數
})
})
===
app.get('/login', (req, res) => {
res.render('login')
});
addTodo: (req, res) => {
res.render('addTodo', {
isLogin: req.session.isLogin
// 傳入 isLogin 參數
// 讓 addTodo 根據 isLogin 的狀態來決定要印出什麼內容
})
}
===
addTodo: (req, res) => {
res.render('addTodo')
}