# Express.js 入門 - Express.js 是以 **Node.js 為基礎的 web 框架** - 常用來 **開發 API** 或是 **web 應用程式** - 可以快速的開發 [RESTful API](https://aws.amazon.com/tw/what-is/restful-api/) - 支援多種模板引擎,可以產生動態網頁 - Express.js 提供多種不同的物件和機制,**簡化專案的複雜度** - 開發者可以專注於應用程式的功能本身 :::info **更多閱讀**: - [Express.js 官網](https://expressjs.com/) - [Express.js 中文文件](https://expressjs.com/zh-tw/) ::: ## 如何開始開發 Express.js APP - 必須先安裝 [Node.js](https://nodejs.org/en/download/current) - 通常會新建一個資料夾,作為專案的目錄 - 透過 Node.js 的 `npm` 工具,下載 Express.js 的套件到專案目錄中 - 打開任意的終端機軟體(如: Power Shell, CMD, git bash...) - 輸入以下指令,將 Express.js 的檔案下載到專案目錄中 ``` npm install express ``` - 在此目錄中的程式碼檔案,即可存取 Express.js 中的功能和套件 ## Express.js 基本概念 Express.js 中有兩個重要的機制 - **Middleware** - 系統中的主要功能都必須用 middleware 的形式表示 - 目的是提供一個**簡單易讀的開發標準**,降低專案的複雜度 - 把複雜的功能拆成多個 middleware - 透過 Express.js 將這些 middleware 串接在一起 - 使用者只需要思考每個 middleware 該執行的任務,和執行的順序 - 資料傳遞和函式呼叫由 Express.js 負責 ![](https://iq.opengenus.org/content/images/2019/08/Add-a-subheading--1-.png) - **Route** - 根據 request 中的 URL,將 request 轉發給對應 middleware 的流程 - 可讓應用程式根據不同的 URL,執行不同的功能 - 同時提供一組簡潔的方法,解析、取得 URL 中的參數和資料 ### Express.js 應用程式架構 在 Node.js 中,可以用 `require()` 引入外部的模組和套件 ```javascript= const express = require("express"); ``` 在 Epress 應用程式中,會先宣告一個 **`Application` 物件** - 此**物件用來代表整個應用程式**,包含整個應用程式的功能和設定 ```javascript= const express = require("express"); /* 建立 Application 物件 */ let app = express(); /* 加入應用程式的功能和設定 */ // ... ``` 完成 `app` 的設定後,需要建立 HTTP Server,啟動應用程式 - 可以透過 Node.js 原生的 HTTP 模組,建立 Server 物件 ```javascript= const express = require("express"); const http = require("http"); /* 建立 Application 物件 */ let app = express(); /* 加入應用程式的功能和設定 */ // ... /* 建立 Server 物件並使用 port 3000 */ let server = http.createServer(app); server.listen(3000, function () { /* 當 server 成功啟動後 顯示一下訊息 */ console.log("Server listening on port 3000 ..."); }); ``` - `app` 物件也提供 `listen()` 方法,可以直接啟動 HTTP Server ```javascript= const express = require("express"); /* 建立 Application 物件 */ let app = express(); /* 加入應用程式的功能和設定 */ // ... /* 啟動 HTTP Server 並使用 port 3000 */ app.listen(3000, function () { /* 當 server 成功啟動後 顯示一下訊息 */ console.log("Server listening on port 3000 ..."); }); ``` ### 基礎 Middleware Express.js 中的所有功能都要以 middleware 的形式表示 - 可以把應用程式的一個功能,想成工廠上的一條產線 - 每一個 middleware 就像產線上的一台機器 - 每個 middleware 都負責完成某個小工作 - 應用程式收到 request 之後,會傳遞給 middleware - 當 middleware 結束他的工作時,就會將資料傳遞給下一個 middleware - 每個 middleware 完成一個小功能,直到產出 response - Middleware 本質上是有固定格式的 function - 包含三個參數,分別代表: - request: 來自 user 的請求 - response: 傳回給 user 的回應 - next: 下一個 middleware - 呼叫 `app.use()` 可以將 middleware 串接到 `app` 中 ```javascript= const express = require("express"); const http = require("http"); /* 建立 Application 物件 */ let app = express(); /* 加入 middleware */ app.use(function(req, res, next) { /* middleware 內容 */ }); app.use(function(req, res, next) { /* middleware 內容 */ }); ... /* 建立 Server 物件並使用 port 3000 */ let server = http.createServer(app); server.listen(3000, function () { /* 當 server 成功啟動後 顯示一下訊息 */ console.log("Server listening on port 3000 ..."); }); ``` :::success **舉個 🌰** ```javascript const express = require("express"); const http = require("http"); /* 建立 Application 物件 */ let app = express(); /* 加入 middleware */ app.use(function(req, res, next) { /* 當收到 request 時 輸出以下訊息 */ console.log("Hello Express!"); next(); // 進入下一個 middleware }); /* 建立 Server 物件並使用 port 3000 */ let server = http.createServer(app); server.listen(3000, function () { /* 當 server 成功啟動後 顯示一下訊息 */ console.log("Server listening on port 3000 ..."); }); ``` ::: ### 產出 Response Express 中有許多產出 response 的方法,以下列出較簡易的幾個方法 :::warning - 由於產出 response 的 middleware,必須是最後被執行的 middleware,所以不會呼叫 `next()` - 故該 middleware 的 `next` 參數可以省略 ::: #### `res.send(body)` - 將 `body` 中的內容加入 response body,並馬上送出 - 因為會馬上將 response 送回給 user,無法和其他方法一起使用 :::success **舉個 🌰** ```javascript const express = require("express"); const http = require("http"); let app = express(); app.use(function(req, res, next) { console.log("hello!"); next(); }); /* 產出 response 可以省略 next 參數 */ app.use(function(req, res) { /* 回應 HTML 字串 */ res.send("<h1>Hello, World!</h1>"); }); let server = http.createServer(app); server.listen(3000, function () { console.log("Server listening on port 3000 ..."); }); ``` - 打開 http://localhost:3000/ ,可以看到應用程式回應的內容 ![](https://hackmd.io/_uploads/By0mDzex6.png) ::: ### 基礎 Route - 在串接 middleware 時,可以設定 route 的參數 (虛擬路徑) - 當 URL 的開頭和該路徑相同時,request 將會傳遞給該 middleware :::success ```javascript const express = require("express"); const http = require("http"); let app = express(); /* 當 URL 的開頭是 / 時 將執行這個 middleware */ app.use("/", function(req, res, next) { /* 輸出 Hello Express! */ console.log("Hello Express!"); next(); }); /* 當 URL 的開頭是 /hello 時 將執行這個 middleware */ app.use("/hello", function(req, res, next) { /* 輸出 hello! */ console.log("hello!"); next(); }); /* 當 URL 的開頭是 /world 時 將執行這個 middleware */ app.use("/world", function(req, res, next) { /* 輸出 world! */ console.log("world!"); next(); }); app.use(function(req, res) { /* 回傳 HTML 字串 */ res.send("<h1>Hello, World!</h1>"); }); /* 建立 Server 物件並使用 port 3000 */ let server = http.createServer(app); server.listen(3000, function () { /* 當 server 成功啟動後 顯示一下訊息 */ console.log("Server listening on port 3000 ..."); }); ``` 在 URL 中加入不同的路徑,觀察程式的輸出 - http://localhost:3000/ - http://localhost:3000/hello - http://localhost:3000/hello/123 - http://localhost:3000/world - http://localhost:3000/world/abc ::: ## 根據不同 URL 回應不同內容 結合 route,和 middleware,可以根據不同的 URL 回應不同內容 :::info ```javascript const express = require("express"); const http = require("http"); let app = express(); app.use(function(req, res, next) { console.log("Hello Express!"); next(); }); app.use("/hello", function(req, res) { res.send("<h1>Hello</h1>"); }); app.use("/world", function(req, res, next) { res.send("<h1>World</h1>"); }); app.use("/", function(req, res, next) { res.send("<h1>Home</h1>"); }); /* 建立 Server 物件並使用 port 3000 */ let server = http.createServer(app); server.listen(3000, function () { /* 當 server 成功啟動後 顯示一下訊息 */ console.log("Server listening on port 3000 ..."); }); ``` - http://localhost:3000/ - http://localhost:3000/ - http://localhost:3000/world :::