ExpressJS - 快速指南



ExpressJS - 概述

ExpressJS 是一個 Web 應用程式框架,它提供了一個簡單的 API 來構建網站、Web 應用和後端。使用 ExpressJS,您無需擔心底層協議、流程等。

什麼是 Express?

Express 提供了一個最小的介面來構建我們的應用程式。它為我們提供了構建應用程式所需的工具。它很靈活,因為在 npm 上有許多可用的模組,可以直接插入 Express 中。

Express 由 TJ Holowaychuk 開發,並由 Node.js 基金會和眾多開源貢獻者維護。

為什麼選擇 Express?

與 Rails 和 Django 等競爭對手不同,它們構建應用程式的方式有自己的主張,Express 沒有“最佳方式”來做某事。它非常靈活且可擴充套件。

Pug

Pug(以前稱為 Jade)是一種簡潔的語言,用於編寫 HTML 模板。它 -

  • 生成 HTML
  • 支援動態程式碼
  • 支援可重用性(DRY)

它是 Express 最常用的模板語言之一。

MongoDB 和 Mongoose

MongoDB 是一個開源的文件資料庫,旨在簡化開發和擴充套件。此資料庫也用於儲存資料。

Mongoose 是一個 node.js 的客戶端 API,它使我們能夠輕鬆地從 Express 應用程式訪問我們的資料庫。

ExpressJS - 環境

在本章中,我們將學習如何開始開發和使用 Express 框架。首先,您應該安裝 Node 和 npm(節點包管理器)。如果您尚未安裝這些,請訪問 Node 設定 在本地系統上安裝 Node。透過在終端中執行以下命令確認 Node 和 npm 是否已安裝。

node --version
npm --version

您應該得到類似於以下的輸出。

v5.0.0
3.5.2

現在我們已經設定了 Node 和 npm,讓我們瞭解一下 npm 是什麼以及如何使用它。

Node 包管理器 (npm)

npm 是 Node 的包管理器。npm 登錄檔是 JavaScript 社群中 Node.js、前端 Web 應用、移動應用、機器人、路由器以及無數其他需求的開原始碼包的公共集合。npm 允許我們訪問所有這些包並在本地安裝它們。您可以在 npmJS 上瀏覽 npm 上可用的包列表。

如何使用 npm?

有兩種方法可以使用 npm 安裝包:全域性安裝和本地安裝。

  • 全域性安裝 - 此方法通常用於安裝開發工具和基於 CLI 的包。要全域性安裝包,請使用以下程式碼。

npm install -g <package-name>
  • 本地安裝 - 此方法通常用於安裝框架和庫。本地安裝的包只能在其安裝的目錄中使用。要本地安裝包,請使用與上面相同的命令,但不帶 -g 標誌。

npm install <package-name>

每當我們使用 npm 建立專案時,都需要提供一個 package.json 檔案,其中包含我們專案的所有詳細資訊。npm 使我們能夠輕鬆設定此檔案。讓我們設定我們的開發專案。

步驟 1 - 啟動您的終端/cmd,建立一個名為 hello-world 的新資料夾並 cd(建立目錄)到其中 -

npm init info

步驟 2 - 現在要使用 npm 建立 package.json 檔案,請使用以下程式碼。

npm init

它會要求您提供以下資訊。

npm init info

只需一直按 Enter 鍵,並在“作者姓名”欄位中輸入您的姓名。

步驟 3 - 現在我們已經設定了 package.json 檔案,我們將進一步安裝 Express。要安裝 Express 並將其新增到我們的 package.json 檔案中,請使用以下命令 -

npm install --save express

要確認 Express 是否已正確安裝,請執行以下程式碼。

ls node_modules #(dir node_modules for windows)

提示 - --save 標誌可以替換為 -S 標誌。此標誌確保 Express 作為依賴項新增到我們的 package.json 檔案中。這有一個優點,下次我們需要安裝專案的所有依賴項時,我們只需執行命令 npm install,它就會在此檔案中找到依賴項併為我們安裝它們。

這就是我們使用 Express 框架開始開發所需的一切。為了使我們的開發過程更容易,我們將從 npm 安裝一個工具,nodemon。此工具會在我們修改任何檔案後立即重新啟動我們的伺服器,否則我們需要在每次檔案修改後手動重新啟動伺服器。要安裝 nodemon,請使用以下命令 -

npm install -g nodemon

您現在可以開始使用 Express 了。

ExpressJS - Hello World

我們已經設定了開發環境,現在是時候開始使用 Express 開發我們的第一個應用程式了。建立一個名為 index.js 的新檔案,並在其中輸入以下內容。

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

app.get('/', function(req, res){
   res.send("Hello world!");
});

app.listen(3000);

儲存檔案,轉到您的終端並輸入以下內容。

nodemon index.js

這將啟動伺服器。要測試此應用程式,請開啟您的瀏覽器並轉到 https://:3000,將顯示一條訊息,如下面的螢幕截圖所示。

Hello world

應用程式如何工作?

第一行將 Express 匯入我們的檔案,我們可以透過變數 Express 訪問它。我們用它來建立一個應用程式並將其分配給 var app。

app.get(route, callback)

此函式告訴在呼叫給定路由的 get 請求時該做什麼。回撥函式有兩個引數,request(req)response(res)。請求 物件 (req) 表示 HTTP 請求,並具有請求查詢字串、引數、主體、HTTP 標頭等的屬性。同樣,響應物件表示 Express 應用程式在收到 HTTP 請求時傳送的 HTTP 響應。

res.send()

此函式以物件作為輸入,並將其傳送到請求的客戶端。這裡我們傳送字串 "Hello World!"

app.listen(port, [host], [backlog], [callback]])

此函式繫結並在指定的主機和埠上偵聽連線。埠是這裡唯一必需的引數。

序號 引數和說明
1

伺服器應接受傳入請求的埠號。

2

主機

域名的名稱。當您將應用程式部署到雲端時,需要設定它。

3

積壓

排隊等待連線的最大數量。預設值為 511。

4

回撥

伺服器開始偵聽請求時呼叫的非同步函式。

ExpressJS - 路由

Web 框架在不同的路由中提供資源,例如 HTML 頁面、指令碼、影像等。

以下函式用於在 Express 應用程式中定義路由 -

app.method(path, handler)

此 METHOD 可以應用於任何一個 HTTP 動詞 - get、set、put、delete。還存在另一種方法,它獨立於請求型別執行。

路徑是請求將執行的路由。

處理程式是在相關路由上找到匹配的請求型別時執行的回撥函式。例如,

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

app.get('/hello', function(req, res){
   res.send("Hello World!");
});

app.listen(3000);

如果我們執行應用程式並轉到 localhost:3000/hello,伺服器將在路由 "/hello" 上接收 get 請求,我們的 Express 應用程式將執行附加到此路由的 回撥 函式並將 "Hello World!" 作為響應傳送。

Hello

我們也可以在同一路由上有多種不同的方法。例如,

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

app.get('/hello', function(req, res){
   res.send("Hello World!");
});

app.post('/hello', function(req, res){
   res.send("You just called the post method at '/hello'!\n");
});

app.listen(3000);

要測試此請求,請開啟您的終端並使用 cURL 執行以下請求 -

curl -X POST "https://:3000/hello"

Curl request

Express 提供了一種特殊方法 all,用於使用相同函式處理特定路由上的所有型別的 http 方法。要使用此方法,請嘗試以下操作。

app.all('/test', function(req, res){
   res.send("HTTP method doesn't have any effect on this route!");
});

此方法通常用於定義中介軟體,我們將在中介軟體章節中討論。

路由器

像上面那樣定義路由維護起來非常麻煩。為了將路由與我們的主要 index.js 檔案分離,我們將使用 Express.Router。建立一個名為 things.js 的新檔案,並在其中輸入以下內容。

var express = require('express');
var router = express.Router();

router.get('/', function(req, res){
   res.send('GET route on things.');
});
router.post('/', function(req, res){
   res.send('POST route on things.');
});

//export this router to use in our index.js
module.exports = router;

現在要在我們的 index.js 中使用此路由器,請在 app.listen 函式呼叫之前輸入以下內容。

var express = require('Express');
var app = express();

var things = require('./things.js');

//both index.js and things.js should be in same directory
app.use('/things', things);

app.listen(3000);

在路由 '/things' 上的 app.use 函式呼叫將 things 路由器附加到此路由。現在,我們的應用程式在 '/things' 上接收到的任何請求都將由我們的 things.js 路由器處理。'/' 路由在 things.js 中實際上是 '/things' 的子路由。訪問 localhost:3000/things/,您將看到以下輸出。

Router Things

路由器在分離關注點並將相關程式碼段放在一起方面非常有用。它們有助於構建可維護的程式碼。您應該在一個檔案中定義與實體相關的路由,並使用上述方法將其包含在您的 index.js 檔案中。

ExpressJS - HTTP 方法

HTTP 方法在請求中提供,並指定客戶端請求的操作。下表列出了最常用的 HTTP 方法 -

序號 方法和說明
1

GET

GET 方法請求指定資源的表示形式。使用 GET 的請求應該只檢索資料,並且不應產生其他影響。

2

POST

POST 方法請求伺服器接受請求中包含的資料作為 URI 標識的資源的新物件/實體。

3

PUT

PUT 方法請求伺服器接受請求中包含的資料作為對 URI 標識的現有物件的修改。如果它不存在,則 PUT 方法應該建立一個。

4

DELETE

DELETE 方法請求伺服器刪除指定的資源。

這些是最常用的 HTTP 方法。要了解更多關於這些方法的資訊,請訪問 https://tutorialspoint.tw/http/http_methods.htm

ExpressJS - URL 構建

我們現在可以定義路由了,但這些路由是靜態的或固定的。要使用動態路由,我們**應該**提供不同型別的路由。使用動態路由允許我們傳遞引數並基於這些引數進行處理。

這是一個動態路由的示例:

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

app.get('/:id', function(req, res){
   res.send('The id you specified is ' + req.params.id);
});
app.listen(3000);

要測試它,請訪問https://:3000/123。將顯示以下響應。

URL Building 1

您可以將 URL 中的“123”替換為任何其他內容,並且更改將反映在響應中。上面一個更復雜的示例是:

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

app.get('/things/:name/:id', function(req, res) {
   res.send('id: ' + req.params.id + ' and name: ' + req.params.name);
});
app.listen(3000);

要測試以上程式碼,請訪問https://:3000/things/tutorialspoint/12345

URL Building 2

您可以使用req.params物件訪問您在 url 中傳遞的所有引數。請注意,以上兩個是不同的路徑。它們永遠不會重疊。此外,如果您希望在獲取'/things'時執行程式碼,則需要單獨定義它。

模式匹配路由

您還可以使用regex來限制 URL 引數匹配。假設您需要id為 5 位長數字。您可以使用以下路由定義:

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

app.get('/things/:id([0-9]{5})', function(req, res){
   res.send('id: ' + req.params.id);
});

app.listen(3000);

請注意,這**僅**匹配具有 5 位長id的請求。您可以使用更復雜的正則表示式來匹配/驗證您的路由。如果您的任何路由都不匹配請求,您將收到“Cannot GET <your-request-route>”訊息作為響應。可以使用以下簡單路由將此訊息替換為 404 未找到頁面:

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

//Other routes here
app.get('*', function(req, res){
   res.send('Sorry, this is an invalid URL.');
});
app.listen(3000);

重要提示:這應該放在所有路由之後,因為 Express 從index.js檔案的開頭到結尾匹配路由,包括您需要的外部路由器。

例如,如果我們定義與上面相同的路由,則在使用有效 URL 請求時,將顯示以下輸出:

Correct regex

而對於錯誤的 URL 請求,將顯示以下輸出。

Invalid regex(404)

ExpressJS - 中介軟體

中介軟體函式是可以訪問請求物件 (req)響應物件 (res)和應用程式請求-響應週期中下一個中介軟體函式的函式。這些函式用於修改reqres物件,以執行解析請求主體、新增響應標頭等任務。

這是一箇中間件函式在行動中的簡單示例:

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

//Simple request time logger
app.use(function(req, res, next){
   console.log("A new request received at " + Date.now());
   
   //This function call is very important. It tells that more processing is
   //required for the current request and is in the next middleware
   function route handler.
   next();
});

app.listen(3000);

上述中介軟體在伺服器上的每個請求都會被呼叫。因此,在每次請求之後,我們將在控制檯中看到以下訊息:

A new request received at 1467267512545

要將其限制到特定路由(及其所有子路由),請將該路由作為app.use()的第一個引數提供。例如,

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

//Middleware function to log request protocol
app.use('/things', function(req, res, next){
   console.log("A request for things received at " + Date.now());
   next();
});

// Route handler that sends the response
app.get('/things', function(req, res){
   res.send('Things');
});

app.listen(3000);

現在,每當您請求'/things'的任何子路由時,它才會記錄時間。

中介軟體呼叫順序

關於 Express 中的中介軟體,最重要的事情之一是它們在檔案中寫入/包含的順序;還需要考慮它們執行的順序,前提是路由也匹配。

例如,在以下程式碼片段中,第一個函式首先執行,然後是路由處理程式,然後是結束函式。此示例總結了如何在路由處理程式之前和之後使用中介軟體;以及如何將路由處理程式本身用作中介軟體。

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

//First middleware before response is sent
app.use(function(req, res, next){
   console.log("Start");
   next();
});

//Route handler
app.get('/', function(req, res, next){
   res.send("Middle");
   next();
});

app.use('/', function(req, res){
   console.log('End');
});

app.listen(3000);

當我們執行此程式碼後訪問'/'時,我們收到響應為Middle,並在控制檯中:

Start
End

下圖總結了我們關於中介軟體的學習內容:

Middleware

現在我們已經介紹瞭如何建立自己的中介軟體,讓我們討論一些最常用的社群建立的中介軟體。

第三方中介軟體

Express 的第三方中介軟體列表可在 此處找到。以下是一些最常用的中介軟體;我們還將學習如何使用/掛載這些中介軟體:

body-parser

這用於解析帶有附加有效負載的請求的主體。要掛載 body-parser,我們需要使用npm install --save body-parser 進行安裝,並將其掛載,在您的 index.js 中包含以下行:

var bodyParser = require('body-parser');

//To parse URL encoded data
app.use(bodyParser.urlencoded({ extended: false }))

//To parse json data
app.use(bodyParser.json())

要檢視 body-parser 的所有可用選項,請訪問其 github 頁面。

cookie-parser

它解析Cookie標頭並將 req.cookies 填充為一個以 cookie 名稱為鍵的物件。要掛載 cookie-parser,我們需要使用 npm install --save cookie-parser 進行安裝,並將其掛載,在您的 index.js 中包含以下行:

var cookieParser = require('cookie-parser');
app.use(cookieParser())

express-session

它使用給定的選項建立一個會話中介軟體。我們將在會話部分討論其用法。

ExpressJS 中還有許多其他第三方中介軟體。但是,我們在這裡只討論了一些重要的中介軟體。

ExpressJS - 模板引擎

Pug 是 Express 的模板引擎。模板引擎用於消除伺服器程式碼中 HTML 的混亂,避免大量連線字串到現有的 HTML 模板。Pug 是一個非常強大的模板引擎,具有多種功能,包括過濾器、包含、繼承、插值等。這方面的內容很多。

要在 Express 中使用 Pug,我們需要安裝它,

npm install --save pug

現在 Pug 已安裝,將其設定為應用程式的模板引擎。您**無需**“require”它。將以下程式碼新增到您的index.js檔案中。

app.set('view engine', 'pug');
app.set('views','./views');

現在建立一個名為 views 的新目錄。在其中建立一個名為first_view.pug的檔案,並在其中輸入以下資料。

doctype html
html
   head
      title = "Hello Pug"
   body
      p.greetings#people Hello World!

要執行此頁面,請將以下路由新增到您的應用程式中:

app.get('/first_template', function(req, res){
   res.render('first_view');
});

您將獲得以下輸出:Hello World! Pug 將這個非常簡單的標記轉換為 html。我們不需要跟蹤關閉標籤,不需要使用 class 和 id 關鍵字,而是使用 '.' 和'#'來定義它們。上面的程式碼首先轉換為:

<!DOCTYPE html>
<html>
   <head>
      <title>Hello Pug</title>
   </head>
   
   <body>
      <p class = "greetings" id = "people">Hello World!</p>
   </body>
</html>

Pug 能夠做的不僅僅是簡化 HTML 標記。

Pug 的重要特性

現在讓我們探索一些 Pug 的重要特性。

簡單標籤

標籤根據縮排巢狀。就像在上面的例子中,<title>縮排在<head>標籤內,所以它在裡面。但是<body>標籤在相同的縮排級別上,所以它是<head>標籤的同級標籤。

我們不需要關閉標籤,一旦 Pug 遇到相同或外部縮排級別的下一個標籤,它就會為我們關閉標籤。

要在標籤內放置文字,我們有三種方法:

  • 空格分隔

h1 Welcome to Pug
  • 管道文字

div
   | To insert multiline text, 
   | You can use the pipe operator.
  • 文字塊

div.
   But that gets tedious if you have a lot of text.
   You can use "." at the end of tag to denote block of text.
   To put tags inside this block, simply enter tag in a new line and 
   indent it accordingly.

註釋

Pug 使用與JavaScript(//)相同的語法來建立註釋。這些註釋被轉換為 html 註釋(<!--comment-->)。例如,

//This is a Pug comment

此註釋將轉換為以下內容。

<!--This is a Pug comment-->

屬性

要定義屬性,我們使用逗號分隔的屬性列表,括在括號中。Class 和 ID 屬性具有特殊的表示形式。以下程式碼行涵蓋了為給定的 html 標籤定義屬性、類和 id。

div.container.column.main#division(width = "100", height = "100")

此程式碼行將轉換為以下內容:

<div class = "container column main" id = "division" width = "100" height = "100"></div>

將值傳遞給模板

當我們渲染 Pug 模板時,我們實際上可以從路由處理程式傳遞一個值,然後我們可以在模板中使用它。建立一個新的路由處理程式,如下所示。

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

app.get('/dynamic_view', function(req, res){
   res.render('dynamic', {
      name: "TutorialsPoint", 
      url:"https://tutorialspoint.tw"
   });
});

app.listen(3000);

並在 views 目錄中建立一個新的檢視檔案,名為dynamic.pug,包含以下程式碼:

html
   head
      title=name
   body
      h1=name
      a(href = url) URL

在瀏覽器中開啟 localhost:3000/dynamic_view;您應該獲得以下輸出:

Variables in template

我們也可以在文字中使用這些傳遞的變數。要在標籤文字之間插入傳遞的變數,我們使用#{variableName}語法。例如,在上面的示例中,如果我們想放置 Greetings from TutorialsPoint,那麼我們可以執行以下操作。

html
   head
      title = name
   body
      h1 Greetings from #{name}
      a(href = url) URL

這種使用值的方法稱為插值。以上程式碼將顯示以下輸出:

Templating Inter

條件語句

我們也可以使用條件語句和迴圈結構。

考慮以下內容:

如果使用者已登入,頁面應顯示“Hi, User”,否則顯示“Login/Sign Up”連結。為了實現這一點,我們可以定義一個簡單的模板,如下所示:

html
   head
      title Simple template
   body
      if(user)
         h1 Hi, #{user.name}
      else
         a(href = "/sign_up") Sign Up

當我們使用我們的路由渲染它時,我們可以像在以下程式中一樣傳遞一個物件:

res.render('/dynamic',{
   user: {name: "Ayush", age: "20"}
});

您將收到一條訊息:Hi, Ayush。但是,如果我們不傳遞任何物件或傳遞一個沒有 user 鍵的物件,那麼我們將獲得一個註冊連結。

包含和元件

Pug 提供了一種非常直觀的方式來建立網頁元件。例如,如果您看到一個新聞網站,帶有徽標和類別的頁首始終是固定的。與其將它複製到我們建立的每個檢視中,我們可以使用include功能。以下示例顯示了我們如何使用此功能:

建立 3 個具有以下程式碼的檢視:

HEADER.PUG

div.header.
   I'm the header for this website.

CONTENT.PUG

html
   head
      title Simple template
   body
      include ./header.pug
      h3 I'm the main content
      include ./footer.pug

FOOTER.PUG

div.footer.
   I'm the footer for this website.

為此建立一個路由,如下所示:

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

app.get('/components', function(req, res){
    res.render('content');
});

app.listen(3000);

轉到 localhost:3000/components,您將收到以下輸出:

templating components

include也可用於包含純文字、css 和 JavaScript。

Pug 還有許多其他功能。但這些超出了本教程的範圍。您可以在 Pug 進一步探索 Pug。

ExpressJS - 提供靜態檔案

靜態檔案是客戶端從伺服器下載的檔案,保持原樣。建立一個新目錄public。預設情況下,Express 不允許您提供靜態檔案。您需要使用以下內建中介軟體啟用它。

app.use(express.static('public'));

注意:Express 相對靜態目錄查詢檔案,因此靜態目錄的名稱不是 URL 的一部分。

請注意,根路由現在設定為您的 public 目錄,因此您載入的所有靜態檔案都將以 public 作為根。要測試這是否正常工作,請在您的新public目錄中新增任何影像檔案,並將其名稱更改為“testimage.jpg”。在您的檢視中,建立一個新的檢視幷包含此檔案,如下所示:

html
   head
   body
      h3 Testing static file serving:
      img(src = "/testimage.jpg", alt = "Testing Image

您應該獲得以下輸出:

Static Files Example

多個靜態目錄

我們還可以使用以下程式設定多個靜態資產目錄:

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

app.use(express.static('public'));
app.use(express.static('images'));

app.listen(3000);

虛擬路徑字首

我們還可以為提供靜態檔案提供路徑字首。例如,如果您想提供一個像'/static'這樣的路徑字首,則需要在您的index.js檔案中包含以下程式碼:

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

app.use('/static', express.static('public'));

app.listen(3000);

現在,每當您需要包含一個檔案時,例如駐留在您的 public 目錄中的名為 main.js 的指令碼檔案,請使用以下指令碼標籤:

<script src = "/static/main.js" />

當提供多個目錄作為靜態檔案時,此技術非常有用。這些字首可以幫助區分多個目錄。

ExpressJS - 表單資料

表單是網頁不可或缺的一部分。我們訪問的幾乎每個網站都提供表單,以便為我們提交或獲取一些資訊。要開始使用表單,我們首先需要安裝body-parser(用於解析 JSON 和 url 編碼資料)和 multer(用於解析 multipart/form 資料)中介軟體。

要安裝body-parsermulter,請轉到您的終端並使用以下命令:

npm install --save body-parser multer

將您的index.js檔案內容替換為以下程式碼:

var express = require('express');
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer();
var app = express();

app.get('/', function(req, res){
   res.render('form');
});

app.set('view engine', 'pug');
app.set('views', './views');

// for parsing application/json
app.use(bodyParser.json()); 

// for parsing application/xwww-
app.use(bodyParser.urlencoded({ extended: true })); 
//form-urlencoded

// for parsing multipart/form-data
app.use(upload.array()); 
app.use(express.static('public'));

app.post('/', function(req, res){
   console.log(req.body);
   res.send("recieved your request!");
});
app.listen(3000);

匯入 body parser 和 multer 後,我們將使用body-parser解析 json 和 x-www-form-urlencoded 頭請求,而使用multer解析 multipart/form-data。

讓我們建立一個 html 表單來測試一下。建立一個名為form.pug的新檢視,內容如下:

html
html
   head
      title Form Tester
   body
      form(action = "/", method = "POST")
         div
            label(for = "say") Say:
            input(name = "say" value = "Hi")
         br
         div
            label(for = "to") To:
            input(name = "to" value = "Express forms")
         br
         button(type = "submit") Send my greetings

使用以下命令執行您的伺服器。

nodemon index.js

現在訪問 localhost:3000/ 並根據需要填寫表單並提交。將顯示以下響應:

Response to form submission

檢視您的控制檯;它將向您顯示請求主體作為 JavaScript 物件,如下面的螢幕截圖所示:

Console output for form

req.body物件包含您解析的請求主體。要使用該物件中的欄位,只需像使用普通 JS 物件一樣使用它們即可。

這是最推薦的傳送請求的方式。還有許多其他方法,但這裡與之無關,因為我們的 Express 應用程式將以相同的方式處理所有這些請求。要詳細瞭解不同的請求方式,請檢視頁面。

ExpressJS - 資料庫

我們不斷收到請求,但最終沒有將它們儲存在任何地方。我們需要一個數據庫來儲存資料。為此,我們將使用名為MongoDB的 NoSQL 資料庫。

要安裝並瞭解 Mongo,請遵循此連結。

為了將 Mongo 與 Express 一起使用,我們需要一個 Node 的客戶端 API。我們有多種選擇,但對於本教程,我們將堅持使用mongoose。Mongoose 用於 MongoDB 的 Node 中的文件建模。對於文件建模,我們建立一個模型(類似於面向文件程式設計中的),然後我們使用此模型生成文件(就像我們在 OOP 中建立類的文件一樣)。我們所有的處理都將在這些“文件”上完成,然後最終,我們將這些文件寫入我們的資料庫。

設定 Mongoose

現在您已經安裝了 Mongo,讓我們安裝 Mongoose,就像我們安裝其他 Node 包一樣:

npm install --save mongoose

在開始使用 mongoose 之前,我們必須使用 Mongo shell 建立一個數據庫。要建立一個新的資料庫,請開啟您的終端並輸入“mongo”。將啟動一個 Mongo shell,輸入以下程式碼:

use my_db

將為您建立一個新的資料庫。每當您開啟 mongo shell 時,它都會預設使用“test”資料庫,您需要使用與上面相同的命令切換到您的資料庫。

要使用 Mongoose,我們將在index.js檔案中引入它,然後連線到在mongodb://上執行的mongodb服務。

var mongoose = require('mongoose');
mongoose.connect('mongodb:///my_db');

現在我們的應用程式已連線到我們的資料庫,讓我們建立一個新的模型。此模型將充當我們資料庫中的集合。要建立一個新的模型,請在定義任何路由之前使用以下程式碼:

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});
var Person = mongoose.model("Person", personSchema);

以上程式碼定義了人的模式,並用於建立 Mongoose 模型Person

儲存文件

現在,我們將建立一個新的 html 表單;此表單將幫助您獲取一個人的詳細資訊並將其儲存到我們的資料庫中。要建立表單,請在 views 目錄中建立一個名為person.pug的新檢視檔案,內容如下:

html
head
   title Person
   body
      form(action = "/person", method = "POST")
      div
         label(for = "name") Name: 
         input(name = "name")
      br
      div
         label(for = "age") Age: 
         input(name = "age")
      br
      div
         label(for = "nationality") Nationality: 
         input(name = "nationality")
      br
      button(type = "submit") Create new person

還在index.js中新增一個新的 get 路由以渲染此文件:

app.get('/person', function(req, res){
   res.render('person');
});

轉到“localhost:3000/person”以檢查表單是否顯示了正確的輸出。請注意,這僅僅是 UI,它還沒有工作。以下螢幕截圖顯示了表單的顯示方式:

Mongoose Create

我們現在將在'/person'處定義一個 post 路由處理程式來處理此請求

app.post('/person', function(req, res){
   var personInfo = req.body; //Get the parsed information
   
   if(!personInfo.name || !personInfo.age || !personInfo.nationality){
      res.render('show_message', {
         message: "Sorry, you provided worng info", type: "error"});
   } else {
      var newPerson = new Person({
         name: personInfo.name,
         age: personInfo.age,
         nationality: personInfo.nationality
      });
		
      newPerson.save(function(err, Person){
         if(err)
            res.render('show_message', {message: "Database error", type: "error"});
         else
            res.render('show_message', {
               message: "New person added", type: "success", person: personInfo});
      });
   }
});

在上面的程式碼中,如果我們收到任何空欄位或未收到任何欄位,我們將傳送錯誤響應。但是,如果我們收到一個格式良好的文件,那麼我們將從 Person 模型建立一個newPerson文件並使用newPerson.save()函式將其儲存到我們的資料庫中。這在 Mongoose 中定義,並接受回撥作為引數。此回撥有兩個引數 - 錯誤和響應。這些引數將渲染show_message檢視。

要顯示此路由的響應,我們還需要建立一個show_message檢視。使用以下程式碼建立一個新檢視:

html
   head
      title Person
   body
      if(type == "error")
         h3(style = "color:red") #{message}
      else
         h3 New person, 
            name: #{person.name}, 
            age: #{person.age} and 
            nationality: #{person.nationality} added!

成功提交表單(show_message.pug)後,我們將收到以下響應:

Mongoose Response

現在我們有一個建立人員的介面。

檢索文件

Mongoose 提供了許多用於檢索文件的函式,我們將重點介紹其中的 3 個。所有這些函式都將回調作為最後一個引數,並且就像 save 函式一樣,它們的 arguments 是錯誤和響應。這三個函式如下:

Model.find(conditions, callback)

此函式查詢與 conditions 物件中的欄位匹配的所有文件。在 Mongo 中使用的相同運算子也適用於 mongoose。例如,

Person.find(function(err, response){
   console.log(response);
});

這將從人員集合中獲取所有文件。

Person.find({name: "Ayush", age: 20}, 
   function(err, response){
      console.log(response);
});

這將獲取名稱欄位為“Ayush”且年齡為 20 的所有文件。

我們還可以提供所需的投影,即我們所需的欄位。例如,如果我們只需要國籍為“Indian”的人的姓名,則使用:

Person.find({nationality: "Indian"}, "name", function(err, response){
   console.log(response);
});

Model.findOne(conditions, callback)

此函式始終獲取一個最相關的單個文件。它與Model.find()具有完全相同的 arguments。

Model.findById(id, callback)

此函式將_id(由 mongo 定義)作為第一個 arguments,一個可選的投影字串和一個回撥來處理響應。例如,

Person.findById("507f1f77bcf86cd799439011", function(err, response){
   console.log(response);
});

現在讓我們建立一個路由來檢視所有人員記錄:

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

var mongoose = require('mongoose');
mongoose.connect('mongodb:///my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

app.get('/people', function(req, res){
   Person.find(function(err, response){
      res.json(response);
   });
});

app.listen(3000);

更新文件

Mongoose 提供了 3 個用於更新文件的函式。這些函式如下所述:

Model.update(condition, updates, callback)

此函式以條件和更新物件作為輸入,並將更改應用於集合中與條件匹配的所有文件。例如,以下程式碼將更新所有 Person 文件中的國籍“American”:

Person.update({age: 25}, {nationality: "American"}, function(err, response){
   console.log(response);
});

Model.findOneAndUpdate(condition, updates, callback)

它根據查詢查詢一個文件,並根據第二個 arguments 更新該文件。它還將回調作為最後一個 arguments。讓我們執行以下示例以瞭解該函式

Person.findOneAndUpdate({name: "Ayush"}, {age: 40}, function(err, response) {
   console.log(response);
});

Model.findByIdAndUpdate(id, updates, callback)

此函式更新由其 id 標識的單個文件。例如,

Person.findByIdAndUpdate("507f1f77bcf86cd799439011", {name: "James"}, 
   function(err, response){
      console.log(response);
});

現在讓我們建立一個路由來更新人員。這將是一個PUT路由,其中 id 作為引數,詳細資訊在有效負載中。

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

var mongoose = require('mongoose');
mongoose.connect('mongodb:///my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

app.put('/people/:id', function(req, res){
   Person.findByIdAndUpdate(req.params.id, req.body, function(err, response){
      if(err) res.json({message: "Error in updating person with id " + req.params.id});
      res.json(response);
   });
});

app.listen(3000);

要測試此路由,請在您的終端中輸入以下內容(將 id 替換為您建立的人員中的 id):

curl -X PUT --data "name = James&age = 20&nationality = American
"https://:3000/people/507f1f77bcf86cd799439011

這將使用上述詳細資訊更新與路由中提供的 id 關聯的文件。

刪除文件

我們已經介紹了建立、讀取更新,現在我們將瞭解如何使用 Mongoose 來刪除文件。我們這裡有 3 個函式,與更新完全一樣。

Model.remove(condition, [callback])

此函式以條件物件作為輸入,並刪除與條件匹配的所有文件。例如,如果我們需要刪除所有 20 歲的使用者,請使用以下語法:

Person.remove({age:20});

Model.findOneAndRemove(condition, [callback])

此函式根據條件物件刪除單個最相關的文件。讓我們執行以下程式碼以瞭解相同的內容。

Person.findOneAndRemove({name: "Ayush"});

Model.findByIdAndRemove(id, [callback])

此函式刪除由其 id 標識的單個文件。例如,

Person.findByIdAndRemove("507f1f77bcf86cd799439011");

現在讓我們建立一個路由來從我們的資料庫中刪除人員。

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

var mongoose = require('mongoose');
mongoose.connect('mongodb:///my_db');

var personSchema = mongoose.Schema({
   name: String,
   age: Number,
   nationality: String
});

var Person = mongoose.model("Person", personSchema);

app.delete('/people/:id', function(req, res){
   Person.findByIdAndRemove(req.params.id, function(err, response){
      if(err) res.json({message: "Error in deleting record id " + req.params.id});
      else res.json({message: "Person with id " + req.params.id + " removed."});
   });
});

app.listen(3000);

要檢查輸出,請使用以下 curl 命令:

curl -X DELETE https://:3000/people/507f1f77bcf86cd799439011

這將刪除具有給定 id 的人員,並生成以下訊息:

{message: "Person with id 507f1f77bcf86cd799439011 removed."}

這總結了我們如何使用 MongoDB、Mongoose 和 Express 建立簡單的 CRUD 應用程式。要進一步探索 Mongoose,請閱讀API 文件。

ExpressJS - Cookies

Cookie 是簡單的、小型的檔案/資料,隨伺服器請求傳送到客戶端並在客戶端儲存。每次使用者重新載入網站時,此 Cookie 都會隨請求一起傳送。這有助於我們跟蹤使用者的操作。

以下是 HTTP Cookie 的眾多用途:

  • 會話管理
  • 個性化(推薦系統)
  • 使用者跟蹤

要將 Cookie 與 Express 一起使用,我們需要 cookie-parser 中介軟體。要安裝它,請使用以下程式碼:

npm install --save cookie-parser

現在要將 Cookie 與 Express 一起使用,我們將需要cookie-parser。cookie-parser 是一箇中間件,它解析附加到客戶端請求物件的 Cookie。要使用它,我們將在index.js檔案中引入它;這可以像我們使用其他中介軟體一樣使用。這裡,我們將使用以下程式碼。

var cookieParser = require('cookie-parser');
app.use(cookieParser());

cookie-parser 解析 Cookie 標頭,並使用 Cookie 名稱作為鍵填充req.cookies物件。要設定新的 Cookie,讓我們在您的 Express 應用程式中定義一個新的路由,例如:

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

app.get('/', function(req, res){
   res.cookie('name', 'express').send('cookie set'); //Sets name = express
});

app.listen(3000);

要檢查您的 Cookie 是否已設定,只需轉到您的瀏覽器,啟動控制檯並輸入:

console.log(document.cookie);

您將獲得類似於以下的輸出(您可能設定了更多 Cookie,可能是由於瀏覽器中的擴充套件程式):

"name = express"

瀏覽器每次查詢伺服器時也會發送回 Cookie。要在伺服器控制檯中檢視伺服器的 Cookie,請在路由中新增以下程式碼到該路由。

console.log('Cookies: ', req.cookies);

下次您向此路由傳送請求時,您將收到以下輸出。

Cookies: { name: 'express' }

新增具有過期時間的 Cookie

您可以新增過期的 Cookie。要新增過期的 Cookie,只需傳遞一個物件,該物件的“expire”屬性設定為您希望它過期的時刻。例如,

//Expires after 360000 ms from the time it is set.
res.cookie(name, 'value', {expire: 360000 + Date.now()}); 

設定過期時間的另一種方法是使用'maxAge'屬性。使用此屬性,我們可以提供相對時間而不是絕對時間。以下為此方法的示例。

//This cookie also expires after 360000 ms from the time it is set.
res.cookie(name, 'value', {maxAge: 360000});

刪除現有 Cookie

要刪除 Cookie,請使用 clearCookie 函式。例如,如果您需要清除名為foo的 Cookie,請使用以下程式碼。

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

app.get('/clear_cookie_foo', function(req, res){
   res.clearCookie('foo');
   res.send('cookie foo cleared');
});

app.listen(3000);

在下一章中,我們將瞭解如何使用 Cookie 管理會話。

ExpressJS - Session

HTTP 協議是無狀態的;為了將一個請求與任何其他請求關聯起來,你需要一種方法在 HTTP 請求之間儲存使用者資料。Cookie 和 URL 引數都是傳輸客戶端和伺服器之間資料的合適方法。但它們都可以在客戶端讀取。會話解決了這個問題。你為客戶端分配一個 ID,它使用該 ID 發出所有後續請求。與客戶端相關的資訊儲存在伺服器上,並與該 ID 連結。

我們需要 Express-session,所以使用以下程式碼安裝它。

npm install --save express-session

我們將 sessioncookie-parser 中介軟體部署到位。在本例中,我們將使用預設儲存來儲存會話,即 MemoryStore。切勿在生產環境中使用此儲存。會話中介軟體為我們處理所有事情,例如建立會話、設定會話 Cookie 以及在 req 物件中建立會話物件。

每當我們再次從同一客戶端發出請求時,我們都會將他們的會話資訊儲存在我們這裡(前提是伺服器沒有重啟)。我們可以向會話物件新增更多屬性。在以下示例中,我們將為客戶端建立一個頁面訪問計數器。

var express = require('express');
var cookieParser = require('cookie-parser');
var session = require('express-session');

var app = express();

app.use(cookieParser());
app.use(session({secret: "Shh, its a secret!"}));

app.get('/', function(req, res){
   if(req.session.page_views){
      req.session.page_views++;
      res.send("You visited this page " + req.session.page_views + " times");
   } else {
      req.session.page_views = 1;
      res.send("Welcome to this page for the first time!");
   }
});
app.listen(3000);

上面程式碼的作用是,當用戶訪問網站時,它會為使用者建立一個新會話併為其分配一個 Cookie。下次使用者訪問時,會檢查 Cookie 並相應地更新 page_view 會話變數。

現在,如果你執行應用程式並訪問 localhost:3000,將會顯示以下輸出。

First visit

如果你重新訪問頁面,頁面計數器會增加。以下螢幕截圖中的頁面重新整理了 42 次。

First visit

ExpressJS - 認證

身份驗證是一個過程,在這個過程中,提供的憑據與本地作業系統或身份驗證伺服器上授權使用者的資訊資料庫中的檔案進行比較。如果憑據匹配,則過程完成,並且使用者被授予訪問許可權。

為了建立身份驗證系統,我們需要建立一個註冊頁面和一個使用者密碼儲存。以下程式碼為我們建立了一個帳戶並將其儲存在記憶體中。這僅僅是為了演示的目的;建議始終使用永續性儲存(資料庫或檔案)來儲存使用者資訊。

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer(); 
var session = require('express-session');
var cookieParser = require('cookie-parser');

app.set('view engine', 'pug');
app.set('views','./views');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true })); 
app.use(upload.array());
app.use(cookieParser());
app.use(session({secret: "Your secret key"}));

var Users = [];

app.get('/signup', function(req, res){
   res.render('signup');
});

app.post('/signup', function(req, res){
   if(!req.body.id || !req.body.password){
      res.status("400");
      res.send("Invalid details!");
   } else {
      Users.filter(function(user){
         if(user.id === req.body.id){
            res.render('signup', {
               message: "User Already Exists! Login or choose another user id"});
         }
      });
      var newUser = {id: req.body.id, password: req.body.password};
      Users.push(newUser);
      req.session.user = newUser;
      res.redirect('/protected_page');
   }
});

app.listen(3000);

現在,對於登錄檔單,建立一個名為 signup.jade 的新檢視。

SIGNUP.JADE

html
   head
      title Signup
   body
      if(message)
         h4 #{message}
         form(action = "/signup" method = "POST")
         input(name = "id" type = "text" required placeholder = "User ID")
         input(name = "password" type = "password" required placeholder = "Password")
         button(type = "Submit") Sign me up!

透過訪問 localhost:3000/signup 檢查此頁面是否載入。

Signup form

我們已為這兩個欄位設定了 required 屬性,因此啟用了 HTML5 的瀏覽器在提供 ID 和密碼之前不會讓我們提交此表單。如果有人嘗試使用 curl 請求在沒有使用者 ID 或密碼的情況下進行註冊,則會顯示錯誤。在 views 中建立一個名為 protected_page.pug 的新檔案,內容如下:

html
   head
      title Protected page
   body
      div Hey #{id}, How are you doing today?
      div Want to log out?
      div Logout

此頁面僅在使用者剛剛註冊或登入時可見。現在讓我們定義其路由以及登入和登出的路由:

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer(); 
var session = require('express-session');
var cookieParser = require('cookie-parser');

app.set('view engine', 'pug');
app.set('views','./views');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true })); 
app.use(upload.array());
app.use(cookieParser());
app.use(session({secret: "Your secret key"}));

var Users = [];

app.get('/signup', function(req, res){
   res.render('signup');
});

app.post('/signup', function(req, res){
   if(!req.body.id || !req.body.password){
      res.status("400");
      res.send("Invalid details!");
   } else {
      Users.filter(function(user){
         if(user.id === req.body.id){
            res.render('signup', {
               message: "User Already Exists! Login or choose another user id"});
         }
      });
      var newUser = {id: req.body.id, password: req.body.password};
      Users.push(newUser);
      req.session.user = newUser;
      res.redirect('/protected_page');
   }
});
function checkSignIn(req, res){
   if(req.session.user){
      next();     //If session exists, proceed to page
   } else {
      var err = new Error("Not logged in!");
      console.log(req.session.user);
      next(err);  //Error, trying to access unauthorized page!
   }
}
app.get('/protected_page', checkSignIn, function(req, res){
   res.render('protected_page', {id: req.session.user.id})
});

app.get('/login', function(req, res){
   res.render('login');
});

app.post('/login', function(req, res){
   console.log(Users);
   if(!req.body.id || !req.body.password){
      res.render('login', {message: "Please enter both id and password"});
   } else {
      Users.filter(function(user){
         if(user.id === req.body.id && user.password === req.body.password){
            req.session.user = user;
            res.redirect('/protected_page');
         }
      });
      res.render('login', {message: "Invalid credentials!"});
   }
});

app.get('/logout', function(req, res){
   req.session.destroy(function(){
      console.log("user logged out.")
   });
   res.redirect('/login');
});

app.use('/protected_page', function(err, req, res, next){
console.log(err);
   //User should be authenticated! Redirect him to log in.
   res.redirect('/login');
});

app.listen(3000);

我們建立了一箇中間件函式 checkSignIn 來檢查使用者是否已登入。protected_page 使用此函式。要登出使用者,我們銷燬會話。

現在讓我們建立登入頁面。將檢視命名為 login.pug 並輸入以下內容:

html
   head
      title Signup
   body
      if(message)
         h4 #{message}
         form(action = "/login" method = "POST")
         input(name = "id" type = "text" required placeholder = "User ID")
         input(name = "password" type = "password" required placeholder = "Password")
         button(type = "Submit") Log in

我們簡單的身份驗證應用程式現在已經完成了;現在讓我們測試一下應用程式。使用 nodemon index.js 執行應用程式,然後繼續訪問 localhost:3000/signup。

輸入使用者名稱和密碼,然後點選註冊。如果詳細資訊有效/唯一,您將被重定向到 protected_page

Protected page

現在退出應用程式。這將把我們重定向到登入頁面:

Auth login

此路由受到保護,因此如果未經身份驗證的人員嘗試訪問它,則會將其重定向到我們的登入頁面。這就是關於基本使用者身份驗證的所有內容。始終建議我們使用永續性會話系統並使用雜湊進行密碼傳輸。現在有更好的方法來驗證使用者,利用 JSON 令牌。

ExpressJS - RESTful API

建立移動應用程式、單頁面應用程式、使用 AJAX 呼叫以及向客戶端提供資料始終需要 API。一種關於如何構建和命名這些 API 及其端點的流行架構風格稱為 REST(具象狀態傳輸)HTTP 1.1 在設計時就考慮了 REST 原則。REST 由 Roy Fielding 於 2000 年在其論文 Fielding Dissertations 中提出。

RESTful URI 和方法為我們提供了處理請求所需的大部分資訊。下表總結了如何使用各種動詞以及如何命名 URI。我們將在最後建立一個電影 API;現在讓我們討論一下它將如何構建。

方法 URI 詳情 功能
GET /movies 安全,可快取 獲取所有電影及其詳細資訊的列表
GET /movies/1234 安全,可快取 獲取電影 ID 1234 的詳細資訊
POST /movies N/A 使用提供的詳細資訊建立一個新電影。響應包含此新建立資源的 URI。
PUT /movies/1234 冪等 修改電影 ID 1234(如果不存在則建立一個)。響應包含此新建立資源的 URI。
DELETE /movies/1234 冪等 如果存在,應刪除電影 ID 1234。響應應包含請求的狀態。
DELETE 或 PUT /movies 無效 應為無效。DELETEPUT 應指定它們正在處理哪個資源。

現在讓我們在 Express 中建立此 API。我們將使用 JSON 作為我們的傳輸資料格式,因為它易於在 JavaScript 中使用並且具有其他優勢。將你的 index.js 檔案替換為以下程式中的 movies.js 檔案。

index.js

var express = require('express');
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer();

var app = express();

app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(upload.array());

//Require the Router we defined in movies.js
var movies = require('./movies.js');

//Use the Router on the sub route /movies
app.use('/movies', movies);

app.listen(3000);

現在我們已經設定了應用程式,讓我們專注於建立 API。

首先設定 movies.js 檔案。我們沒有使用資料庫來儲存電影,而是將它們儲存在記憶體中;因此,每次伺服器重啟時,我們新增的電影都會消失。這可以透過使用資料庫或檔案(使用 node fs 模組)輕鬆模擬。

匯入 Express 後,建立一個路由器並使用 module.exports 匯出它:

var express = require('express');
var router = express.Router();
var movies = [
   {id: 101, name: "Fight Club", year: 1999, rating: 8.1},
   {id: 102, name: "Inception", year: 2010, rating: 8.7},
   {id: 103, name: "The Dark Knight", year: 2008, rating: 9},
   {id: 104, name: "12 Angry Men", year: 1957, rating: 8.9}
];

//Routes will go here
module.exports = router;

GET 路由

讓我們定義獲取所有電影的 GET 路由:

router.get('/', function(req, res){
   res.json(movies);
});

要測試它是否正常工作,執行你的應用程式,然後開啟你的終端並輸入:

curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET 
localhost:3000/movies

將顯示以下響應:

[{"id":101,"name":"Fight Club","year":1999,"rating":8.1},
{"id":102,"name":"Inception","year":2010,"rating":8.7},
{"id":103,"name":"The Dark Knight","year":2008,"rating":9},
{"id":104,"name":"12 Angry Men","year":1957,"rating":8.9}]

我們有一個獲取所有電影的路由。現在讓我們建立一個透過其 ID 獲取特定電影的路由。

router.get('/:id([0-9]{3,})', function(req, res){
   var currMovie = movies.filter(function(movie){
      if(movie.id == req.params.id){
         return true;
      }
   });
   if(currMovie.length == 1){
      res.json(currMovie[0])
   } else {
      res.status(404);//Set status to 404 as movie was not found
      res.json({message: "Not Found"});
   }
});

這將根據我們提供的 ID 獲取電影。要檢查輸出,請在你的終端中使用以下命令:

curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET 
localhost:3000/movies/101

你將得到以下響應:

{"id":101,"name":"Fight Club","year":1999,"rating":8.1}

如果你訪問無效的路由,它會產生 無法獲取錯誤,而如果你訪問具有不存在 ID 的有效路由,它會產生 404 錯誤。

我們完成了 GET 路由,現在讓我們繼續 POST 路由。

POST 路由

使用以下路由處理 POST 的資料:

router.post('/', function(req, res){
   //Check if all fields are provided and are valid:
   if(!req.body.name ||
      !req.body.year.toString().match(/^[0-9]{4}$/g) ||
      !req.body.rating.toString().match(/^[0-9]\.[0-9]$/g)){
      
      res.status(400);
      res.json({message: "Bad Request"});
   } else {
      var newId = movies[movies.length-1].id+1;
      movies.push({
         id: newId,
         name: req.body.name,
         year: req.body.year,
         rating: req.body.rating
      });
      res.json({message: "New movie created.", location: "/movies/" + newId});
   }
});

這將建立一個新的電影並將其儲存在 movies 變數中。要檢查此路由,請在你的終端中輸入以下程式碼:

curl -X POST --data "name = Toy%20story&year = 1995&rating = 8.5" https://:3000/movies

將顯示以下響應:

{"message":"New movie created.","location":"/movies/105"}

要測試這是否已新增到 movies 物件中,請再次執行 /movies/105 的 get 請求。將顯示以下響應:

{"id":105,"name":"Toy story","year":"1995","rating":"8.5"}

讓我們繼續建立 PUT 和 DELETE 路由。

PUT 路由

PUT 路由與 POST 路由幾乎相同。我們將為要更新/建立的物件指定 ID。以以下方式建立路由。

router.put('/:id', function(req, res){
   //Check if all fields are provided and are valid:
   if(!req.body.name ||
      !req.body.year.toString().match(/^[0-9]{4}$/g) ||
      !req.body.rating.toString().match(/^[0-9]\.[0-9]$/g) ||
      !req.params.id.toString().match(/^[0-9]{3,}$/g)){
      
      res.status(400);
      res.json({message: "Bad Request"});
   } else {
      //Gets us the index of movie with given id.
      var updateIndex = movies.map(function(movie){
         return movie.id;
      }).indexOf(parseInt(req.params.id));
      
      if(updateIndex === -1){
         //Movie not found, create new
         movies.push({
            id: req.params.id,
            name: req.body.name,
            year: req.body.year,
            rating: req.body.rating
         });
         res.json({message: "New movie created.", location: "/movies/" + req.params.id});
      } else {
         //Update existing movie
         movies[updateIndex] = {
            id: req.params.id,
            name: req.body.name,
            year: req.body.year,
            rating: req.body.rating
         };
         res.json({message: "Movie id " + req.params.id + " updated.", 
            location: "/movies/" + req.params.id});
      }
   }
});

此路由將執行上表中指定的功能。如果物件存在,它將使用新詳細資訊更新物件。如果不存在,它將建立一個新物件。要檢查路由,請使用以下 curl 命令。這將更新現有的電影。要建立一個新電影,只需將 ID 更改為不存在的 ID。

curl -X PUT --data "name = Toy%20story&year = 1995&rating = 8.5" 
https://:3000/movies/101

響應

{"message":"Movie id 101 updated.","location":"/movies/101"}

DELETE 路由

使用以下程式碼建立一個刪除路由:

router.delete('/:id', function(req, res){
   var removeIndex = movies.map(function(movie){
      return movie.id;
   }).indexOf(req.params.id); //Gets us the index of movie with given id.
   
   if(removeIndex === -1){
      res.json({message: "Not found"});
   } else {
      movies.splice(removeIndex, 1);
      res.send({message: "Movie id " + req.params.id + " removed."});
   }
});

以與我們檢查其他路由相同的方式檢查路由。成功刪除(例如 ID 105)後,你將獲得以下輸出:

{message: "Movie id 105 removed."}

最後,我們的 movies.js 檔案將如下所示。

var express = require('express');
var router = express.Router();
var movies = [
   {id: 101, name: "Fight Club", year: 1999, rating: 8.1},
   {id: 102, name: "Inception", year: 2010, rating: 8.7},
   {id: 103, name: "The Dark Knight", year: 2008, rating: 9},
   {id: 104, name: "12 Angry Men", year: 1957, rating: 8.9}
];
router.get('/:id([0-9]{3,})', function(req, res){
   var currMovie = movies.filter(function(movie){
      if(movie.id == req.params.id){
         return true;
      }
   });
   
   if(currMovie.length == 1){
      res.json(currMovie[0])
   } else {
      res.status(404);  //Set status to 404 as movie was not found
      res.json({message: "Not Found"});
   }
});
router.post('/', function(req, res){
   //Check if all fields are provided and are valid:
   if(!req.body.name ||
      !req.body.year.toString().match(/^[0-9]{4}$/g) ||
      !req.body.rating.toString().match(/^[0-9]\.[0-9]$/g)){
      res.status(400);
      res.json({message: "Bad Request"});
   } else {
      var newId = movies[movies.length-1].id+1;
      movies.push({
         id: newId,
         name: req.body.name,
         year: req.body.year,
         rating: req.body.rating
      });
      res.json({message: "New movie created.", location: "/movies/" + newId});
   }
});

router.put('/:id', function(req, res) {
   //Check if all fields are provided and are valid:
   if(!req.body.name ||
      !req.body.year.toString().match(/^[0-9]{4}$/g) ||
      !req.body.rating.toString().match(/^[0-9]\.[0-9]$/g) ||
      !req.params.id.toString().match(/^[0-9]{3,}$/g)){
      res.status(400);
      res.json({message: "Bad Request"});
   } else {
      //Gets us the index of movie with given id.
      var updateIndex = movies.map(function(movie){
         return movie.id;
      }).indexOf(parseInt(req.params.id));
      
      if(updateIndex === -1){
         //Movie not found, create new
         movies.push({
            id: req.params.id,
            name: req.body.name,
            year: req.body.year,
            rating: req.body.rating
         });
         res.json({
            message: "New movie created.", location: "/movies/" + req.params.id});
      } else {
         //Update existing movie
         movies[updateIndex] = {
            id: req.params.id,
            name: req.body.name,
            year: req.body.year,
            rating: req.body.rating
         };
         res.json({message: "Movie id " + req.params.id + " updated.",
            location: "/movies/" + req.params.id});
      }
   }
});

router.delete('/:id', function(req, res){
   var removeIndex = movies.map(function(movie){
      return movie.id;
   }).indexOf(req.params.id); //Gets us the index of movie with given id.
   
   if(removeIndex === -1){
      res.json({message: "Not found"});
   } else {
      movies.splice(removeIndex, 1);
      res.send({message: "Movie id " + req.params.id + " removed."});
   }
});
module.exports = router;

這完成了我們的 REST API。現在你可以使用這種簡單的架構風格和 Express 建立更復雜的應用程式。

ExpressJS - 腳手架

腳手架使我們能夠輕鬆地建立 Web 應用程式的框架。我們手動建立我們的 public 目錄,新增中介軟體,建立單獨的路由檔案等。腳手架工具為我們設定了所有這些東西,以便我們可以直接開始構建我們的應用程式。

我們將使用的腳手架工具稱為 Yeoman。它是一個為 Node.js 構建的腳手架工具,但也為其他幾個框架(如 flask、rails、django 等)提供了生成器。要安裝 Yeoman,請在你的終端中輸入以下命令:

npm install -g yeoman

Yeoman 使用生成器來構建應用程式。要檢視 npm 上可用於 Yeoman 的生成器,你可以點選此 連結。在本教程中,我們將使用 'generator-Express-simple'。要安裝此生成器,請在你的終端中輸入以下命令:

npm install -g generator-express-simple

要使用此生成器,請輸入以下命令:

yo express-simple test-app

系統會詢問你一些簡單的問題,例如你想在你的應用程式中使用哪些功能。選擇以下答案,或者如果你已經瞭解這些技術,那麼可以選擇你希望它們如何使用。

express-simple comes with bootstrap and jquery
[?] Select the express version you want: 4.x
[?] Do you want an mvc express app: Yes
[?] Select the css preprocessor you would like to use: sass
[?] Select view engine you would like to use: jade
[?] Select the build tool you want to use for this project: gulp
[?] Select the build tool you want to use for this project: gulp
[?] Select the language you want to use for the build tool: javascript
   create public/sass/styles.scss
   create public/js/main.js
   create views/layout.jade
   create views/index.jade
   create views/404.jade
   create app.js
   create config.js
   create routes/index.js
   create package.json
   create bower.json
identical .bowerrc
identical .editorconfig
identical .gitignore
identical .jshintrc
   create gulpfile.js

I'm all done. Running bower install & npm install for you to install the
required dependencies. If this fails, try running the command yourself.

然後它將為你建立一個新的應用程式,安裝所有依賴項,向你的應用程式新增一些頁面(主頁、404 未找到頁面等)併為你提供一個目錄結構來進行操作。

此生成器為我們建立了一個非常簡單的結構。探索 Express 可用的眾多生成器,並選擇最適合你的那個。使用所有生成器的步驟相同。你需要安裝一個生成器,使用 Yeoman 執行它;它會問你一些問題,然後根據你的答案為你的應用程式建立一個框架。

ExpressJS - 錯誤處理

Express 中的錯誤處理是使用中介軟體完成的。但是此中介軟體具有特殊屬性。錯誤處理中介軟體的定義方式與其他中介軟體函式相同,只是錯誤處理函式 必須有四個引數而不是三個 - err, req, res, next。例如,要對任何錯誤傳送響應,我們可以使用:

app.use(function(err, req, res, next) {
   console.error(err.stack);
   res.status(500).send('Something broke!');
});

到目前為止,我們一直在路由本身處理錯誤。錯誤處理中介軟體允許我們分離錯誤邏輯並相應地傳送響應。我們在中介軟體中討論的 next() 方法將我們帶到下一個 中介軟體/路由處理程式

對於錯誤處理,我們有 next(err) 函式。對該函式的呼叫將跳過所有中介軟體,並使我們匹配到該路由的下一個錯誤處理程式。讓我們透過一個例子來理解這一點。

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

app.get('/', function(req, res){
   //Create an error and pass it to the next function
   var err = new Error("Something went wrong");
   next(err);
});

/*
 * other route handlers and middleware here
 * ....
 */

//An error handling middleware
app.use(function(err, req, res, next) {
   res.status(500);
   res.send("Oops, something went wrong.")
});

app.listen(3000);

此錯誤處理中介軟體可以策略性地放置在路由之後,或者包含檢測錯誤型別並相應地響應客戶端的條件。上述程式將顯示以下輸出。

Error handling

ExpressJS - 除錯

Express 使用 Debug 模組在內部記錄有關路由匹配、中介軟體函式、應用程式模式等的資訊。

要檢視 Express 中使用的所有內部日誌,在啟動應用程式時將 DEBUG 環境變數設定為 Express:*

DEBUG = express:* node index.js

將顯示以下輸出。

Debug log

當你的應用程式的某個元件無法正常工作時,這些日誌非常有用。此詳細輸出可能有點壓倒性。你還可以將 DEBUG 變數限制為要記錄的特定區域。例如,如果你希望將記錄器限制在應用程式和路由器中,可以使用以下程式碼。

DEBUG = express:application,express:router node index.js

預設情況下,除錯功能處於關閉狀態,並在生產環境中自動開啟。除錯功能還可以擴充套件以滿足你的需求,你可以在 其 npm 頁面 上閱讀更多相關資訊。

ExpressJS - 最佳實踐

與 Django 和 Rails 這些擁有明確開發規範(包括檔案結構等)的框架不同,Express 並沒有固定的開發方式。這意味著您可以根據自己的喜好構建應用程式的結構。但是,隨著應用程式規模的擴大,如果沒有一個明確定義的結構,維護起來將會非常困難。在本章中,我們將瞭解一些常用的目錄結構和關注點分離方法,以便構建我們的應用程式。

首先,我們將討論建立 Node 和 Express 應用程式的最佳實踐。

  • 始終使用 **npm init** 開始一個 Node 專案。

  • 始終使用 **--save** 或 **--save-dev** 安裝依賴項。這將確保如果您遷移到不同的平臺,只需執行 npm install 即可安裝所有依賴項。

  • 堅持使用小寫檔名和駝峰式變數命名。如果您檢視任何 npm 模組,其名稱都是小寫並用連字元分隔。當您需要這些模組時,請使用駝峰式命名法。

  • 不要將 node_modules 推送到您的程式碼倉庫。相反,npm 會在開發機器上安裝所有內容。

  • 使用 **config** 檔案儲存變數。

  • 將路由分組並隔離到它們自己的檔案中。例如,在 REST API 頁面中看到的電影示例中的 CRUD 操作。

目錄結構

現在讓我們討論 Express 的目錄結構。

網站

Express 沒有社群定義的應用程式建立結構。以下是網站常用的專案結構。

test-project/
   node_modules/
   config/
      db.js                //Database connection and configuration
      credentials.js       //Passwords/API keys for external services used by your app
      config.js            //Other environment variables
   models/                 //For mongoose schemas
      users.js
      things.js
   routes/                 //All routes for different entities in different files 
      users.js
      things.js
   views/
      index.pug
      404.pug
        ...
   public/                 //All static content being served
      images/
      css/
      javascript/
   app.js
   routes.js               //Require all routes in this and then require this file in 
   app.js 
   package.json

還有其他方法可以使用 Express 構建網站。您可以使用 MVC 設計模式構建網站。有關更多資訊,您可以訪問以下連結。

https://code.tutsplus.com/tutorials/build-a-complete-mvc-website-with-expressjs--net-34168

以及,

https://www.terlici.com/2014/08/25/best-practices-express-structure.html.

RESTful API

API 的設計更簡單,不需要公共目錄或檢視目錄。使用以下結構構建 API:

test-project/
   node_modules/
   config/
      db.js                //Database connection and configuration
      credentials.js       //Passwords/API keys for external services used by your app
   models/                 //For mongoose schemas
      users.js
      things.js
   routes/                 //All routes for different entities in different files 
      users.js
      things.js
   app.js
   routes.js               //Require all routes in this and then require this file in 
   app.js 
   package.json

您還可以使用 Yeoman 生成器 獲取類似的結構。

ExpressJS - 資源

本章列出了我們本教程中使用的各種資源。

廣告

© . All rights reserved.