Express get started:route 路由部分
basic routing: 表示 '/' 的 request 要給誰處理,導去哪裡
const express = require('express')
const app = express()
const port = 5001
app.get('/', (req, res) => {
// basic routing: 表示 '/' 的 request 要給誰處理,導去哪裡
res.send('Hello World!')
})
app.get('/poop', (req, res) => { // 表示 '/poop' 的 request 要給誰處理,導去哪裡
res.send('Hello Poop!')
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:5001`)
})
Express 與 Apache + PHP 的差異
- Apache + PHP: 會侷限在檔案系統,檔案結構長怎樣,url 就長怎樣。
- Express: 不需經過額外 php 處理(==待查證==),自己本身就是 server,透過路由系統(basic routing)可以根據不同的 url 回傳不同的結果,不需要有 js 檔案之類。
Express 基本架構與 MVC
- Model: 負責管理資料
- View: 顯示相關的模板 template
- Controller: 中間的協調者
Express 的 view (template engine) 寫法
- 先建立一個檔名是 view 的資料夾,裡面放 ejs 檔案
```javascript=
.
.
.
app.set('view engine', 'ejs')
// 第一個參數 /hello 為網址列路徑
app.get('/hello', (req, res) => {
res.render('hello')
// render view 底下的 hello 檔案
})
.
.
.
---
* 簡單範例
在 index.js 建立一個 todo 的陣列
```javascript=
.
.
const todos = ['first todo', 'second todo', 'third todo']
app.get('/todos', (req, res) => {
res.render('todos', { // 第二個參數把資料傳進去
todos: todos
})
})
.
.
建立 todo.ejs 的 view
<h1>todos</h1>
<ul>
<% for(let i = 0; i < todos.length; i++) { %>
<li><%= todos[i] %></li> <!-- 用 = 輸出資料 -->
<% } %>
</ul>
- ==template engine 會自己做 escape,所以
<%= 輸出內容 %>
可以避免 XSS==,用<%- 輸出內容 %>
就不會 escape
- 拿取網址列的參數
:id
. . const todos = ['first todo', 'second todo', 'third todo'] app.get('/todos/:id', (req,res) => { const id = req.params.id // 可以拿到上面網址列的 id const todo = todos[id] res.render('todo', { todo }) }) . .
Express 的 model 與 controller 重構寫法
- model 部分負責處理資料,把要用的資料包成 function
```javascript=
const todos = ['first todo', 'second todo', 'third todo']
const todoModel = {
getAll: () => {
return todos
},
get: id => {
return todos[id]
}
}
module.exports = todoModel
* controller 拿 model 的資料,送給 view 去 render
```javascript=
const todoModel = require('../models/todo')
const todoController = {
getAll: (req, res) => {
const todos = todoModel.getAll()
res.render('todos', {
todos: todos
})
},
get: (req, res) => {
const id = req.params.id
const todo = todoModel.get(id)
res.render('todo', {
todo: todo
})
}
}
module.exports = todoController
- 而一開始的路由 index 就可以直接導到 controller,之前上半部混著寫的部分就可以改成下半部直接用 function
app.get('/todos', (req, res) => {
res.render('todos', { // 第二個參數把資料傳進去
todos: todos
})
})
app.get('/todos/:id', (req,res) => {
const id = req.params.id // 可以拿到上面網址列的 id
const todo = todos[id]
res.render('todo', {
todo
})
})
===
app.get('/todos', todoController.getAll)
app.get('/todos/:id', todoController.get)
Node.js 與 MySQL 的串接
串接時遇到的問題
Q: 問一個有關 BE201 Node.js 與 MySQL 的串接影片實作的相關問題,我照著影片做之後,噴出第二張圖的錯誤 Error: connect ECONNREFUSED 127.0.0.1:3306
,查了一陣子發現是 port 預設不一樣,我就把 port 改成之前第九週在用 XAMPP 時的 localhost:8080 (第一張圖),再送出之後 terminal 跑了一陣子又噴第三張圖的錯誤 Error: Connection lost: The server closed the connection.
,查了很久大概都是說可能資料庫那邊沒弄好,但是我確定我 XAMPP 有開好,phpmyadmin 那邊也確定可以開也看得到裡面資料,找超久還是不知道問題出在哪裡。
A: 因為 xampp 是虛擬機,所以你 localhost:3306 本來就是沒有東西的,那為什麼 localhost:8080 會有東西呢?是因為在 xampp 的設定裡面,有把虛擬機的 8080 port 對應到你 localhost 的 8080 port,所以你 localhost:8080 就是連到虛擬機的 8080 port。因此解法就是把虛擬機的 3306 port 對應到自己的 3306 port,建立一個通道。
意思就是如果用虛擬機的 mysql,那你就要把虛擬機的 3306 對應到自己的 3306,連 localhost:3306 就等於是連 remote:3306
簡單來說要把虛擬機想成是另外一台主機比較好,而不是在你電腦裡的東西。
延伸資料:
關於 SSH Tunnel 連線
通訊埠轉發 Port Forwarding 設定教學
todo 範例
- 建立 mysql 連線檔
參考資料:github mysql.js
```javascript=
// db.js
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'ericcch24',
password : 'zxc124040219',
database : 'ericcch24'
});
module.exports = connection
2. index.js 設定連線
```javascript=
const express = require('express')
const app = express()
const port = 5001
const todoController = require('./controllers/todo')
const db = require('./db')
app.set('view engine', 'ejs')
// 設定網址列路徑
app.get('/todos', todoController.getAll)
app.get('/todos/:id', todoController.get)
app.listen(port, () => {
db.connect(); // 設定連線到資料庫
console.log(`Example app listening at http://localhost:${port}`);
})
- 設定 model
```javascript=
// /models/todo.js
const db = require('../db')
const todoModel = {
getAll: (cb) => { // 要用 callback 才可以拿資料
db.query('SELECT * from todos', (err, results) => {
if (err) return cb(err);
cb(null, results);
// 這裡設定第一個參數是 err, 第二個是 result
// 後面的 controller 就可以拿這邊的 callback:
// todoModel.getAll((err, result) => {})
});
},
get: (id, cb) => {
db.query('SELECT * from todos where id = ?', [id],
// 預防 SQL injection 所以用 prepare statement 語法
// 第二個參數在陣列內部放要取代問號的東西
(err, results) => {
if (err) return cb(err);
cb(null, results);
});
}
}
module.exports = todoModel
類似 MySQL 的 prepared statements 作法,==預防 SQL injection==
```javascript=
connection.query('UPDATE users SET foo = ?, bar = ?, baz = ? WHERE id = ?', ['a', 'b', 'c', userId], function (error, results, fields) {
if (error) throw error;
// ...
});
- 設定 controller
```javascript=
// /controllers/todo.js
const todoModel = require('../models/todo')
const todoController = {
getAll: (req, res) => {
todoModel.getAll((err, result) => {
if(err) return console.log(err);
res.render('todos', {
todos: result
})
})
},
get: (req, res) => {
const id = req.params.id
todoModel.get(id, (err,result) => {
res.render('todo', {
todo: result[0]
})
})
}
}
module.exports = todoController
result 長這樣:
```javascript=
[
RowDataPacket { id: 1, content: 'todo1' },
RowDataPacket { id: 2, content: 'todo2' },
RowDataPacket { id: 3, content: 'todo3' }
]
- 設定 view
```javascript=todos
-
<% for(let i = 0; i < todos.length; i++) { %>
- <%= todos[i].id + ': ' + todos[i].content %> <% } %>
```javascript=
<h1>todo</h1>
<h2><%= todo.content %></h2>
// 因為拿到的資料是物件