2012年8月21日 星期二

在 Node.js 中的 MVC 架構 - Express

這篇文章是在探討 Node.js 中如何實現架構程式的方法 MVC 中的 Control 和 View,Control 指的就是 Node.js 相關的 Javscript code 而 View 指的就是一般 Browser 可以讀取的 HTML+CSS+Javascript。而這篇的主角是 Express。
首先要安裝。參考過前一篇 Node.js 學習資源 之後應該就會有 NPM 這個工具了。安裝 express 的方式很簡單就是以下指令
npm install -g express
安裝完之後可以查詢目前 express 的版本,用如下的指令
express -V
讀者將會在 Terminal 看到
3.0.0rc2
目前的版本是 3.0 和 2.0 有比較大的差別。之後文章有用到會提一下差別之處。
來寫個簡單的 GET 和 POST 把

首先是 GET

var express = require("express");
var app = express();
app.get('/', function(req, res) {
    res.send("Hello Express Server");
    res.end();
});

app.listen(8800);
// in expressServer.js
之後用  node expressServer.js 然後在 Browser 輸入
http://127.0.0.1:8800/
就會看到
Hello Express Server
我們來分析一下這個程式,一開始我們需要 express 所以有 require 這個很合理,然後 express() 取得一個 Server 的實體,給 app 這個變數。接下來是重點,
app.get('/', function(req, res) {
    res.send("Hello Express Server");
    res.end();
});
app.get("/", "nameOfFunction");  這個 function 就是用來處理 GET method 發生的事情,第一個參數是 "/" 也就是說,當 client 存取這個路徑的時候,會用第二個參數,某個 function 來處理。寫到這理 express 很簡單的就處理兩件事了,
  1. GET 
  2. 路徑
我們先來客制化一下路徑,比如說改成 /index 這樣 browser 輸入 
http://127.0.0.1:8800/index 
才會有結果,對 Server 來說才是一個可以處理的路徑。
接著我們來看一下第二個參數 function 做了什麼事情?
function(req, res) {
    res.send("Hello Express Server");
    res.end();
}
這個 function 有兩個參數,一個叫 req,一個叫 res 其實就是 request 和 response 的縮寫,client 給 server 的代理用 req ,相對的 server 給  client 的就用 res 這個變數,在這裡我們很簡單地用
res.send("Hello Express Server");
也就是 server 要傳給  client 一個字串,其實也可以加上HTML Tag 比如改成
res.send("<h1>Hello Express Server</h1>");
就會在 Browser 看到具有 Tag 效果的字串,如下圖
最後一行是寫了
res.end();
這個很重要,因為 client 會一直等待 server 的回應結束才會認為此次 request 結束了,如果不寫讀者將會看到 browser 一直在等待資料的樣子。
對我們對 GET 的了解,Browser 可以傳一些資料透過 GET 給  Server 就直接加在 link 後面比如說。
http://127.0.0.1:8800/send?name=michael&age=30
這樣一來怎麼從 express 把 name 和 age 這兩個 key 的值讀取出來?筆者來寫一段 server 端的對應程式。如下
app.get('/send', function(req, res) {
    var message = "<h1>Welcome to Express Server</h1>";
    var name = req.query.name;
    var age = req.query.age;
    if (name) {
        message = message +"<p> Hello "+name+"</p>";
    }
    if (age) {
        message = message +"<p> You are "+age+" years old.</p>";
    }
    res.send(message);
    res.end();
}); 
一開始新增一個路徑為 /send 然後在處理 request 和 response 的 function 裡加上一個很重要的 property 為。
req.query
在這個例子是把由 req.query.name 把 name 的值取出來還有 req.query.age 把 age 的值取出來。然後再用 req.send 把相對應的處理傳給 client。中間有兩個 if 來檢查 client 是不是真的有傳值到 server。這樣就是可處理各式的 GET Request 了。

再來是 POST

HTTP POST Method 最簡單的方式是用 HTML form 來和 server 溝通。可以參考以下程式碼。
<form action="http://localhost:8800/formData" method="post">
    <input value="Username" name="username"/>
    <input type="password" value="Password" name="password"/>
    <input type="submit" value="login"/>
</form>
對於 form 來說要透過 POST 要傳資料給 server,要設定 method="post",對於文字資料而言,傳給 server 的方法是在form 裡的 input 中,value,和 name 這兩個 attribute 的值,所以上面這個 from 要傳給 server 的資料就是,左邊是 key 右邊是 value
username : Username
password : Password
相對於 GET 的寫法就是
http://localhost:8800/formData?username=Username&password=Password
只是 POST 要透過 from 來傳送給 server。
有了這個 POST Client 端的 HTML 那 server 要寫什麼程式對應呢?如下
app.post('/formData', function(req, res) {
    res.send("<h1>Hello "+ req.query.username+"</h1>");
    res.end();
});
// 注意這個是錯誤的寫法
你會發現在按下 form 的 submit 之後,會在Browser 看到
對於 req.query.username 的寫法是給 GET 用的,POST 的情況下要用其他的方式,server 要改寫如下
app.use(express.bodyParser());
app.post('/formData', function(req, res) {
    res.send("<h1>Hello "+ req.body.username+"</h1>");
    res.end();
});

首先要用 bodyParser() 讓 express 可以來解析 post 傳來的資料,再來就是在處理 request 和 response 的 function 裡用 req.body 這個例子是用
req.body.username
簡單想就是把 GET 用的 query 改成 body 就是了。也可以直接存取 key。再用 browser 執行一次 html 的 form 按下 submit 就會看到如下
到目前為,Express 處理 GET 或 POST 的 request 只要短短幾行 code 就可以了,再次體會到 Node.js 的強大。

Render HTML - Jade

最後一個部分要介紹的就是用 Express 和 Jade 來方便產生 HTML 格式給使用者看。
介紹一下 Jade 這個好用的工具。首先來安裝
npm install -g jade
馬上來看一個例子。如果筆者想要有一個這樣的 HTML 畫面。
<html>
    <head>
        <title>Jade Demo</title>
    </head>
    <body>
        <h1>Hello Michael</h1>
    </body>
</html>
不過,<title>  和 <h1>裡面的文字是依照不同的使用者和不同情況下去改寫的。上的 HTML 可以用 Jade 改寫成
html
    head
        title #{title}
    body
        h1 Hello #{username}
// 檔名為 hello.jade
我用粗體字標示下來的是會被換成 HTML 的 Tag 的部分,而 #{} 這樣的格式是指執行的時候會由 express 指定值的設定。先把 hello.jade 放到 expressServer.js 同一個目錄底下的 views (如果沒有就新建一個)。然後新增下面的程式碼到 expressServer.js
app.set('view engine', 'jade');
app.set('views', __dirname+"/views" );

app.get('/hello', function(req, res) {
    res.render("hello", {title:"Jade Demo", username:"Michael"});
    res.end();
});
我們一一來看解說一下。
 app.set('view engine', 'jade');
指得是要用 jade 當成 view 的 engine 也就是要把 jade 格式來表示 html,還有其他的比如
ejs 詳情可以參考 express 官網

app.set('views', __dirname+"/views" );
這個設定是說 server 要去那找 jade 檔案?這邊的設定是 __dirname (注意是兩個下底線) 目前的目錄,再加上 "/views" 也就是底下的 views 資料夾裡。
然後在 app.get 這個我們熟悉的 function 裡用
res.render("hello", {title:"Jade Demo", username:"Michael"});
來把 hello.jade (第一個參數) 檔轉成 html然後把  
{title:"Jade Demo", username:"Michael"}
送給 hello.jade ,也就是把 #{title} 換成 Jade Demo,#{username} 換成 Michael. 這樣一來就完成了,執行 node expressServer.js
然後在 browser 輸入
http://127.0.0.1:8800/hello
就會看到
title 和 h1都是我們從 express 指定過去的值。也可以用 firebug 之類看到 html 為
<html>
    <head>
        <title>Jade Demo</title>
    </head>
    <body>
        <h1>Hello Michael</h1>
    </body>
</html>
也許可能會有讀者問,怎麼在 jade 檔中引入寫好的 css 或是 javascript ?可能會有讀者想要加入一些 framework 如 twitter bootstrap 之類的。請先準備好一個 css 和 一個 javascript 檔。

hello.css

h1 {
    background-color: yellow;
}

hello.js

function sayHello(){
    alert("Hello User");
}
然後這個 project 的目錄是這樣結構的,
在 expressServer.js 除了放 jade 的 views 之外,還要再新增一個 public 資料夾。我們把 hello.js 放到 public/js 之下把  hello.css 放到 public/style 之下。這樣一來材料就準備齊全了。
回到 hello.jade 要新增如下
html
    head
        title #{title}
        link(rel='stylesheet', href='/style/hello.css')
        script(type="text/javascript", src="/js/hello.js")
    body
        h1 Hello #{username}
        #content(onclick="sayHello();") click
由 link 和 script 來導入 hello.css 和 hello.js。最後要在 expressServer.js 新增一個 static 的路徑設定。
app.use(express.static(__dirname + '/public'));
這個是和系統說 expressServer.js 這個目錄底下的 public 是 browser 也看得懂的靜態路徑。這樣一來就行了。說明一下靜態路徑的特色是可以從 browser 輸入路徑找到這個目錄底下的東西。比如
http://127.0.0.1:8800/style/hello.css
這樣 browser 可以看到我們的 css
h1 {
    background-color: yellow;
}
在這個 Project 底下雖然 views 也是個目錄,但是無法從 browser 看到其內容物。完成之後在 browser 試試 css 和 javascript 有沒有作用
 h1 會變成黃色,用mouse click一下 click 字樣就會跳出 alert 視窗。這樣就對了。
Jade 還有很多好玩的功能,書本上只要有提到 express 的都會介紹,包含 Node.js 學習資源
提到的兩本書都有。本文章的範例筆者上傳到 GitHub 了。希望有幫助到想學 Node.js 的朋友,咱們下次見。

3 則留言:

  1. 你好
    我照著你的方法做
    但是在post的那一段
    req.body 為 undefined

    請問是我哪裡做錯了嗎?
    可否幫我看一下呢?
    https://github.com/WhiteFur/express_practice

    感謝您

    回覆刪除
    回覆
    1. app.use(express.bodyParser()) 停用了
      改成這樣
      var bodyParser = require('body-parser')
      app.use(bodyParser.urlencoded({
      extended: false
      }))
      app.get('/formData', function (req, res) {
      res.sendfile('你的html')
      })

      刪除