# Server Side Frameworks

# [Lecture] Introduction to Express

# Framework

在使用 Express 之前,先來談談什麼叫做 框架(Framework)。框架和函數庫一樣,通常是第三方所撰寫好的,但之間最大的差異性在於有沒有 控制反轉(Inversion Of Control) 的性質,框架同時具備約束性與支撐性,在有限制的條件下提供開發者在更短時間內開發出品質更佳的產品。對於框架的定義可以參考以下文章:

# Express.js

網頁框架比比皆是,比如:

  • Python 框架 Flask, Django, Tornado...
  • Ruby 框架 Rails...
  • ...

這門課使用 Express.js 來進行教學,是一個輕量級,基於 Node.js 的開源框架,用以建置網頁應用。輕量級並不代表他所實作的功能較少,而是指相對於 Rails 這類重量級框架而言,許多被掩蓋在框架背後的細節不會被掩蓋掉,這能夠在學習時更了解後端的運作。

# [Lecture] Note about Our First Express App

下一個單元中講師約在影片 13:38 處重啟伺服器,關閉伺服器時是在鍵盤上按下 Ctrl + C。如果想要知道怎麼樣自動地執行這樣的過程,可以觀看助教 Ian 所錄製的影片 YouTube | Automate Node Server Restart with Nodemon

# [Lecture] Our First Express App

首先創建我們的專案資料夾與檔案:

$ mkdir FirstExpressApp
$ cd FirstExpressApp
$ touch app.js
$ npm install express --save

app.js 中撰寫簡單的代碼:

var express = require("express");
var app = express();

//=============================
// Router
//=============================

// "/" => "Hi there!"
app.get("/", function(req, res) {
  res.send("Hi there!");
});

// "/bye" => "Goodbye!"
app.get("/bye", function(req, res) {
  res.send("Goodbye");
});

// "/dog" => "MEOW!"
app.get("/dog", function(req, res) {
  console.log("SOMEONE MADE A REQUEST TO /dog")
  res.send("MEOW");
});

//=============================
// Server
//=============================

// Tell Express to Listen for requests (start server)
app.listen(process.env.PORT, process.env.IP, function() {
  console.log("Server Started!");
});

上述的代碼將會建立一個伺服器,監聽指定的 埠(port),並響應 路由(route) 中的需求。路由是指判斷應用程式如何回應用戶端對特定端點的要求,其中包含了 URI 與一個特定的 HTTP 要求方法(如:GETPOST…等)。關於這一部分的內容可以參考以下文件:

值得一提的是在本機端上,伺服器監聽部分應該是這樣寫的:

//=============================
// Server
//=============================

// Tell Express to Listen for requests (start server)
app.listen(3000, function() {
  console.log("Server Started!");
});

差別在於對於 Cloud9 而言,僅開放了系統所定義的 埠(port) 及 IP 位置,此處必須透過環境變數 process.env.PORTprocess.env.IP 傳入。

# [Lecture] The Package.json

package.json 是一個符合 CommonJS 規定 用來描述套件包的文件,使用 json 格式表示,在其中可以定義 相依(dependency) 的相關套件以及應用程式資訊,以便我們管理專案所會使用到的套件及其對應版本。透過以下命令可以初始化 package.json 檔案:

$ npm init

在安裝套件時,在後方加上 --save 參數可以將其加到專案的相依套件中,也就是在 package.json 中加上對應的內容,比如:

$ npm install express --save

相關的補充可以查看:

# [Lecture] How to automate server restart

在前面的單元我們知道可以使用 node app.js 命令來啟動伺服器,在助教提供的 YouTube | Automate Node Server Restart with Nodemon 影片中介紹了 nodemon 工具來自動地重新載入更動的檔案並啟動伺服器。

# Install nodemon with npm
$ npm install -g nodemon

# Run server with nodemon
$ nodemon

# [Lecture] Route Params

# The * Route Matcher

承接上一個課程的路由概念,當使用者對那些沒有設置路由的位置發送 GET 請求時,伺服器端將回應 Cannot GET /URL,我們可以透過 * 對這些位置進行通配,回應相同的結果:

app.get("*", function(req, res) {
  res.send("There is no ROUTE!");
});

# Route Order

值得注意的是,路由撰寫時擺放的位置是會影響伺服器處理的順序的,比方說將 * 通配擺放在其他路由之前,那麼伺服器在處理時都會先受到 * 通配路由影響,其餘的都不會處理了。

# Route Pamras

除此之外,我們經常需要將某種模式所匹配到所有路由都對應到同一個組件,比如針對不同的 ID 的用戶來說,路徑可能是 ./user/<id>,在這裡可以透過 路由參數(route parameter) 來標記,比如:

app.get("/post/:topic/comments/:id/:title/", function(req, res) {
  console.log(req.params);
  res.send("There is a Route Pamras Page!");
});

傳入時的路由參數將被保存在 req.parems 當中。

# [Lecture] Express Basics Exercise

# Demand

  1. Create a brand new Express app from scratch.
  2. Create a package.json using npm init and add express as a dependency.
  3. In your main app.js file, add 3 different routes:
    • /
    • /speak/:animal
    • /repeat/:message/:times

# Example

Visiting "/" should print "Hi there, welcome to my assignment!"

Visiting "/speak/pig" should print "The pig says 'Qink'"
Visiting "/speak/cow" should print "The cow says 'Moo'"
Visiting "/speak/dog" should print "The cow says 'Woof Woof!'"

Visiting "/repeat/hello/3" should print "hello hello hello"
Visiting "/repeat/hello/5" should print "hello hello hello hello hello"
Visiting "/repeat/blah/2" should print "blah blah"

Visiting any other route, print "Sorry, page not found... What are you doing with your life?"

# [Lecture] Express Basics Exercise: SOLUTION

首先創建我們的專案資料夾與檔案:

$ mkdir ExpressBasic
$ cd ExpressBasic
$ touch app.js
$ npm init
$ npm install express --save

app.js 中完成代碼:

var express = require("express");
var app = express();

//=============================
// Router
//=============================

// "/"
app.get("/", function(req, res) {
  res.send("Hi there, welcome to my assignment!");
});

// "/speak/:animal"
app.get("/speak/:animal", function(req, res) {
  var sounds = {
    pig: "Qink",
    cow: "Moo",
    dog: "Wolf Wolf!",
    cat: "Meow",
    goldfish: "..."
  }
  var animal = req.params.animal.toLowerCase();
  var sound = sounds[animal];
  res.send("The" + animal + " says '" + sound + "'");
});

// "/repeat/:message/:times"
app.get("/repeat/:message/:times", function(req, res) {
  var message = req.params.message;
  var times = Number(req.params.times);
  var result = "";
  
  for (var i = 0; i <= times; i++) {
    result += message + " ";
  };
  
  res.send(result);
});

// "*"
app.get("*", function(req, res) {
  res.send("Sorry, page not found... What are you doing with your life?");
});

//=============================
// Server
//=============================

// Tell Express to Listen for requests (start server)
app.listen(process.env.PORT, process.env.IP, function() {
  console.log("Server Started!");
});
Last Updated: 12/15/2020, 10:27:30 PM