後端好朋友 Express 基本介紹與操作


Posted by Christy on 2022-01-10

這一系列文章為 [BE201] 後端 Express 學習筆記,主要紀錄學習過程,裡面包含 express 基本原理、基本操作、Middleware 介紹,最後以留言板來練習 express 實作

零、內容摘要

  1. 介紹如何不用框架實作一個 server,藉此了解基本原理

  2. Express 建立基本環境,並理解用 Express 跟之前第九週用 Apache + PHP 的差別

  3. Express 基本架構

一、要學框架,從不用框架先開始

server 只是一個程式,在介紹 Express 框架以前,先來看一下如何不用框架,實作底層的 server

用 node.js 實作一個 server

const http = require('http')
const server = http.createServer(handler)

function handler(req, res) {
  console.log(req.url)
  res.write('hello!')
  res.end()
}

server.listen(5001)

一個簡單導向的概念

const http = require('http')
const server = http.createServer(handler)

function handler(req, res) {
  console.log(req.url)
  if (req.url === '/hello') {
    res.write('hello!')
  } else if (req.url ==='/bye'){
    res.write('bye!')
  } else {
    res.write('invalid url')
  }
  res.end()
}

server.listen(5001)

二、初探 Express

如何建立一個 Express 環境

a. $ npm init,接著全按 enter

b. $ npm install express

c. 開新檔案,參考文件 Hello world example 引入 library

註:在 dev tool 記得要把 cache 打勾

Basic routing

const express = require('express')
const app = express()
const port = 5001

app.get('/', (req, res) => {
  res.send('hello')
})

app.get('/hello', (req, res) => {
  res.send('hello, man')
})

app.get('/bye', (req, res) => {
  res.send('bye, man')
})

app.listen(port, () => {
  console.log('success')
})

三、與之前 Apache + PHP 組合的差別在哪?

  • Apache + PHP: 瀏覽器 -> Apache server -> PHP,處理完再 PHP -> Apache server -> 瀏覽器

Apache + PHP檔案結構長怎樣,url 就長怎樣

  • Express: 瀏覽器 -> Express server

四、Express 基本架構

1. 基礎介紹

在 node.js 嘗試 MVC 架構,這裡用一個叫 EJS 的 template ,跟 PHP 蠻像的、蠻簡單的一個 template。

用 template 感覺就是把資料塞進去一個模板裡面,EJS,通常用 <% %> 操作

參考文件:Using template engines with Express

開始安裝:

a. $ npm install ejs

b. set engine to ejs app.set('view engine', 'ejs')

c. create a new folder named views and inside a new file named hello.ejs

d. 路徑說明

// 如果路徑是 '/hello',就去 render 叫做 hello 的檔案
app.get('/hello', (req, res) => {
  res.render('hello')
})

hello 檔案裡面的更動只要重新整理就會顯示,不需要重開 node

2. 先來個畫面吧,實作一個只能看的 todolist

todo list 畫面,對應網址:http://localhost:5001/todos

todos

  • first todo
  • second todo
  • third todo
// index.js

const express = require('express')
const app = express()
const port = 5001

app.set('view engine', 'ejs')

const todos = [
  'first todo', 'second todo', 'third todo'
]

app.get('/todos', (req, res) => {
  res.render('todos', {
    todos
  })
})

app.listen(port, () => {
  console.log('success')
})
// todos.ejs

<h1>todos</h1>
<ul>
  <% for(let i=0; i<todos.length; i++) { %>
    <li><%= todos[i] %></li> // 用 = 來輸出
  <% } %>
</ul>

單個 todo 畫面,對應網址:http://localhost:5001/todos/0

todo
first todo

// todo.ejs

<h1>todo</h1>
<h2><%= todo %></h2>
// index.js
app.get('/todos/:id', (req, res) => {
  const id = req.params.id
  const todo = todos[id]
  res.render('todo', {
    todo
  })
})

3. 把架構切成 MVC

運作方式:

  • model 資料

  • view 顯示畫面

  • controller 去 model 拿資料並且把資料給 view 顯示

資料夾結構:

  • controllers folder

    • todo.js 去 model 拿資料並把資料給畫面顯示
  • models folder

    • todo.js 有 getAll & get 兩個 methods
  • views folder

    • todo.ejs 顯示單個 todo

    • todos.ejs 顯示整個 todos

  • index.js

// controllers > todo.js

const todoModel = require('../models/todo')

const todoController = {
  getAll: (req, res) => {
    const todos = todoModel.getAll()
    res.render('todos', {
      todos
    })
  },

  get: (req, res) => {
    const id = req.params.id
    const todo = todoModel.get(id)
    res.render('todo', {
      todo
    })
  }
}

module.exports = todoController
// models > todo.js

const todos = [
  'first todo', 'second todo', 'third todo'
]

const todoModel = {
  getAll: () => {
    return todos
  },

  get: (id) => {
    return todos[id]
  }
}
module.exports = todoModel
// views > todo.ejs

<h1>todo</h1>
<h2><%= todo %></h2>
// views > todos.ejs

<h1>todos</h1>
<ul>
  <% for(let i=0; i<todos.length; i++) { %>
    <li><%= todos[i] %></li>
  <% } %>
</ul>
// index.js

const express = require('express')
const app = express()
const port = 5001

const todoController = require('./controllers/todo')

app.set('view engine', 'ejs')

app.get('/todos', todoController.getAll)

app.get('/todos/:id', todoController.get)

app.listen(port, () => {
  console.log('success')
})

五、Node.js 與 MySQL 的串接

1. 影片裡用的是 sequel pro 來操作 MySQL

Sequel Pro 語言介面恢復英文如何用 sequel pro 連到之前 xampp 的資料庫

error log:

裝好 Sequel Pro 以後無法登入,試了以前的所有帳密的組合都登不進去

後來又下載了 MySQL 然後不知道自己在幹嘛

經過一番回想,終於想起導讀有提示,花了幾個小時在上面,嗚嗚。

2. 開始安裝

參考文件:mysql

a. $ npm install mysql

b. 開資料庫

c. 新增 db.js 檔案(在參考文件的 introduction 裡面)

var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'your name',
  password : '*****',
  database : 'todos'
});

connection.connect();

connection.query('SELECT * from todos', function (error, results, fields) {
  if (error) throw error;
  console.log(results);
});

connection.end();

這樣寫其他地方比較好用

var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'your name',
  password : '*****',
  database : 'todos'
});

module.exports = connection

d. $ node db.js,成功得到資料了!

[
  RowDataPacket { id: 1, content: 'todo1\n' },
  RowDataPacket { id: 2, content: 'todo2\n' },
  RowDataPacket { id: 3, content: 'todo3\n' }
]

e. 連線 db

// index.js

const db = require('./db')

app.listen(port, () => {
  db.connect()
  console.log('success')
})

f. 改 model

// todos.js
const db = require('../db')

const todoModel = {
  getAll: (cb) => {
    db.query(
      'SELECT * from todos', (err, results) => {
      if (err) return cb(err)
      cb(null, results)
    });
  },

  // 在文件裡搜尋「prepared statements」 防止 sql injection

  get: (id, cb) => {
    db.query(
      'SELECT * from todos where id = ?', [id], (err, results) => {
      if (err) return cb(err)
      cb(null, results)
    });
  }
}

module.exports = todoModel

g. 改 controller

const todoModel = require('../models/todo')

const todoController = {
  getAll: (req, res) => {
    todoModel.getAll((err, results) => {
      if (err) return console.log(err)
      res.render('todos', {
        todos: results
      })
    })
  },

  get: (req, res) => {
    const id = req.params.id
    todoModel.get(id, (err, results) => {
      if (err) return console.log(err)
      res.render('todo', {
        todo: results[0]
      })
    })
  }
}

module.exports = todoController

h. views 也要改

// todos.ejs

<h1>todos</h1>
<ul>
  <% for(let i=0; i<todos.length; i++) { %>
    <li><%= todos[i].id + ': ' + todos[i].content %></li>
  <% } %>
</ul>
// todo.ejs

<h1>todo</h1>
<h2><%= todo.content %></h2>









Related Posts

用 TLA+ 幫你驗證系統規格設計

用 TLA+ 幫你驗證系統規格設計

Day 1 Markdown & Minimal Table

Day 1 Markdown & Minimal Table

使用JavaScript更改CSS偽元素

使用JavaScript更改CSS偽元素


Comments