Express


Posted by ericcch24 on 2020-10-16

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 範例

  1. 建立 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}`);
})
  1. 設定 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;
  // ...
});
  1. 設定 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' }
]
  1. 設定 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>
// 因為拿到的資料是物件
tags: Week17

#week17







Related Posts

React-[核心篇]- 元件 &元件實例&React Element

React-[核心篇]- 元件 &元件實例&React Element

[第六週] 認識 HTML 及常用標籤 (上)

[第六週] 認識 HTML 及常用標籤 (上)

F2E合作社|互動圖文卡片|網頁切版

F2E合作社|互動圖文卡片|網頁切版


Comments