用 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
- 在 CLI 上面跑檔案,讓 server 跑起來
- 在瀏覽器上輸入 localhost:5001
- CLI 會顯示 favicon.icon,是 tag 上的小圖示
- 用網址內容決定輸出的內容是甚麼
- 設定 header,Content-type 可以告訴瀏覽器該怎麼解析
Express
安裝
- 建立一個 express-demo 的資料夾
- npm init,會出現一堆敘述,直接按 enter 按到底,init 完後會出現一個 package.json 的資料夾
- npm install express
基本操作
- 新建一個 index.js 的檔案
進入 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}`) })
儲存,在 cli 輸入 node index.js,當看到 Example app listening at http://localhost:5001 代表有跑起來
- 在瀏覽器輸入 localhost:5001/要帶的網址,就可看到相應的內容
Apache + php 跟 Express 的差別
Apache + php
- Apache 接收 resquest, Apache 掛了就都掛了
- 瀏覽器 <-> Apache server <-> php(處理器)
- 由 php 處理完再回傳回去
- 侷限在檔案系統,檔案結構(路徑)長怎樣,url 就是怎樣
Express
- 瀏覽器 <-> Express server
- 所有東西都由 Express 處理再回傳回去
- 路由系統可以自行決定甚麼樣的網址要回傳甚麼樣的資料
基本架構
- 安裝 ejs (跟 php 有點像的東西)
npm install ejs
- 新增資料夾 views,template 檔案的預設位置
- 新增一個 ejs 的檔案存到 views 裡面
- 在 index.js 檔案設定 template engine
app.set('view engine', 'ejs') // 設定 ejs 為 template engine
- 在 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
})
})
- 執行後就可以在瀏覽器看到被 render 的檔案內容,如果改了 render 的檔案,不需要重新執行就可以看到修改過的內容
建立模組
- 新增資料夾 models
- 在 models 新增 js 檔案(todo.js)
- 把資料寫入,export Model
const todos = [ 'first todo', 'second todo', 'third todo' ] const todoModel = { getAll: () => { return todos }, get: id => { return todos[id] } } module.exports = todoModel
- 新增一個資料夾(controllers)
- 在 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
- 在 index.js 設定網址相對應的內容
app.get('/todos', todoController.getAll) app.get('/todos/:id', todoController.get)
- 執行後就可以在瀏覽器看到被 render 的檔案內容
補充:
在 sublime 安裝 EJS 的 package
- 在 sublime 按 ctrl+shift+p (Win, Linux)、cmd+shift+p (Mac),會跳出一個搜尋框
- 輸入 Package Control:Install package,按下去等一下會再跳出搜尋框
- 輸入 EJS,按下 EJS 就會自動安裝
- 安裝好後就可以在右下角看到 EJS 的選項了
Node.js 與 mySQL 溝通
安裝 mySQL
npm install mysql
新增檔案 db.js,建立 db,connection 的資料寫自己的
var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : 'root', database : 'app' }); module.exports = connection
在 index.js 的檔案引入 db
const db = require('./db')
連線到 db
app.listen(port, () => { db.connect() console.log(`Example app listening at http://localhost:${port}`) })
- 建一個 todos 的 tabel(id, content)
- 在 models 引入 db,用 callback function 拿資料,再 export 出去
要等很久的東西,愛用 callback functionconst 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
- 在 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
- 在 index.js 設定網址對應的內容
app.get('/todos', todoController.getAll) app.get('/todos/:id', todoController.get)
- 執行後就可以在瀏覽器看到被 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 功能
- 安裝 body-parser
npm install body-parser
- 引入 body-parser
var bodyParser = require('body-parser')
- 在 .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())
- 使用 post ,新增todo
app.post('/todos', todoController.newTodo)
- 在 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') }) }
- 在 model 添加 add 的 function
add: (content, cb) => { db.query( 'insert into todos(todo) values(?)', [content], (err, results) => { if (err) return cb(err); cb(null) } ) }
- 執行
Express-session
ex. login in 功能
- 安裝
npm install express-session
- 引用
const session = require('express-session')
- 設定 .use()
app.use(session({ secret: 'keyboard cat', resave: false, saveUninitialized: true }))
- 顯示 login 頁面
app.get('/login', (req, res) => { res.render('login') })
- login,用 session.isLogin 判斷密碼正確性
app.post('/login', (req, res) => { if (req.body.password === '123') { req.session.isLogin = true res.redirect('/') } else { res.redirect('/login') } })
- logout,session.isLogin 改為 false
app.get('/logout', (req, res) => { req.session.isLogin = false res.redirect('/') })
connect-flash
- 安裝
npm install connect-flash
- 引入,require 進來
- 設定 .use()
app.use(flash())
- 設定 errorMessage
req.flash('errorMessage', 'Please input the correct password')
- 引用 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()
})