Express


Posted by chihyu on 2021-01-25

用 Node.js 寫 http server

const http = require('http')
const server = http.createServer(handler) // t傳一個 function
function handler(req, res) {
    console.log(req.url)
    if (req.url === '/hello') {
        res.writeHead(200, {
            'Content-Type': 'text-html'
        })
        res.write('hello!')
    } else if (req.url =='/bye') {
        res.write('bye')
    } else {
        res.write('Invalid url')
    }
    res.end()
}
server.listen(5001) // port
  1. 在 CLI 上面跑檔案,讓 server 跑起來
  2. 在瀏覽器上輸入 localhost:5001
  3. CLI 會顯示 favicon.icon,是 tag 上的小圖示
  4. 用網址內容決定輸出的內容是甚麼
  5. 設定 header,Content-type 可以告訴瀏覽器該怎麼解析

Express

安裝

  1. 建立一個 express-demo 的資料夾
  2. npm init,會出現一堆敘述,直接按 enter 按到底,init 完後會出現一個 package.json 的資料夾
  3. npm install express

基本操作

  1. 新建一個 index.js 的檔案
  2. 進入 Express -> getting Started -> Hello World 看範例

    const express = require('express') // 引入 express 這個 library
    const app = express() // express 引入進來的東西是一個 function 
    const port = 5001 // 指定 port
    // 第一個參數帶網址,第二個帶要回傳的東西
    app.get('/', (req, res) => {
     res.send('Hello World!')
    })
    app.get('/hello', (req, res) => {
     res.send('hello man')
    })
    app.get('/bye', (req, res) => {
     res.send('yo bye')
    })
    // 跑起來
    app.listen(port, () => {
     console.log(`Example app listening at http://localhost:${port}`)
    })
    
  3. 儲存,在 cli 輸入 node index.js,當看到 Example app listening at http://localhost:5001 代表有跑起來

  4. 在瀏覽器輸入 localhost:5001/要帶的網址,就可看到相應的內容

Apache + php 跟 Express 的差別

Apache + php

  • Apache 接收 resquest, Apache 掛了就都掛了
  • 瀏覽器 <-> Apache server <-> php(處理器)
  • 由 php 處理完再回傳回去
  • 侷限在檔案系統,檔案結構(路徑)長怎樣,url 就是怎樣

Express

  • 瀏覽器 <-> Express server
  • 所有東西都由 Express 處理再回傳回去
  • 路由系統可以自行決定甚麼樣的網址要回傳甚麼樣的資料

基本架構

  1. 安裝 ejs (跟 php 有點像的東西)
    npm install ejs
    
  2. 新增資料夾 views,template 檔案的預設位置
  3. 新增一個 ejs 的檔案存到 views 裡面
  4. 在 index.js 檔案設定 template engine
    app.set('view engine', 'ejs') // 設定 ejs 為 template engine
    
  5. 在 index.js render views 裡面的檔案內容

Ex1. hello.ejs

<h1>hello</h1>
app.get('/hello', (req, res) => {
    res.render('hello') // 去 render views 底下 hello 這個檔案
})

Ex2. todos.ejs

<h1>Todo</h1>
// 在 EJS 的檔案裡面寫 JS 用 <% %> 包住
<ul>
    <% for(let i=0; i<todos.length; i++) { %>
        <li><%= todos[i] %></li> // "=" 類似 echo
    <% } %>
</ul>
const todos = [
    'first todo', 'second todo', 'third todo'
]
app.get('/todos', (req, res) => {
    res.render('todos', {
        todos: todos
    })
})
  1. 執行後就可以在瀏覽器看到被 render 的檔案內容,如果改了 render 的檔案,不需要重新執行就可以看到修改過的內容

建立模組

  1. 新增資料夾 models
  2. 在 models 新增 js 檔案(todo.js)
  3. 把資料寫入,export Model
    const todos = [
     'first todo', 'second todo', 'third todo'
    ]
    const todoModel = {
     getAll: () => {
         return todos
     },
     get: id => {
         return todos[id]
     }
    }
    module.exports = todoModel
    
  4. 新增一個資料夾(controllers)
  5. 在 controllers 建立一個 js 檔案(todo.js),把 models 的檔案引入,render 內容,export controller
    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
    
  6. 在 index.js 設定網址相對應的內容
    app.get('/todos', todoController.getAll)
    app.get('/todos/:id', todoController.get)
    
  7. 執行後就可以在瀏覽器看到被 render 的檔案內容

補充:

在 sublime 安裝 EJS 的 package

  1. 在 sublime 按 ctrl+shift+p (Win, Linux)、cmd+shift+p (Mac),會跳出一個搜尋框
  2. 輸入 Package Control:Install package,按下去等一下會再跳出搜尋框
  3. 輸入 EJS,按下 EJS 就會自動安裝
  4. 安裝好後就可以在右下角看到 EJS 的選項了

Node.js 與 mySQL 溝通

  1. 安裝 mySQL

    npm install mysql
    
  2. 新增檔案 db.js,建立 db,connection 的資料寫自己的

    var mysql      = require('mysql');
    var connection = mysql.createConnection({
    host     : 'localhost',
    user     : 'root',
    password : 'root',
    database : 'app'
    });
    module.exports = connection
    
  3. 在 index.js 的檔案引入 db

    const db = require('./db')
    
  4. 連線到 db

    app.listen(port, () => {
     db.connect()
     console.log(`Example app listening at http://localhost:${port}`)
    })
    
  5. 建一個 todos 的 tabel(id, content)
  6. 在 models 引入 db,用 callback function 拿資料,再 export 出去
    const todoModel = {
     getAll: (cb) => {
         db.query('SELECT * from todos', (err, results) => {
             if (err) return cb(err);
             cb(null, results)
         });
     },
     get: (id, db) => {
         db.query(
             'SELECT * from todos where id = ?', [id], 
             (err, results) => {
                 if (err) return cb(err);
                 cb(null, results)
             }
         )
     }
    }
    module.exports = todoModel
    
    要等很久的東西,愛用 callback function
  7. 在 controller 把 models 的東西拿進來,然後 render 出來,要做錯誤處理,再 export 出去
    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
    
  8. 在 index.js 設定網址對應的內容
    app.get('/todos', todoController.getAll)
    app.get('/todos/:id', todoController.get)
    
  9. 執行後就可以在瀏覽器看到被 render 的檔案內容

重點:建立 db,連線 db,引入 db

Middleware

request 跟 response 之間的媒介

  • app.use(req, res, next) 整個地方都用 middleware
  • next 代表把控制權交給下個 middleware
    app.use((req, res, next) => {
      console.log('Time', new Date())
      next()
    })
    

body-parser

可以拿到 request body 裡面的資料(post 的東西)
ex. 新增 todo 功能

  1. 安裝 body-parser
    npm install body-parser
    
  2. 引入 body-parser
    var bodyParser = require('body-parser')
    
  3. 在 .use 的設定 body-parser, 解析 body(才能使用 req.body 去拿發過來的東西)
    // parse application/x-www-form-urlencoded
    app.use(bodyParser.urlencoded({ extended: false }))
    // parse application/json
    app.use(bodyParser.json())
    
  4. 使用 post ,新增todo
    app.post('/todos', todoController.newTodo)
    
  5. 在 controller 使用 req.body 去拿 post 的東西
    newTodo: (req, res) => {
     const content = req.body.content // 這是 input 的 name
     todoModel.add(content, (err) => {
       if (err) return console.log(err)
       res.redirect('./todos')
     })
    }
    
  6. 在 model 添加 add 的 function
    add: (content, cb) => {
     db.query(
       'insert into todos(todo) values(?)', [content],
       (err, results) => {
         if (err) return cb(err);
         cb(null)
       }
     )
    }
    
  7. 執行

Express-session

ex. login in 功能

  1. 安裝
    npm install express-session
    
  2. 引用
    const session = require('express-session')
    
  3. 設定 .use()
    app.use(session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: true
    }))
    
  4. 顯示 login 頁面
    app.get('/login', (req, res) => {
     res.render('login')
    })
    
  5. login,用 session.isLogin 判斷密碼正確性
    app.post('/login', (req, res) => {
     if (req.body.password === '123') {
         req.session.isLogin = true
         res.redirect('/')
     } else {
         res.redirect('/login')
     }
    })
    
  6. logout,session.isLogin 改為 false
    app.get('/logout', (req, res) => {
     req.session.isLogin = false
     res.redirect('/')
    })
    

connect-flash

  1. 安裝
    npm install connect-flash
    
  2. 引入,require 進來
  3. 設定 .use()
    app.use(flash())
    
  4. 設定 errorMessage
    req.flash('errorMessage', 'Please input the correct password')
    
  5. 引用 errorMessage
    errorMessage: req.flash('errorMessage')
    

res.local

讓 view 可以用任何來自 local 的東西,適合用在 global 的地方

app.use((req, res, next) => {
  res.locals.isLogin = req.session.isLogin
  res.locals.errorMessage = req.flash('errorMessage')
  next()
})

#Web #Backend #Express







Related Posts

長的帥,連Code都是香的 - Elvis Operator ?:

長的帥,連Code都是香的 - Elvis Operator ?:

每日心得筆記 2020-06-23(二)

每日心得筆記 2020-06-23(二)

Python基礎入門 | 物件和類別 (Objects and Classes)

Python基礎入門 | 物件和類別 (Objects and Classes)


Comments