發表文章

目前顯示的是有「Express Web Framwork」標籤的文章

如何於 Koa 實作長輪詢(Long-polling)機制

io.js 的到來以及即將釋出的 Node.js v0.12,意味著我們不能再忽視 ECMAScript 6 的存在,更代表著 Koa Web Framework 開始要正式進入到市場了(如果你對 Koa 有興趣,可以參考『 舊文 』的簡報內容,有說明完整的開發方法,也有說明 Generator 的使用方法)。Koa 大量運用到 ECMAScript 6 新的 Generator 支援,簡化了非同步的機制,更減少了 Callback 的使用場景。 平心而論,Generator 的機制對許多 JavaScript 開發者確是一個不小的門檻,與舊有的習慣格格不入,需要一點時間學習及熟悉。如果你還不熟悉其機制的細節,不如先學習一些習慣的使用,這是一個比較快能上手的方式。所以,我們可以試著來實作 Long-polling(長輪詢)機制,理解如何運用 Generator 在 Koa 中進行非同步的實作。 若開發 Web 應用涉及了即時的需求,不免碰到 Long-polling(長輪詢)的機制, Long-polling(長輪詢)簡單來說,就是讓伺服器可以即時通知客戶端有資料要進行傳送,常被用在聊天室、即時通知訊息等應用,如 Facebook 的訊息通知機制,就是採用這樣的方式。 Long-polling(長輪詢)的運行原理,就是讓客戶端向伺服器要求新的資料,但取得資料後雙方都不中止連線,等到伺服器有資料要通知客戶端時才將連線中斷,而客戶端就知道有新資料要接收,重新建立一次連線以取得新資料,然後又再次保持連線不中斷,等待下次通知。 暸解了原理,我們就可以開始實作伺服器的部分。首先,假設我們現在有一個事件發送器,每次有資料更新,就會觸發一次事件。如下範例,是每秒鐘更新一次資料: // 建立一個事件發送器 var dispatcher = new events.EventEmitter(); var num = 0; setInterval(function() {     // 將 num 加一,更新資料     num++;     // 通知所有客戶端資料有更新     dispatcher.emit('update'); }, 1000); 如果是 Express,若要實...

Express 4 來了!好用的 Router 機制

還記得前陣子,就在書將出版之際,半路殺出來最新的 Express 4,使得筆者手忙腳亂,緊急通知出版社修改書的內容。將所有使用到 Express 的範例,都特別加上版本號的指定,讓讀者在照著範例做時,能夠使用舊的 Express 3.0,而不會發生任何問題。最新的 Express 4 雖然效能提升不少,也有一些好用的新東西,但和舊版有些微 API 的不相容,此外,也因為是新東西,可能很多東西的支援會發生問題,如果舊版本在上線的產品中運行相當穩定,建議先不要急著升級。 Express 4 做了一些改進,有不少新東西,其中相當值得提的一項就是路由機制(Router)的翻新。如果你以前在使用 Node.js + Express 開發網站服務時,因為 Router 很難管理而痛苦不已,這將是一個你會愛上的功能改進。 傳統的使用方法 傳統的 Router 設定方法依然可以運作,我們可以放心使用。 var express = require('express'); // Create express application var app = express(); // Apply this router on (/apis/user) app.get('/', function(req, res) {     // Home }); app.listen(8000); 新 Router 物件的使用方法範例 除了舊的方法之外,你可以用 Router() 去建立一個 Express 的 Router 物件,然後管理你一系列的路由規則。 var express = require('express'); // Create express application var app = express(); // Create a router to handle routes for a set of  user API var userAPI = express.Router(); // Sign in (/apis/user/signin) userAPI.post('/signin', function(req, res) {     // ... }); // Sign out (...

不一樣的 Node.js!終於完成的新書前來報到!

圖片
這陣子,最怕的就是有人問:『書什麼時候要發行?』,參加社群聚會或活動時,有人會問我,平常周圍朋友也會問我,工作上隊友和客戶都會問我,只有一些親人根本不知道不知道我在寫書,所以不會問我。如果你問我,寫書這件事上,你最怕的是什麼?我會跟你說,出版社截稿日期的死線(Deadline)並不是最可怕的,最可怕的是,它是一種詛咒,讓周圍人都會特別關心你的一種魔法。而這個關心的壓力,讓人喘不過氣來。 還好,書總算從初稿、校稿到排版完成,直到出版社編輯傳來最後的封面設計,才讓人鬆了一口氣。可以告訴大家,書總算要真正出來了! 事實上,國內 Node.js 的書其實並不多,其中幾乎多為翻譯書,或是從簡體直接翻成繁體中文的書。這讓這幾年一直在到處推廣 Node.js 的我,相當困擾。每當有人問起 Node.js 有什麼書可以看時,我總是無法推薦出很多本給大家參考。所以,當出版社的編輯跟我說,希望寫一本 Node.js 的中文書時,我其實相當開心。 還記得當時接下這個任務後,我便開始思考和回想,在推廣和使用 Node.js 時,所遭遇到的困難是什麼?我能否藉這本書來幫助讀者排除這些困難?甚至給讀者一個更大的視野,一個 Node.js 可以發揮在各種領域的可能性。 此外,網路時代來臨後,有太多的新資料都可以從網路上取得,這本書要提供什麼內容,更是需要被探討的問題。尤其是 Node.js 這樣版本號快速上升的新技術,可能書還沒寫完,內容就已經過時。在種種思索下,期望本書的定位,將帶給大家的不是絕對的技術新知,而是一本學習 Node.js 的參考書和技術指南,讓想入門的讀者可以學會怎麼使用 Node.js,讓已經懂的人可以知道 Node.js 還能做些什麼不一樣的事,更進一步知道從哪裡去得到更多 Node.js 的新知。如果這本書在幾年後,仍有六成的資料值得參考,那就算完美達成我賦予它的任務了。 從引領初學者的角度來看,並參考了一些市上的 Node.js 書籍,也從過去教育訓練和推廣的場合發現一個現象。Node.js 不是什麼很難的新東西,如果有人已經很熟悉 JavaScript 語言,而且能寫的很好,Node.js 根本是小菜一碟。所以,多半不得其門而入的人,是對 JavaScript 不這麼熟悉,或是想使用 Node.js 技術進行第一次接觸程式開發的人。只不過,...

一起來用 Courser 吧!管理 Route 的利器!

跳脫出前端應用之後的 JavaScript 語言,所遭遇的挑戰極大,至今還沒有一個非常好的開發模型和原則,讓人不免抱怨。因為沒有前人可學習,也沒有太多文獻可以參考,所以,為了讓自己能夠更快完全領會 JavaScript 在各種極端環境之下的開發情況,在這一年來,花了相當多的時間投入去做各種不同類型的 Project,從 Web service、Embedded System、System Programming 到各式雲端技術開發,嘗試從中累積更多用 JavaScript 打天下的經驗。唉,慣 C 都不慣 C 了。 不過,做了許多東西後,就會開始有機會重覆開發相同的功能,這時就會希望能重覆利用舊的程式碼,讓同一段程式,不用每次都重寫一次而浪費時間。於是,開始慢慢把之前做過的專案整理,然後整併許多功能,獨立出來變成一個個 Node.js 模組。 Courser 就是這樣被獨立出來的專案,而功能是為 Express Web Framework 提供一個管理 HTTP Routing 的方案。你可以從 github 上找到專案首頁: https://github.com/cfsghost/courser Express 範例的傳統做法,是每次增加一個 API 或頁面,就要更動一次主程式檔案(app.js),以管理 HTTP Routing,相當麻煩,尤其當有許多人協同開發時,更多不必要的程式碼衝突便出現了。此外,管理專案中的 Handler 也是件麻煩事,因為我們總不可能將所有的 Handler 寫在同一支檔案裡,必須適當分類分檔,才能便於維護。隨著專案規模越來越大,需求越來越複雜,這便是件苦差事。 這些問題當然有解決方案,Express Source Code 裡的範例,就提供了幾種不同的方法,供開發者參考,只不過,這些方法大部份很簡陋、不通用,多半還是要靠專案開發者自行想辦法,開發一套機制,才能解決需求。因此,之前為了專案的需要,就實作了 Courser 去管理專案下的 HTTP Routing 和所有的 Handler,嘗試讓 routes 不要每次開發出來,都是這麼醜。 Courser 目前已經釋出 v0.0.2,你可以從 NPM 上直接安裝它: npm install courser 假設你已經會使用 Express Web ...

用 Node.js 實作多個 Process 監聽並處理同一個 Port

Node.js 實作了一個內建的模組『cluster』,目的在於提供開發者一個容易的方式,針對多核心機器進行支援。可惜的是,截至今天 Node.js v0.6.14 版本,『cluster』功能還是不夠完整,從官方文件上可以得知,其 API 仍處於實驗(Experimental)階段,在日後會有極大的變動(Drastic changes in future versions)。 而就 git repository 上最新的 Node.js 開發版本來看,『cluster』模組已經大幅度更改,也從簡單的 function call 變成了物件的設計。所以,目前非常不建議在自家產品中大量使用 『cluster』,至少短期內,他都不是穩定可用的,會有很大的改動空間。 不過,你如果是要實作 Web Service,會想要使用『cluster』的目的就顯而易見:『透過多個 Process 分擔流量和工作量』。若只是單單要做到這一點,我們可以自己來實作。 通常我們有兩種方式達成這個需求: 反向代理(Reverse Proxy) 技術 使用多個程序(Process) 監聽並處理同一個 Port 前者的做法,是建立一個常駐程式或機器,統一處理所有連線的需求,然後將這些連線做流量平衡(Load Balance),轉送並分配給後端多個程序或機器處理,待處理完成後,再將後端的回應送回給使用者。這樣的 Proxy 機制,你可以用已經相當成熟的解決方案達成,像是 Nginx 或 Apache。亦或是自己使用 Node.js 撰寫,但由於這不是本文的重點,日後再來討論這個實作的細節。 而後者,使用多個程序(Process) 監聽並處理同一個 Port,是本文的主題,也是『cluster』模組正在做的事。如果你有看過『cluster』的程式碼,就會發現它其實主要運用了兩個技術:『取得 net._handle』和『利用 process.send() 傳送 Handle 給子行程(child process)』。 知道了關鍵後,實作上就不是什麼大問題,主要流程是: 運用『net』建立一個 Server,目的在於監聽(listen) 80 Port 建立多個子行程(child process) 準備處理連線 將『net』的內建 Handle 傳給子行...

使用 NodeJS + Express 從 GET/POST Request 取值

過去無論哪一種網站應用程式的開發語言,初學者教學中第一次會提到的起手式,八九不離十就是 GET/POST Request 的取值。但是,在 Node.js + Express 的世界中,彷彿人人是高手,天生就會使用,從不曾看到有人撰文說明。 這應該算是開發 Web Service 的入門,在 Client 與 Server 的互動中,瀏覽器發出 GET/POST Request 時會傳值給 Server-side,常見應用就是網頁上以 POST method 送出的表單內容,或是網址列上的 Query Strings (ex: page?page=3&id=5)。然後,我們的網站應用程式透過解析這些參數,得到使用者上傳的資訊。 取得 GET Request 的 Query Strings: GET /test?name=fred&tel=0926xxx572 app.get('/test', function(req, res) {     console.log(req.query.name);     console.log(req.query.tel); }); 如果是透過表單且是用 POST method: <form action='/test' method='post'> <input type='text' name='name' value='fred'> <input type='text' name='tel' value='0926xxx572'> <input type='submit' value='Submit'> </form> app.post('/test', function(req, res) {     console.log(req.body.name);     console.log(req.body.tel); }); 當然也可以 Query Strings 和 POST method 的表單同時使用: <form action=...

NodeJS + Express + i18next 支援多國語系吧!

除非是區域性的網站服務,不然在這個網路通全世界的時代,開發網站服務就一定有多國語系的需求。一般來說,i18n 的支援都是由 Web Framework 所提供,但 Express 並沒有支援,所以我們要借助 i18next 這個模組。i18next 可以和 Express 以及 template 很完美的結合,最重要的是,有客戶端支援(clientside support),若搭配 jquery,我們也可以使前端的 JavaScript 支援多國語系。 先使用 NPM 安裝 i18next: npm install i18next 在 express 的應用程式(app.js)中引入使用 i18next: var express = require('express'); var i18n = require('i18next'); var app = module.exports = express.createServer(); /* Initializing i18n */ i18n.init(); i18n.registerAppHelper(app); app.configure(function(){ app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); app.use(express.bodyParser()); app.use(i18n.handle); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(__dirname + '/public')); }); app.get('/', function(req, res) { res.render('index'); }); app.listen(3000); 接著要在應用程式的目錄下,建立預設的翻譯檔和存放路徑(locales/dev/translation.json): { "exam...

如何在 Debian 建置 NodeJS + Express 環境

截至目前為止,只有 Debian Sid(unstable) 提供 Nodejs 和 Express 套件可讓使用者直接安裝。但是,在 Server 的環境之下,一般都使用 Debian 5.0/6.0 (Lenny/Squeeze) 穩定版的系統,因此沒有套件可以直接安裝,唯一的方法就是自己下載編譯 Nodejs。還好自己手動安裝的過程並不困難,幾個步驟就可以完成。 安裝 Nodejs 更新系統並安裝編譯 Nodejs 所需的套件: sudo apt-get update sudo apt-get install git-core curl build-essential openssl libssl-dev 從 Git Repository 下載 Nodejs 原始碼: git clone https://github.com/joyent/node.git 進入 Nodejs 原始碼目錄,並切選擇我們要的版本(截至本文,0.6.6 是最新版): cd node git checkout v0.6.6 # Note: 可以使用 git tag 看到 Nodejs 所有的版本列表 編譯並安裝 Nodejs (預設會裝到 /usr/local/lib/node): ./configure make sudo make install 如果安裝過程中沒有任何問題,就可以使用 node 指令查看 Nodejs 的版本了: node -v 手動設定公用的 Nodejs Module 路徑: echo "NODE_PATH=/usr/local/lib/node_modules" >> .bashrc # Note: 如果想讓所有 Server 上的 user 都套用設定,可以放在 /etc/profile 安裝 Express Web Framework 使用 npm 安裝 express(使用 -g 選項會安裝到公用的目錄 /usr/local/lib/node_modules): sudo npm install express -g Express 通常預設使用 jade template engine,也需要手動安裝: sudo npm install jade -g 安裝完...

Nodejs + Express 的 Route 流程規劃

如大多數人的習慣,往往我們都把 Route 的路徑都定義在 app.js 當中。Express 所提供的眾多範例程式中,雖然有將 Route 抽出來放在 routes 目錄下的實作方式,但也太簡單,只支援一層的設計。這意味著,如果 routes 之下有更多子目錄,放著更多的 route 設定,都是不會被讀取的。 這邊做了簡單的修改,利用遞迴的做法,掃描所有的子目錄,然後去載入所有的 js 檔案以擴充 Route 的設定。 以下是 route.js 的程式碼: var vm = require('vm'); var fs = require('fs'); module.exports = function(app) { var dir = __dirname + '/routes'; var context = { app: app, require: require }; var newContext; /* Initializing Context */ for (var key in global) context[key] = global[key]; newContext = vm.createContext(context); /* Loading all routes */ loadRouteDir(newContext, dir); }; function loadRouteDir(context, path) { /* Scanning files */ fs.readdirSync(path).forEach(function(file) { loadRouteFile(context, path, file); }); } function loadRouteFile(context, path, file) { var fullpath = path + '/' + file; fs.stat(fullpath, function(err, stats) { if (stats.isFile()) { /* Loading route */ ...

NodeJS + Express 實作檔案上傳

『 Express Web Framework 』是基於『Connect Middleware Framework』所開發,大部份的常見功能,藉由 Connect 本身支援或 Connect 的第三方(Third-party)模組,就可以實作出來。如果要實作檔案上傳的功能,可以使用第三方模組『 connect-form 』來達成。 直接透過 npm 安裝需要的模組: npm install connect-form 在 app.js 中實作: var express = require('express'); var form = require('connect-form'); var app = module.exports = express.createServer(); app.configure(function(){ app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(form({ keepExtensions: true, uploadDir: __dirname + '/uploads' })); app.use(app.router); app.use(express.static(__dirname + '/public')); }); app.get('/', function(req, res) { res.send('<form method="post" enctype="multipart/form-data">' + '<p>Image: <input type="file" name="image" /></p>'...

在 NodeJS + Express 使用 Cookie-based Session

開發網站的人應該對 Session 都不陌生,主要是用於 Client 與 Server 之間的溝通和狀態記錄。底層的實作細節本文就不多說明,若仍不理解他的用途,只要知道它可以讓你在不同頁面之間傳遞資料,像是登入狀態等。 一般標準的『Express』利用『Connect Middleware Framework』(之後簡稱 Connect)中的模組來實作 Session 的,而預設的方法是使用 MemoryStore 的方式,也就是將 Session 資料存放於記憶體上。若你開發的是中大型的網站,也可以使用 Connect 的第三方模組『 connect-redis 』,利用『 Redis 』這樣的 Key-Value Database 來存放 Session 的資料。這些在 NodeJS + Express 的各類開發文獻中相當常見,你可以從許多網站上找到使用方法。 不過,也許你仍不滿足,希望使用近年來興起的 Cookie-based Session,Connect 也有第三方模組『 cookie-sessions 』可以使用。 可以直接使用 npm 安裝: npm install cookie-sessions 在你的 app.js 這樣使用: var express = require('express'); var CookieStore = require('cookie-sessions'); var app = module.exports = express.createServer(); app.configure(function(){ app.set('views', __dirname + '/views'); app.set('view engine', 'jade'); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(express.cookieParser()); app.use(CookieStore({ secret: 'FredChien' })); app.use(app.rout...

使用 NodeJS + Express + Socket.IO 實作 WebSocket 服務

一直處於假標準狀態的 HTML5,過程中一直有變化,讓人無法放心使用。不過到了今年,HTML5 總算進入了準決賽的階段,大部份功能已經確定下來,其中 WebSocket 就是最讓人關切的項目之一,因為這意味著未來 Web 不再只是單次性的觸發服務,而是可以讓使用者端與伺服器長時間處於連線狀態,並進行即時性的資料傳交換。 關於 WebSocket,彷間許多隨 HTML5 起舞的人,其實都說明不夠清楚,多半只強調能達成 TCP Socket 形式的連線,和無所不能的優點。其實,WebSocket 並非是我們所知的原生 Socket ,而只是一種架構於 HTTP Protocol 上的延伸定義,所以換句話來說,不能直接拿來連線到非 HTTP 以外的通訊協定。 至於它的做法,就是在瀏覽器向伺服器要資料時,在 Request Header 裡加上『Connection:Upgrade』來告訴伺服器:『我要改變連線形態』。然後伺服器會依照瀏覽器給的 WebSocket 相關標頭(header)和交握用的金鑰(Key),進行認證和改變處理方式。進入 WebSocket 連線的客戶端,就有如在使用 TCP Socket 或 Telnet 一樣,可以隨意在任意時間點傳送資料給伺服器處理,伺服器也可以在這連線下即時回應。所以就某方面來說,這剛好可以含蓋並取代過去 Web 1.0 的 Refresh Page 和 Web 2.0 後的 Long-polling 技術。 寫過網路程式的人都知道,如何定義 Protocol 才是傷腦筋的事,開啟了一個『類 Socket』意味著我們又回到了過去的年代,要自己解決 Protocol 的定義。不過也已經有現成的 Library 可以使用,許多問題我們已經不用擔心,本文將提到的 Socket.IO 就是其中之一。此外,由於 WebSocket 需要 Client/Server 同時實作,所以,這些 Library 多半都包括了兩部份的實作。 Server 端,使用 NodeJS + Express web framework + Socket.IO(0.8.6): /* Module dependencies */ var express = require('express'); var io = require(...