diff --git a/files/zh-tw/learn/server-side/express_nodejs/deployment/index.md b/files/zh-tw/learn/server-side/express_nodejs/deployment/index.md index a72ebeee107266..c67fc16410e2de 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/deployment/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/deployment/index.md @@ -3,126 +3,112 @@ title: 'Express 教學 7: 佈署到生產環境' slug: Learn/Server-side/Express_Nodejs/deployment translation_of: Learn/Server-side/Express_Nodejs/deployment --- -
{{LearnSidebar}}
+{{LearnSidebar}}{{PreviousMenu("Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}} -
{{PreviousMenu("Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}
- -

現在你已經創建(並測試)了一個不錯的 本地圖書館 網站了,你打算把它發佈到一個公共網絡服務器,這樣圖書館管理員和網路上的其他成員就可以訪問它了。這篇文章總結了你可以怎樣找到一台主機部署你的網站,以及你需要為網站準備好佈署到生產環境該做什麼。

+現在你已經創建(並測試)了一個不錯的 本地圖書館 網站了,你打算把它發佈到一個公共網絡服務器,這樣圖書館管理員和網路上的其他成員就可以訪問它了。這篇文章總結了你可以怎樣找到一台主機部署你的網站,以及你需要為網站準備好佈署到生產環境該做什麼。 - - - - - - - - - - + + + + + + + + + +
預備知識:完成前面所有的指南主題,包括 Express Tutorial Part 6: Working with forms.
目標:學習你可以怎樣以及在哪裡部署一個 Express 應用到生產環境。
預備知識: + 完成前面所有的指南主題,包括 + Express Tutorial Part 6: Working with forms. +
目標:學習你可以怎樣以及在哪裡部署一個 Express 應用到生產環境。
-

概覽

+## 概覽 -

一旦您的站點完成(或完成 “足夠” 以開始公共測試),您將需要將其託管在比您的個人開發計算機,更公開和可訪問的地方。

+一旦您的站點完成(或完成 “足夠” 以開始公共測試),您將需要將其託管在比您的個人開發計算機,更公開和可訪問的地方。 -

到目前為止,您一直在開發環境中工作,使用Express / Node 作為 Web 服務器,將您的站點共享到本地瀏覽器/網路,並使用(不安全的)開發設置運行您的網站,以顯示調試和其他私人信息。在您可以在外部託管網站之前,您首先必須:

+到目前為止,您一直在開發環境中工作,使用 Express / Node 作為 Web 服務器,將您的站點共享到本地瀏覽器/網路,並使用(不安全的)開發設置運行您的網站,以顯示調試和其他私人信息。在您可以在外部託管網站之前,您首先必須: - +- 選擇託管 Express 應用程序的環境。 +- 對項目設置進行一些更改。 +- 設置生產級別的基礎架構,以服務您的網站。 -

本教程提供了,有關選擇託管站點的選項的一些指導,簡要概述了為使您的Express 應用程序準備好生產,所需執行的操作,以及一個工作示例,演示如何將 LocalLibrary 網站安裝到 Heroku 雲託管上的服務。

+本教程提供了,有關選擇託管站點的選項的一些指導,簡要概述了為使您的 Express 應用程序準備好生產,所需執行的操作,以及一個工作示例,演示如何將 LocalLibrary 網站安裝到 [Heroku](https://www.heroku.com/) 雲託管上的服務。 -

請記住,您不必使用 Heroku - 還有其他託管服務可用。我們還提供了一個單獨的教程,以展示如何在 PWS/Cloud Foundry 上安裝 LocalLibrary。

+請記住,您不必使用 Heroku - 還有其他託管服務可用。我們還提供了一個單獨的教程,以展示如何在 [PWS/Cloud Foundry ](/en-US/docs/Learn/Server-side/Express_Nodejs/Installing_on_PWS_Cloud_Foundry)上安裝 LocalLibrary。 -

什麼是生產環境?

+## 什麼是生產環境? -

生產環境是服務器計算機提供的環境,您可以在其中運行網站,以供外部使用。環境包括:

+生產環境是服務器計算機提供的環境,您可以在其中運行網站,以供外部使用。環境包括: - +- 網站運行的計算機硬件。 +- 操作系統(例如 Linux 或 Windows)。 +- 編程語言運行庫和框架庫,在其上編寫您的網站。 +- Web 服務器基礎結構,可能包含 Web 服務器,反向代理,負載平衡器等。 +- 您的網站所依賴的數據庫。 -

服務器計算機,可以位於您的場所,並通過快速鏈接,連接到 Internet,但使用 “託管在雲上” 的計算機更為常見。這實際上意味著,您的代碼運行在託管公司的數據中心的某台遠程計算機(或可能是“虛擬”計算機)。遠程服務器,通常會以特定價格提供互聯網連接,和一些保證級別的計算資源(例如CPU,RAM,存儲器等)。

+服務器計算機,可以位於您的場所,並通過快速鏈接,連接到 Internet,但使用 “託管在雲上” 的計算機更為常見。這實際上意味著,您的代碼運行在託管公司的數據中心的某台遠程計算機(或可能是“虛擬”計算機)。遠程服務器,通常會以特定價格提供互聯網連接,和一些保證級別的計算資源(例如 CPU,RAM,存儲器等)。 -

這種可遠程訪問的計算/網絡硬件,稱為基礎架構即服務(IaaS)。許多 IaaS 供應商,提供預安裝特定操作系統的選項,您必須在其上,安裝生產環境的其他組件。其他供應商,允許您選擇功能更全面的環境,可能包括完整的 node 設置。

+這種可遠程訪問的計算/網絡硬件,稱為基礎架構即服務(IaaS)。許多 IaaS 供應商,提供預安裝特定操作系統的選項,您必須在其上,安裝生產環境的其他組件。其他供應商,允許您選擇功能更全面的環境,可能包括完整的 node 設置。 -
-

備註: 預構建環境,可以使您的網站設置變得非常簡單,因為它們會減少配置,但可用選項可能會限制您使用不熟悉的服務器(或其他組件),並且可能基於較舊版本的操作系統。通常最好自己安裝組件,以便獲得所需的組件,並且當您需要升級系統的某些部分時,您可以知道從哪裡開始!

-
+> **備註:** 預構建環境,可以使您的網站設置變得非常簡單,因為它們會減少配置,但可用選項可能會限制您使用不熟悉的服務器(或其他組件),並且可能基於較舊版本的操作系統。通常最好自己安裝組件,以便獲得所需的組件,並且當您需要升級系統的某些部分時,您可以知道從哪裡開始! -

其他託管服務提供商,支持 Express 作為平台即服務(PaaS)產品的一部分。使用此類託管時,您無需擔心大多數生產環境(服務器,負載平衡器等),因為主機平台會為您處理這些問題。這使得部署非常簡單,因為您只需要專注於 Web 應用程序,而不是任何其他服務器基礎結構。

+其他託管服務提供商,支持 Express 作為平台即服務(PaaS)產品的一部分。使用此類託管時,您無需擔心大多數生產環境(服務器,負載平衡器等),因為主機平台會為您處理這些問題。這使得部署非常簡單,因為您只需要專注於 Web 應用程序,而不是任何其他服務器基礎結構。 -

一些開發人員選擇 IaaS ,相對於 PaaS ,IaaS 提供更高靈活性,而其他開發人員偏好 PaaS 的降低維護開銷,和更輕鬆的擴展性。當您在一開始使用時,在 PaaS 系統上設置您的網站,要容易得多,因此我們將在本教程中使用 PaaS。

+一些開發人員選擇 IaaS ,相對於 PaaS ,IaaS 提供更高靈活性,而其他開發人員偏好 PaaS 的降低維護開銷,和更輕鬆的擴展性。當您在一開始使用時,在 PaaS 系統上設置您的網站,要容易得多,因此我們將在本教程中使用 PaaS。 -
-

備註: 如果您選擇 Node/Express 友好的託管服務提供商,他們應該提供,有關如何使用 Web 服務器,應用程序服務器,反向代理等不同配置,來設置 Express 網站的說明。例如,在 Digital Ocean 的node 社區文檔中,有許多各種配置的手把手指南。

-
+> **備註:** 如果您選擇 Node/Express 友好的託管服務提供商,他們應該提供,有關如何使用 Web 服務器,應用程序服務器,反向代理等不同配置,來設置 Express 網站的說明。例如,在[ Digital Ocean](https://www.digitalocean.com/community/tutorials?q=node) 的 node 社區文檔中,有許多各種配置的手把手指南。 -

選擇一個主機供應商

+## 選擇一個主機供應商 -

眾所周知,眾多託管服務提供商,都積極支持或與 Node(和Express)合作。這些供應商提供不同類型的環境(IaaS,PaaS),以及不同價格的不同級別的計算和網絡資源。

+眾所周知,眾多託管服務提供商,都積極支持或與 Node(和 Express)合作。這些供應商提供不同類型的環境(IaaS,PaaS),以及不同價格的不同級別的計算和網絡資源。 -
-

備註: 有很多託管解決方案,他們的服務和定價,可能會隨著時間而改變。雖然我們在下面介紹幾個選項,但在選擇託管服務提供商之前,有必要自己進行互聯網搜索。

-
+> **備註:** 有很多託管解決方案,他們的服務和定價,可能會隨著時間而改變。雖然我們在下面介紹幾個選項,但在選擇託管服務提供商之前,有必要自己進行互聯網搜索。 -

選擇主機時需要考慮的一些事項:

+選擇主機時需要考慮的一些事項: - +- 您的網站可能有多忙,以及滿足該需求所需的數據,和計算資源的成本。 +- 水平擴展(添加更多機器)和垂直擴展(升級到更強大的機器)的支持級別,以及這樣做的成本。 +- 供應商有數據中心的地方,因此訪問可能是最快的。 +- 主機正常運行時間和停機時間的歷史表現。 +- 用於管理站點的工具 - 易於使用且安全(例如 SFTP 與 FTP)。 +- 用於監控服務器的內置框架。 +- 已知限制。有些主機會故意阻止某些服務(例如電子郵件)。其他在某些價格層中,僅提供一定數小時的 “實時時間”,或者僅提供少量存儲空間。 +- 額外的好處。一些提供商將提供免費域名和 SSL 證書支持,否則您將不得不為此另外支付費用。 +- 您所依賴的“免費”等級,是否會隨著時間的推移而過期,以及遷移到更昂貴等級的成本,是否意味著您最好在一開始就使用其他服務! -

當你剛開始時,好消息是有很多網站提供“免費”的計算環境,儘管有一些條件。例如, Heroku “永遠” 提供免費但資源有限的 PaaS 環境,而 Amazon Web Services, Microsoft Azure 和開源選項 PWS/Cloud Foundry 在您第一次加入時,提供免費信用額度。

+當你剛開始時,好消息是有很多網站提供“免費”的計算環境,儘管有一些條件。例如, [Heroku](https://www.heroku.com/) “永遠” 提供免費但資源有限的 PaaS 環境,而 [Amazon Web Services](http://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/billing-free-tier.html), [Microsoft Azure](https://azure.microsoft.com/en-us/pricing/details/app-service/) 和開源選項 [PWS/Cloud Foundry](/en-US/docs/Learn/Server-side/Express_Nodejs/Installing_on_PWS_Cloud_Foundry) 在您第一次加入時,提供免費信用額度。 -

許多提供商還擁有“基本”層,可提供更多有用的計算能力,和更少的限制。舉例來說, Digital Ocean 是一個流行的託管服務提供商,它提供了一個相對便宜的基本計算層(在本教程寫作時,是每月5美元的較低範圍)。

+許多提供商還擁有“基本”層,可提供更多有用的計算能力,和更少的限制。舉例來說, [Digital Ocean](https://www.digitalocean.com/) 是一個流行的託管服務提供商,它提供了一個相對便宜的基本計算層(在本教程寫作時,是每月 5 美元的較低範圍)。 -
-

備註: 請記住,價格不是唯一的選擇標準。如果您的網站成功,可能會發現可擴展性是最重要的考慮因素。

-
+> **備註:** 請記住,價格不是唯一的選擇標準。如果您的網站成功,可能會發現可擴展性是最重要的考慮因素。 -

準備好發布你的網站

+## 準備好發布你的網站 -

發佈網站時,要考慮的主要問題是網絡安全性和性能。至少,您需要刪除開發期間,錯誤頁面上包含的堆棧跟踪,整理日誌記錄,並設置適當的標頭,以避免許多常見的安全威脅。

+發佈網站時,要考慮的主要問題是網絡安全性和性能。至少,您需要刪除開發期間,錯誤頁面上包含的堆棧跟踪,整理日誌記錄,並設置適當的標頭,以避免許多常見的安全威脅。 -

在以下小節中,我們概述了您應該對應用進行的、最重要的更改。

+在以下小節中,我們概述了您應該對應用進行的、最重要的更改。 -
-

備註: Express文檔中還有其他有用的提示 - 請參閱“生產最佳實踐:性能和可靠性”,以及“生產最佳實踐:安全性”。

-
+> **備註:** Express 文檔中還有其他有用的提示 - 請參閱“[生產最佳實踐:性能和可靠性](https://expressjs.com/en/advanced/best-practice-performance.html)”,以及“[生產最佳實踐:安全性](https://expressjs.com/en/advanced/best-practice-security.html)”。 -

設置 NODE_ENV 為 'production'

+### 設置 NODE_ENV 為 'production' -

我們可以通過將 NODE_ENV 環境變量,設置為 production ,來刪除錯誤頁面中的堆棧跟踪(默認設置為 “development” )。除了生成較為不詳細的錯誤消息之外,還要將變量設置為生產緩存視圖模板,和從 CSS 擴展生成的 CSS 文件。測試表明,將NODE_ENV設置為生產,可以將應用程序性能提高三倍!

+我們可以通過將 `NODE_ENV` 環境變量,設置為 production ,來刪除錯誤頁面中的堆棧跟踪(默認設置為 “development” )。除了生成較為不詳細的錯誤消息之外,還要將變量設置為生產緩存視圖模板,和從 CSS 擴展生成的 CSS 文件。測試表明,將`NODE_ENV`設置為生產,可以將應用程序性能提高三倍! -

可以使用導出或環境文件,或使用 OS 初始化系統,以進行此更改。

+可以使用導出或環境文件,或使用 OS 初始化系統,以進行此更改。 -
-

備註: 這實際上是在環境設置,而不是應用中所做的更改,但重要的是,要注意這裡!我們將在下面,展示我們的託管示例要如何設置。

-
+> **備註:** 這實際上是在環境設置,而不是應用中所做的更改,但重要的是,要注意這裡!我們將在下面,展示我們的託管示例要如何設置。 -

Log appropriately

+### Log appropriately -

記錄呼叫會對高流量網站產生影響。在生產環境中,您可能需要記錄網站活動(例如,跟踪流量,或記錄API調用),但您應嘗試最小化為調試目的而添加的日誌記錄量。

+記錄呼叫會對高流量網站產生影響。在生產環境中,您可能需要記錄網站活動(例如,跟踪流量,或記錄 API 調用),但您應嘗試最小化為調試目的而添加的日誌記錄量。 -

在生產環境中,最小化“調試”日誌記錄的一種方法,是使用類似調試 debug 的模塊,允許您通過設置環境變量,來控制執行的日誌記錄。例如,下面的代碼片段,顯示如何設置 “author” 日誌記錄。調試變量使用名稱 “author” 聲明,並且將自動顯示,來自此對象的所有日誌的前綴 “author”。

+在生產環境中,最小化“調試”日誌記錄的一種方法,是使用類似調試 [debug ](https://www.npmjs.com/package/debug)的模塊,允許您通過設置環境變量,來控制執行的日誌記錄。例如,下面的代碼片段,顯示如何設置 “author” 日誌記錄。調試變量使用名稱 “author” 聲明,並且將自動顯示,來自此對象的所有日誌的前綴 “author”。 -
var debug = require('debug')('author');
+```js
+var debug = require('debug')('author');
 
 // Display Author update form on GET
 exports.author_update_get = function(req, res, next) {
@@ -130,49 +116,52 @@ exports.author_update_get = function(req, res, next) {
     req.sanitize('id').escape().trim();
     Author.findById(req.params.id, function(err, author) {
         if (err) {
-            debug('update error:' + err);
+            debug('update error:' + err);
             return next(err);
         }
         //On success
         res.render('author_form', { title: 'Update Author', author: author });
     });
 
-};
+}; +``` -

然後,您可以通過在 DEBUG 環境變量中,將它們指定為逗號分隔列表,來啟用特定日誌集。您可以設置顯示作者和書籍日誌的變量,如圖所示(也支持通配符)。

+然後,您可以通過在 `DEBUG `環境變量中,將它們指定為逗號分隔列表,來啟用特定日誌集。您可以設置顯示作者和書籍日誌的變量,如圖所示(也支持通配符)。 -
#Windows
+```bash
+#Windows
 set DEBUG=author,book
 
 #Linux
 export DEBUG="author,book"
-
+``` -
-

備註: 調用debug可以替換您以前使用 console.log()console.error()執行的日誌記錄。通過調試模塊 debug 進行日誌記錄,替換代碼中的所有console.log()調用。通過設置 DEBUG 變量,並在其中記錄對日誌記錄的影響,在開發環境中,打開和關閉日誌記錄。

-
+> **備註:** 調用`debug`可以替換您以前使用 `console.log()`或`console.error()`執行的日誌記錄。通過調試模塊 [debug ](https://www.npmjs.com/package/debug)進行日誌記錄,替換代碼中的所有`console.log()`調用。通過設置 DEBUG 變量,並在其中記錄對日誌記錄的影響,在開發環境中,打開和關閉日誌記錄。 -

如果您需要記錄網站活動,可以使用 Winston 或 Bunyan 等日誌庫。有關此主題的更多信息,請參閱:生產最佳實踐:性能和可靠性

+如果您需要記錄網站活動,可以使用 Winston 或 Bunyan 等日誌庫。有關此主題的更多信息,請參閱:[生產最佳實踐:性能和可靠性](https://expressjs.com/en/advanced/best-practice-performance.html)。 -

使用 gzip/deflate 壓縮響應

+### 使用 gzip/deflate 壓縮響應 -

Web 服務器,通常可以壓縮發送回客戶端的 HTTP 響應,從而顯著減少客戶端獲取和加載頁面所需的時間。使用的壓縮方法,取決於客戶端在請求中支持的解壓縮方法(如果不支持壓縮方法,則響應將以未壓縮的方式發送)。

+Web 服務器,通常可以壓縮發送回客戶端的 HTTP 響應,從而顯著減少客戶端獲取和加載頁面所需的時間。使用的壓縮方法,取決於客戶端在請求中支持的解壓縮方法(如果不支持壓縮方法,則響應將以未壓縮的方式發送)。 -

您可以使用壓縮中間件 compression,將其添加到您的站點。通過在項目的根目錄下,運行以下命令,將其安裝到項目中。

+您可以使用壓縮中間件 [compression](https://www.npmjs.com/package/compression),將其添加到您的站點。通過在項目的根目錄下,運行以下命令,將其安裝到項目中。 -
npm install compression
+```bash +npm install compression +``` -

打開./app.js,並導入壓縮庫,如圖所示。使用 use()方法,將壓縮庫添加到中間件鏈(這應該出現在您想要壓縮的任何路由之前 - 在本教程這種情況下,全部都是!)

+打開**./app.js**,並導入壓縮庫,如圖所示。使用` use()`方法,將壓縮庫添加到中間件鏈(這應該出現在您想要壓縮的任何路由之前 - 在本教程這種情況下,全部都是!) -
var catalogRouter = require('./routes/catalog'); //Import routes for "catalog" area of site
-var compression = require('compression');
+```js
+var catalogRouter = require('./routes/catalog'); //Import routes for "catalog" area of site
+var compression = require('compression');
 
 // Create the Express application object
 var app = express();
 
 ...
 
-app.use(compression()); //Compress all routes
+app.use(compression()); //Compress all routes
 
 app.use(express.static(path.join(__dirname, 'public')));
 
@@ -181,341 +170,339 @@ app.use('/users', usersRouter);
 app.use('/catalog', catalogRouter);  // Add catalog routes to middleware chain.
 
 ...
-
+``` + +> **備註:** 對於生產中流量較大的網站,您不會使用此中間件。相反,你會使用像 Nginx 這樣的反向代理。 -
-

備註: 對於生產中流量較大的網站,您不會使用此中間件。相反,你會使用像 Nginx 這樣的反向代理。

-
+### 使用 Helmet 避免被常見漏洞侵襲 -

使用 Helmet 避免被常見漏洞侵襲

+[Helmet](https://www.npmjs.com/package/helmet) 是一個中間件包,可以通過設置適當的 HTTP 標頭,來幫助保護您的應用,免受一些眾所周知的 Web 漏洞的影響(有關它設置的標頭/防護漏洞的詳細信息,請參閱文檔 [docs](https://helmetjs.github.io/docs/)) 。 -

Helmet 是一個中間件包,可以通過設置適當的 HTTP 標頭,來幫助保護您的應用,免受一些眾所周知的 Web 漏洞的影響(有關它設置的標頭/防護漏洞的詳細信息,請參閱文檔 docs) 。

+通過在項目的根目錄下,運行以下命令,將其安裝到項目中。 -

通過在項目的根目錄下,運行以下命令,將其安裝到項目中。

+```bash +npm install helmet +``` -
npm install helmet
-
+打開**./app.js**,並導入如圖所示的 helmet 庫。然後使用`use()`方法,將模塊添加到中間件鏈。 -

打開./app.js,並導入如圖所示的 helmet 庫。然後使用use()方法,將模塊添加到中間件鏈。

+```js +var compression = require('compression'); +var helmet = require('helmet'); -
var compression = require('compression');
-var helmet = require('helmet');
-
 // Create the Express application object
 var app = express();
 
-app.use(helmet());
-...
+app.use(helmet()); +... +``` + +> **備註:** 上面的命令,添加了對大多數站點有意義的可用標頭子集。您可以按照 [npm ](https://www.npmjs.com/package/helmet)上的說明,根據需要添加/禁用特定標頭。 + +## 例子:在 Heroku 上安裝本地圖書館 + +本節提供如何在 [Heroku PaaS cloud](http://heroku.com) 雲上安裝 LocalLibrary 的實際演示。 + +### 為什麼選擇 Heroku? + +Heroku 是運行時間最長,且最受歡迎的基於雲的 PaaS 服務之一。它最初只支持 Ruby 應用程序,但現在可用於託管來自許多編程環境的應用程序,包括 Node(以及 Express)! + +我們選擇使用 Heroku 有以下幾個原因: + +- Heroku 有一個免費套餐 [free tier](https://www.heroku.com/pricing)(儘管有一些限制)。 +- 作為 PaaS,Heroku 為我們提供了大量的 Web 基礎架構。這使得入門更加容易,因為您不必擔心服務器,負載平衡器,反向代理,崩潰時重新啟動網站,或者 Heroku 為我們提供的任何其他 Web 基礎結構。 +- 雖然它確實有一些限制,但這些不會影響這個特定的應用程序。例如: + + - Heroku 只提供短期存儲,因此用戶上傳的文件無法安全地存儲在 Heroku 本身。 + - 如果半小時內沒有請求,免費套餐將使不活動的網絡應用程序進入睡眠。然後,該網站可能需要幾秒鐘才能被喚醒。 + - 免費套餐將您網站運行的時間,限制為每月一定的小時數(不包括網站“睡著”的時間)。這對於低使用/演示站點來說很好,但如果需要 100%的正常運行時間,則不適用。 + - Heroku 官方文檔 [Limits ](https://devcenter.heroku.com/articles/limits)中列出的其他限制。 + +- 大多數情況下,它只是可以工作,如果你最終喜歡它,並希望升級,那麼擴展你的應用程序非常容易。 + +雖然 Heroku 非常適合舉辦此演示,但它可能並不適合您的真實網站。 Heroku 可以輕鬆設置和擴展,但代價是靈活性較低,而且一旦退 ​​​​ 出免費套餐,可能會花費更多。 + +### Heroku 如何工作? + +Heroku 在一個或多個 "[Dynos](https://devcenter.heroku.com/articles/dynos)" 中運行網站,這些 “Dynos” 是獨立的虛擬化 Unix 容器,提供運行應用程序所需的環境。 Dynos 是完全隔離的,並且有一個短暫的文件系統(一個短暫的文件系統,每次 dyno 重新啟動時都會清理/清空)。 dynos 默認共享的唯一內容,是應用程序配置變量 [configuration variables](https://devcenter.heroku.com/articles/config-vars)。 Heroku 內部使用負載均衡器,將 Web 流量分配給所有 “web” dynos。由於它們之間沒有任何共享,Heroku 可以通過添加更多 dynos,來水平擴展應用程序(當然,您可能還需要擴展數據庫,以接受其他連接)。 + +由於文件系統是短暫的,因此無法直接安裝應用程序所需的服務(例如數據庫,隊列,緩存系統,存儲,電子郵件服務等)。相反,Heroku Web 應用程序使用 Heroku 或第三方作為獨立“附加組件”提供的支持服務。連接到 Web 應用程序後,可以通過環境變量,在 Web 應用程序中訪問附加服務。 -
-

備註: 上面的命令,添加了對大多數站點有意義的可用標頭子集。您可以按照 npm 上的說明,根據需要添加/禁用特定標頭。

-
- -

例子:在 Heroku 上安裝本地圖書館

- -

本節提供如何在 Heroku PaaS cloud 雲上安裝 LocalLibrary 的實際演示。

- -

為什麼選擇 Heroku?

- -

Heroku 是運行時間最長,且最受歡迎的基於雲的 PaaS 服務之一。它最初只支持 Ruby 應用程序,但現在可用於託管來自許多編程環境的應用程序,包括 Node(以及Express)!

- -

我們選擇使用 Heroku 有以下幾個原因:

- - +為了執行您的應用程序,Heroku 需要能夠設置適當的環境和依賴關係,並了解它是如何啟動的。對於 Node 應用程序,它所需的所有信息都是從 **package.json**文件中獲取的。 -

雖然 Heroku 非常適合舉辦此演示,但它可能並不適合您的真實網站。 Heroku 可以輕鬆設置和擴展,但代價是靈活性較低,而且一旦退​​​​出免費套餐,可能會花費更多。

+開發人員使用特殊的客戶端應用程序/終端,與 Heroku 交互,這很像 Unix bash 腳本。這允許您上傳存儲在 git 儲存庫中的代碼,檢查正在運行的進程,查看日誌,設置配置變量等等! -

Heroku 如何工作?

+為了讓我們的應用程序在 Heroku 上工作,我們需要將我們的 Express Web 應用程序放入 git 儲存庫,並對 package.json 進行一些小的更改。完成後,我們可以設置 Heroku 帳戶,獲取 Heroku 客戶端,並使用它來安裝我們的網站。 -

Heroku在一個或多個 "Dynos" 中運行網站,這些 “Dynos” 是獨立的虛擬化Unix容器,提供運行應用程序所需的環境。 Dynos 是完全隔離的,並且有一個短暫的文件系統(一個短暫的文件系統,每次dyno重新啟動時都會清理/清空)。 dynos 默認共享的唯一內容,是應用程序配置變量 configuration variables。 Heroku 內部使用負載均衡器,將Web流量分配給所有 “web” dynos。由於它們之間沒有任何共享,Heroku 可以通過添加更多 dynos,來水平擴展應用程序(當然,您可能還需要擴展數據庫,以接受其他連接)。

+這是您開始教程所需的全部概述(有關更全面的指南,請參閱帶有 Node.js 的[Heroku](https://devcenter.heroku.com/articles/getting-started-with-nodejs) 入門)。 -

由於文件系統是短暫的,因此無法直接安裝應用程序所需的服務(例如數據庫,隊列,緩存系統,存儲,電子郵件服務等)。相反,Heroku Web應用程序使用 Heroku 或第三方作為獨立“附加組件”提供的支持服務。連接到Web應用程序後,可以通過環境變量,在Web應用程序中訪問附加服務。

+### 在 Github 上創建一個應用倉庫 -

為了執行您的應用程序,Heroku 需要能夠設置適當的環境和依賴關係,並了解它是如何啟動的。對於 Node 應用程序,它所需的所有信息都是從 package.json文件中獲取的。

+Heroku 與 **git** 源代碼版本控制系統緊密集成,使用它來上傳/同步您對實時運行系統所做的任何更改。它通過添加一個名為 heroku 的新 Heroku“遠程”儲存庫,來指向您在 Heroku 雲上的源儲存庫。在開發期間,您使用 git 在“主”儲存庫 master 中儲存更改。如果要部署站點,請將更改同步到 Heroku 存儲庫。 -

開發人員使用特殊的客戶端應用程序/終端,與 Heroku 交互,這很像 Unix bash 腳本。這允許您上傳存儲在 git 儲存庫中的代碼,檢查正在運行的進程,查看日誌,設置配置變量等等!

+> **備註:** 如果您習慣於遵循良好的軟件開發實踐,那麼您可能已經在使用 git 或其他一些 SCM 系統。如果您已有 git 儲存庫,則可以跳過此步驟。 -

為了讓我們的應用程序在 Heroku 上工作,我們需要將我們的 Express Web 應用程序放入 git 儲存庫,並對 package.json 進行一些小的更改。完成後,我們可以設置Heroku 帳戶,獲取 Heroku 客戶端,並使用它來安裝我們的網站。

+有很多方法可以使用 git,但最簡單的方法之一,是首先在 [GitHub ](https://github.com/)上建立一個帳戶,在那裡創建儲存庫,然後在本地同步它: -

這是您開始教程所需的全部概述(有關更全面的指南,請參閱帶有 Node.js 的Heroku 入門)。

+1. 訪問 並創建一個帳戶。 +2. 登錄後,單擊頂部工具欄中的 + 號鏈接,然後選擇新建儲存庫 **New repository**。 +3. 填寫此表單上的所有字段。雖然這些不是強制性的,但強烈建議使用它們。 -

在 Github 上創建一個應用倉庫

+ - 輸入新的存儲庫名稱(例如,express-locallibrary-tutorial)和描述(例如 “以 Express(node)編寫的本地圖書館網站”)。 + - 在 Add .gitignore 選擇列表中選擇 **Node**。 + - 在添加許可證 Add license 選擇列表中,選擇您偏好的許可證。 + - 點選 使用自述文件初始化此儲存庫 **Initialize this repository with a README**. -

Heroku 與 git 源代碼版本控制系統緊密集成,使用它來上傳/同步您對實時運行系統所做的任何更改。它通過添加一個名為 heroku 的新 Heroku“遠程”儲存庫,來指向您在Heroku雲上的源儲存庫。在開發期間,您使用 git 在“主”儲存庫 master 中儲存更改。如果要部署站點,請將更改同步到 Heroku 存儲庫。

+4. 按 **Create repository**. +5. 單擊新倉庫頁面上的綠色“克隆或下載”按鈕 "**Clone or download**" 。 +6. 從顯示的對話框的文本字段,複製 URL 值(它應該類似於:**https\://github.com/_\_/express-locallibrary-tutorial.git**)。 -
-

備註: 如果您習慣於遵循良好的軟件開發實踐,那麼您可能已經在使用 git 或其他一些 SCM 系統。如果您已有 git 儲存庫,則可以跳過此步驟。

-
+現在創建了儲存庫(“repo”),我們將要在本地計算機上克隆它: -

有很多方法可以使用git,但最簡單的方法之一,是首先在 GitHub 上建立一個帳戶,在那裡創建儲存庫,然後在本地同步它:

+1. 為您的本地計算機安裝 git(您可以在[此處找到不同平台的版本](https://git-scm.com/downloads))。 -
    -
  1. 訪問 https://github.com/ 並創建一個帳戶。
  2. -
  3. 登錄後,單擊頂部工具欄中的 + 號鏈接,然後選擇新建儲存庫 New repository
  4. -
  5. 填寫此表單上的所有字段。雖然這些不是強制性的,但強烈建議使用它們。 -
      -
    • 輸入新的存儲庫名稱(例如,express-locallibrary-tutorial)和描述(例如 “以Express(node)編寫的本地圖書館網站”)。
    • -
    • 在 Add .gitignore 選擇列表中選擇 Node
    • -
    • 在添加許可證 Add license 選擇列表中,選擇您偏好的許可證。
    • -
    • 點選 使用自述文件初始化此儲存庫 Initialize this repository with a README.
    • -
    -
  6. -
  7. Create repository.
  8. -
  9. 單擊新倉庫頁面上的綠色“克隆或下載”按鈕 "Clone or download" 。
  10. -
  11. 從顯示的對話框的文本字段,複製 URL值(它應該類似於:https://github.com/<your_git_user_id>/express-locallibrary-tutorial.git)。
  12. -
+2. 打開命令提示符/終端,並使用您在上面複製的 URL ,克隆儲存庫: -

現在創建了儲存庫(“repo”),我們將要在本地計算機上克隆它:

+ ```bash + git clone https://github.com//express-locallibrary-tutorial.git + ``` -
    -
  1. 為您的本地計算機安裝 git(您可以在此處找到不同平台的版本)。
    -
  2. -
  3. 打開命令提示符/終端,並使用您在上面複製的 URL ,克隆儲存庫: -
    git clone https://github.com/<your_git_user_id>/express-locallibrary-tutorial.git
    -
    - 這將在當前時間點之後,創建儲存庫。
  4. -
  5. 到新的儲存庫。 -
    cd express-locallibrary-tutorial
    -
  6. -
+ 這將在當前時間點之後,創建儲存庫。 -

最後一步,是複制你的應用程序,然後使用 git ,將文件添加到你的倉庫:

+3. 到新的儲存庫。 -
    -
  1. 將Express應用程序,複製到此文件夾中(不包括 /node_modules,其中包含您應根據需要,從 NPM 獲取的依賴項文件)。
  2. -
  3. 打開命令提示符/終端,並使用 add 命令,將所有文件添加到 git。
  4. -
  5. -
    git add -A
    -
    -
  6. -
  7. 使用 status 命令,檢查要添加的所有文件是否正確(您希望包含源文件,而不是二進製文件,臨時文件等)。它應該看起來有點像下面的列表。 -
    > git status
    -On branch master
    -Your branch is up-to-date with 'origin/master'.
    -Changes to be committed:
    -  (use "git reset HEAD <file>..." to unstage)
    +    ```bash
    +    cd express-locallibrary-tutorial
    +    ```
     
    -        new file:   ...
    -
  8. -
  9. 如果您滿意,請將文件提交到本地儲存庫: -
    git commit -m "First version of application moved into github"
    -
  10. -
  11. 然後使用以下內容,將本地儲存庫同步到 Github 網站: -
    git push origin master
    -
  12. -
+最後一步,是複制你的應用程序,然後使用 git ,將文件添加到你的倉庫: -

完成此操作後,您應該可以返回創建儲存庫的 Github 上的頁面,刷新頁面,並查看您的整個應用程序現已上傳。使用此添加/提交/推送循環,您可以在文件更改時,繼續更新儲存庫。

+1. 將 Express 應用程序,複製到此文件夾中(不包括 **/node_modules**,其中包含您應根據需要,從 NPM 獲取的依賴項文件)。 +2. 打開命令提示符/終端,並使用 `add `命令,將所有文件添加到 git。 +3. ```bash + git add -A + ``` +4. 使用 status 命令,檢查要添加的所有文件是否正確(您希望包含源文件,而不是二進製文件,臨時文件等)。它應該看起來有點像下面的列表。 -
-

備註: 這是備份你的“vanilla”項目的好時機 - 雖然我們將在以下部分中進行的一些更改,可能對任何平台(或開發)上的部署有用,而一些其他的更改可能沒有用。

+ ```plain + > git status + On branch master + Your branch is up-to-date with 'origin/master'. + Changes to be committed: + (use "git reset HEAD ..." to unstage) -

執行此操作的最佳方法,是使用 git 來管理您的修訂。使用 git,您不僅可以回到特定的舊版本,而且可以在生產變更的單獨“分支”中進行維護,並選擇在生產和開發分支之間移動的任何更改。學習Git非常值得,但超出了本主題的範圍。

+ new file: ... + ``` -

最簡單的方法,是將文件複製到另一個位置。以您對 git 了解,使用最符合的方法!

-
+5. 如果您滿意,請將文件提交到本地儲存庫: -

更新 Heroku 的應用程序

+ ```bash + git commit -m "First version of application moved into github" + ``` -

本節介紹了您需要對 LocalLibrary 應用程序進行的更改,以使其在 Heroku 上運行。

+6. 然後使用以下內容,將本地儲存庫同步到 Github 網站: -

設置 node 版本

+ ```plain + git push origin master + ``` -

package.json 包含解決應用程序依賴項所需的所有內容,以及啟動站點時,應啟動的文件。 Heroku 檢測到此文件的存在,並將使用它來配置您的應用程序環境。

+完成此操作後,您應該可以返回創建儲存庫的 Github 上的頁面,刷新頁面,並查看您的整個應用程序現已上傳。使用此添加/提交/推送循環,您可以在文件更改時,繼續更新儲存庫。 -

我們當前的 package.json 中,缺少的唯一有用信息,是 node 的版本。我們可以通過輸入命令,找到我們用於開發的 node 版本:

+> **備註:** 這是備份你的“vanilla”項目的好時機 - 雖然我們將在以下部分中進行的一些更改,可能對任何平台(或開發)上的部署有用,而一些其他的更改可能沒有用。 +> +> 執行此操作的最佳方法,是使用 git 來管理您的修訂。使用 git,您不僅可以回到特定的舊版本,而且可以在生產變更的單獨“分支”中進行維護,並選擇在生產和開發分支之間移動的任何更改。[學習 Git](https://help.github.com/articles/good-resources-for-learning-git-and-github/)非常值得,但超出了本主題的範圍。 +> +> 最簡單的方法,是將文件複製到另一個位置。以您對 git 了解,使用最符合的方法! -
>node --version
-v8.9.1
+### 更新 Heroku 的應用程序 -

打開 package.json,並將此信息添加為 engines > node 部分,如圖所示(使用系統的版本號)。

+本節介紹了您需要對 LocalLibrary 應用程序進行的更改,以使其在 Heroku 上運行。 -
{
+#### 設置 node 版本
+
+**package.json** 包含解決應用程序依賴項所需的所有內容,以及啟動站點時,應啟動的文件。 Heroku 檢測到此文件的存在,並將使用它來配置您的應用程序環境。
+
+我們當前的 **package.json** 中,缺少的唯一有用信息,是 node 的版本。我們可以通過輸入命令,找到我們用於開發的 node 版本:
+
+```bash
+>node --version
+v8.9.1
+```
+
+打開 **package.json**,並將此信息添加為 **engines > node** 部分,如圖所示(使用系統的版本號)。
+
+```json
+{
   "name": "express-locallibrary-tutorial",
   "version": "0.0.0",
-  "engines": {
+  "engines": {
     "node": "8.9.1"
-  },
+  },
   "private": true,
   ...
-
+``` -

數據庫配置

+#### 數據庫配置 -

到目前為止,在本教程中,我們使用了一個硬編碼到 app.js 的單個數據庫。通常我們希望,能夠為生產和開發創建不同的數據庫,接下來我們將修改 LocalLibrary 網站,以從 OS 環境獲取數據庫 URI(如果已定義),否則使用我們的開發數據庫。

+到目前為止,在本教程中,我們使用了一個硬編碼到 **app.js** 的單個數據庫。通常我們希望,能夠為生產和開發創建不同的數據庫,接下來我們將修改 LocalLibrary 網站,以從 OS 環境獲取數據庫 URI(如果已定義),否則使用我們的開發數據庫。 -

打開 app.js,並找到設置 mongoDB 連接變量的行。它看起來像這樣:

+打開 **app.js**,並找到設置 mongoDB 連接變量的行。它看起來像這樣: -
var mongoDB = 'mongodb://your_user_id:your_password@ds119748.mlab.com:19748/local_library';
+```js +var mongoDB = 'mongodb://your_user_id:your_password@ds119748.mlab.com:19748/local_library'; +``` -

使用以下代碼替換該行,該代碼使用 process.env.MONGODB_URI 從名為 MONGODB_URI 的環境變量中,獲取連接字符串(如果已設置)(使用您自己的數據庫URL,而不是下面的佔位符。)

+使用以下代碼替換該行,該代碼使用 `process.env.MONGODB_URI `從名為 `MONGODB_URI `的環境變量中,獲取連接字符串(如果已設置)(使用您自己的數據庫 URL,而不是下面的佔位符。) -
var mongoDB = process.env.MONGODB_URI || 'mongodb://your_user_id:your_password@ds119748.mlab.com:19748/local_library';
-
+```js +var mongoDB = process.env.MONGODB_URI || 'mongodb://your_user_id:your_password@ds119748.mlab.com:19748/local_library'; +``` -

安裝依賴並重新測試

+#### 安裝依賴並重新測試 -

在我們繼續之前,讓我們再次測試該網站,並確保它不受我們的任何更改的影響。

+在我們繼續之前,讓我們再次測試該網站,並確保它不受我們的任何更改的影響。 -

首先,我們需要獲取我們的依賴項(你會記得,我們​​沒有將 node_modules文件夾,複製到我們的 git 樹中)。您可以通過在項目根目錄的終端中,運行以下命令來執行此操作:

+首先,我們需要獲取我們的依賴項(你會記得,我們 ​​ 沒有將 **node_modules**文件夾,複製到我們的 git 樹中)。您可以通過在項目根目錄的終端中,運行以下命令來執行此操作: -
npm install
-
+```bash +npm install +``` -

現在運行該站點(請參閱測試路由的相關命令),並檢查該站點,是否仍按預期運行。

+現在運行該站點(請參閱[測試路由](/zh-TW/docs/Learn/Server-side/Express_Nodejs/routes#Testing_the_routes)的相關命令),並檢查該站點,是否仍按預期運行。 -

將更改保存到 Github

+#### 將更改保存到 Github -

接下來,讓我們將所有更改保存到 Github。在終端中(在我們的儲存庫中),輸入以下命令:

+接下來,讓我們將所有更改保存到 Github。在終端中(在我們的儲存庫中),輸入以下命令: -
git add -A
+```bash
+git add -A
 git commit -m "Added files and changes required for deployment to heroku"
-git push origin master
+git push origin master +``` -

我們現在應該準備開始在 Heroku 上,部署 LocalLibrary。

+我們現在應該準備開始在 Heroku 上,部署 LocalLibrary。 -

獲取一個 Heroku 帳戶

+### 獲取一個 Heroku 帳戶 -

要開始使用 Heroku,您首先需要創建一個帳戶(如果您已經擁有一個帳戶,並安裝了 Heroku 客戶端,請跳過創建並上傳網站):

+要開始使用 Heroku,您首先需要創建一個帳戶(如果您已經擁有一個帳戶,並安裝了 Heroku 客戶端,請跳過創建並上傳網站): -
    -
  • 訪問 www.heroku.com 並單擊免費註冊按鈕 SIGN UP FOR FREE
  • -
  • 輸入您的詳細信息,然後按 CREATE FREE ACCOUNT。系統會要求您,檢查帳戶中是否有註冊電子郵件。
  • -
  • 單擊註冊電子郵件中的帳戶激活鏈接。您將在網絡瀏覽器上收回您的帳戶。
  • -
  • 輸入您的密碼,然後單擊 SET PASSWORD AND LOGIN.
  • -
  • 然後,您將登錄並進入Heroku儀表板: https://dashboard.heroku.com/apps.
  • -
+- 訪問 [www.heroku.com](https://www.heroku.com/) 並單擊免費註冊按鈕 **SIGN UP FOR FREE** +- 輸入您的詳細信息,然後按 **CREATE FREE ACCOUNT**。系統會要求您,檢查帳戶中是否有註冊電子郵件。 +- 單擊註冊電子郵件中的帳戶激活鏈接。您將在網絡瀏覽器上收回您的帳戶。 +- 輸入您的密碼,然後單擊 **SET PASSWORD AND LOGIN**. +- 然後,您將登錄並進入 Heroku 儀表板: . -

安裝客戶端

+### 安裝客戶端 -

按照 Heroku 上的說明,下載並安裝 Heroku 客戶端。

+按照 [Heroku 上的說明](https://devcenter.heroku.com/articles/getting-started-with-python#set-up),下載並安裝 Heroku 客戶端。 -

安裝客戶端后,您將能夠運行命令。例如,要獲得客戶端的幫助說明:

+安裝客戶端后,您將能夠運行命令。例如,要獲得客戶端的幫助說明: -
heroku help
-
+```bash +heroku help +``` -

創建並上傳網站

+### 創建並上傳網站 -

要創建應用程序,我們在儲存庫的根目錄中,運行 “create” 命令。這將在我們的本地git 環境中,創建一個名為 heroku 的 git remote(“指向遠程儲存庫的指針”)。

+要創建應用程序,我們在儲存庫的根目錄中,運行 “create” 命令。這將在我們的本地 git 環境中,創建一個名為 heroku 的 git remote(“指向遠程儲存庫的指針”)。 -
heroku create
+```bash +heroku create +``` -
-

備註: 如果您願意,可以在“創建”create 之後指定遠程儲存庫的命名。如果你不這樣做,你會得到一個隨機的名字。該名稱用於默認 URL。

-
+> **備註:** 如果您願意,可以在“創建”create 之後指定遠程儲存庫的命名。如果你不這樣做,你會得到一個隨機的名字。該名稱用於默認 URL。 -

然後,我們可以將我們的應用程序,推送到 Heroku 儲存庫,如下所示。這將上傳應用程序,獲取所有依賴項,將其打包到 dyno 中,然後啟動該站點。

+然後,我們可以將我們的應用程序,推送到 Heroku 儲存庫,如下所示。這將上傳應用程序,獲取所有依賴項,將其打包到 dyno 中,然後啟動該站點。 -
git push heroku master
+```bash +git push heroku master +``` -

如果我們很幸運,該應用程序現在正在網站上“運行”。要打開瀏覽器並運行新網站,請使用以下命令:

+如果我們很幸運,該應用程序現在正在網站上“運行”。要打開瀏覽器並運行新網站,請使用以下命令: -
heroku open
+```bash +heroku open +``` -
-

備註: 該站點將使用我們的開發數據庫運行。創建一些書本和其他對象,並檢查該網站是否按預期運行。在下一節中,我們將其設置為使用我們的新數據庫。

-
+> **備註:** 該站點將使用我們的開發數據庫運行。創建一些書本和其他對象,並檢查該網站是否按預期運行。在下一節中,我們將其設置為使用我們的新數據庫。 -

設定配置變量

+### 設定配置變量 -

您將從前一節回憶起,我們需要將 NODE_ENV 設置為 'production',以便提高性能,並生成更簡潔的錯誤消息。我們通過輸入以下命令,來完成此操作:

+您將從前一節回憶起,我們需要將 NODE_ENV 設置為 'production',以便提高性能,並生成更簡潔的錯誤消息。我們通過輸入以下命令,來完成此操作: -
>heroku config:set NODE_ENV='production'
+```bash
+>heroku config:set NODE_ENV='production'
 Setting NODE_ENV and restarting limitless-tor-18923... done, v13
 NODE_ENV: production
-
+``` -

我們還應該使用單獨的數據庫進行生產,在MONGODB_URI環境變量中,設置其URI。您可以完全按照我們原來的方式,設置新數據庫和數據庫用戶,並獲取其URI。您可以如下圖所示設置URI(顯然,要使用您自己的URI!)

+我們還應該使用單獨的數據庫進行生產,在**MONGODB_URI**環境變量中,設置其 URI。您可以完全按照[我們原來的方式](/zh-TW/docs/Learn/Server-side/Express_Nodejs/mongoose#Setting_up_the_MongoDB_database),設置新數據庫和數據庫用戶,並獲取其 URI。您可以如下圖所示設置 URI(顯然,要使用您自己的 URI!) -
>heroku config:set MONGODB_URI='mongodb://your_user:your_password@ds139278.mlab.com:39278/local_library_production'
+```bash
+>heroku config:set MONGODB_URI='mongodb://your_user:your_password@ds139278.mlab.com:39278/local_library_production'
 Setting MONGODB_URI and restarting limitless-tor-18923... done, v13
 MONGODB_URI: mongodb://your_user:your_password@ds139278.mlab.com:39278/local_library_production
-
+``` -

您可以使用 heroku config 命令,隨時檢查配置變量 - 立即嘗試:

+您可以使用 `heroku config `命令,隨時檢查配置變量 - 立即嘗試: -
>heroku config
+```bash
+>heroku config
 === limitless-tor-18923 Config Vars
 MONGODB_URI: mongodb://your_user:your_password@ds139278.mlab.com:39278/local_library_production
 NODE_ENV:    production
-
+``` -

Heroku 會在更新變量時,重新啟動應用程序。如果您現在檢查主頁,它應該顯示對象計數的零值,因為上面的更改,意味著我們現在正在使用新的(空)數據庫。

+Heroku 會在更新變量時,重新啟動應用程序。如果您現在檢查主頁,它應該顯示對象計數的零值,因為上面的更改,意味著我們現在正在使用新的(空)數據庫。 -

管理附加組件

+### 管理附加組件 -

Heroku 使用獨立的附加組件,為應用程序提供支持服務 - 例如電子郵件或數據庫服務。我們不在本網站中使用任何插件,但它們是使用 Heroku 的重要部分,因此您可能需要查看主題 - 管理插件(Heroku 官方文件)

+Heroku 使用獨立的附加組件,為應用程序提供支持服務 - 例如電子郵件或數據庫服務。我們不在本網站中使用任何插件,但它們是使用 Heroku 的重要部分,因此您可能需要查看主題 - [管理插件(Heroku 官方文件)](https://devcenter.heroku.com/articles/managing-add-ons)。 -

除錯

+### 除錯 -

Heroku 客戶端提供了一些除錯工具:

+Heroku 客戶端提供了一些除錯工具: -
heroku logs  # Show current logs
+```bash
+heroku logs  # Show current logs
 heroku logs --tail # Show current logs and keep updating with any new results
 heroku ps   #Display dyno status
-
- -
    -
- -

總結

- -

本教程介紹在生產環境中,如何配置 Express 應用。是Express系列教程的最後一個。我們希望你覺得這些教程有用。你可以在 Github 上取得完整的源碼。

- -

相關鏈接

- - - -

{{PreviousMenu("Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}

- -

- -

本教學鏈接

- - - -

+``` + +## 總結 + +本教程介紹在生產環境中,如何配置 Express 應用。是 Express 系列教程的最後一個。我們希望你覺得這些教程有用。你可以在 [Github ](https://github.com/mdn/express-locallibrary-tutorial)上取得完整的源碼。 + +## 相關鏈接 + +- [Production best practices: performance and reliability](https://expressjs.com/en/advanced/best-practice-performance.html) (Express docs) +- [Production Best Practices: Security](https://expressjs.com/en/advanced/best-practice-security.html) (Express docs) +- Heroku + + - [Getting Started on Heroku with Node.js](https://devcenter.heroku.com/articles/getting-started-with-nodejs) (Heroku docs) + - [Deploying Node.js Applications on Heroku](https://devcenter.heroku.com/articles/deploying-nodejs) (Heroku docs) + - [Heroku Node.js Support](https://devcenter.heroku.com/articles/nodejs-support) (Heroku docs) + - [Optimizing Node.js Application Concurrency](https://devcenter.heroku.com/articles/node-concurrency) (Heroku docs) + - [How Heroku works](https://devcenter.heroku.com/articles/how-heroku-works) (Heroku docs) + - [Dynos and the Dyno Manager](https://devcenter.heroku.com/articles/dynos) (Heroku docs) + - [Configuration and Config Vars](https://devcenter.heroku.com/articles/config-vars) (Heroku docs) + - [Limits](https://devcenter.heroku.com/articles/limits) (Heroku docs) + +- Digital Ocean + + - [Express](https://www.digitalocean.com/community/tutorials?q=express) tutorials + - [Node.js](https://www.digitalocean.com/community/tutorials?q=node.js) tutorials + +{{PreviousMenu("Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}} + +## 本教學鏈接 + +- [Express/Node introduction](/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction) +- [Setting up a Node (Express) development environment](/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment) +- [Express Tutorial: The Local Library website](/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website) +- [Express Tutorial Part 2: Creating a skeleton website](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website) +- [Express Tutorial Part 3: Using a Database (with Mongoose)](/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose) +- [Express Tutorial Part 4: Routes and controllers](/en-US/docs/Learn/Server-side/Express_Nodejs/routes) +- [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms) +- [Express Tutorial Part 7: Deploying to production](/en-US/docs/Learn/Server-side/Express_Nodejs/deployment) diff --git a/files/zh-tw/learn/server-side/express_nodejs/development_environment/index.md b/files/zh-tw/learn/server-side/express_nodejs/development_environment/index.md index 9169f714f28efa..8ae629ee4891b8 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/development_environment/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/development_environment/index.md @@ -3,380 +3,380 @@ title: Setting up a Node development environment slug: Learn/Server-side/Express_Nodejs/development_environment translation_of: Learn/Server-side/Express_Nodejs/development_environment --- -
{{LearnSidebar}}
+{{LearnSidebar}}{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Introduction", "Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs")}} -
{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Introduction", "Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs")}}
- -

現在你已經了解Express的目的了,接下來繼續說明如何設定和測試 Windows、Linux (Ubuntu)和Mac OS X上的Node/Express開發環境。不管你用的是什麼作業系統,你都能在本文中找到開發Express應用的入門需知。

+現在你已經了解 Express 的目的了,接下來繼續說明如何設定和測試 Windows、Linux (Ubuntu)和 Mac OS X 上的 Node/Express 開發環境。不管你用的是什麼作業系統,你都能在本文中找到開發 Express 應用的入門需知。 - - - - - - - - - - + + + + + + + + + +
前置需求:了解如何開啟terminal / command line. 了解如何在開發系統上安裝套件。
目標:在你的電腦上設定Express(X.XX)開發環境。
前置需求: + 了解如何開啟terminal / command line. 了解如何在開發系統上安裝套件。 +
目標:在你的電腦上設定Express(X.XX)開發環境。
-

Express 開發環境概覽

+## Express 開發環境概覽 -

為了使你能快速的開發web應用,NodeExpress 非常容易安裝,這個部分說明哪些工具是需要的、在Ubuntu、macOS和Windows中安裝Node和Express的最簡單方法、展示如何測試安裝成功與否。

+為了使你能快速的開發 web 應用,_Node_ 和 _Express_ 非常容易安裝,這個部分說明哪些工具是需要的、在 Ubuntu、macOS 和 Windows 中安裝 Node 和 Express 的最簡單方法、展示如何測試安裝成功與否。 -

什麼是Express開發環境?

+### 什麼是 Express 開發環境? -

Express 開發環境包含 Nodejs、NPM 套件管理器的安裝, 還有 Express Application 產生器(可選)

+_Express_ 開發環境包含 _Nodejs、NPM_ 套件管理器的安裝, 還有 _Express Application_ 產生器(可選)。 -

Node NPM 套件管理器會從準備好的 binary package、安裝檔、 作業系統的套件管理器或是從源檔一起安裝。接著 Express 會透過 NPM 進行安裝,成為你所有個別 Express web 應用的依賴項(以及其他函式庫,如模板引擎,資料庫驅動程式,身份驗證中間層,用於提供靜態文件的中間件等)

+_Node_ 和 _NPM_ 套件管理器會從準備好的 binary package、安裝檔、 作業系統的套件管理器或是從源檔一起安裝。接著 _Express_ 會透過 NPM 進行安裝,成為你所有個別 Express web 應用的依賴項(以及其他函式庫,如模板引擎,資料庫驅動程式,身份驗證中間層,用於提供靜態文件的中間件等) -

NPM 也可用來安裝 Express 應用程式產生器(全域用),一個方便的工具幫助你創造符合 MVC模式的 Express web app 骨架。你不一定要使用應用程式產生器,因為每個Express應用程式不需要擁有同樣的檔案結構或依賴項。但為了專注於學習本身以及習慣模組化架構,我們會在接下來的教學中使用它。

+NPM 也可用來安裝 Express 應用程式產生器(全域用),一個方便的工具幫助你創造符合 [MVC 模式](/en-US/Apps/Fundamentals/Modern_web_app_architecture/MVC_architecture)的 Express web app 骨架。你不一定要使用應用程式產生器,因為每個 Express 應用程式不需要擁有同樣的檔案結構或依賴項。但為了專注於學習本身以及習慣模組化架構,我們會在接下來的教學中使用它。 -
-

備註: 與其他不包含單獨的web開發伺服器的Web框架不同。 在Node / Express中,Web應用程式創建並運行自己的Web伺服器!

-
+> **備註:** 與其他不包含單獨的 web 開發伺服器的 Web 框架不同。 在 Node / Express 中,Web 應用程式創建並運行自己的 Web 伺服器! -

典型的開發環境還包含其他工具,例如:編輯程式碼使用的文字編輯器、IDE,進行版本控置管理不同版本程式碼的Git。這邊假設你已經有這種工具了(尤其是文字編輯器)

+典型的開發環境還包含其他工具,例如:編輯程式碼使用的[文字編輯器](/en-US/docs/Learn/Common_questions/Available_text_editors)、IDE,進行版本控置管理不同版本程式碼的[Git](/zh-TW/docs/Glossary/Git)。這邊假設你已經有這種工具了(尤其是文字編輯器) -

哪些作業系統有支援?

+### 哪些作業系統有支援? -

Node 可以執行在 Windows、macOS、各種 Linux、Docker 等等(nodejs 的下載頁面有完整的列表),在開發階段中個人電腦應該都有足夠的效能來執行 Node 。Express 執行在 Node 環境中,所以也能所有有安裝Node的平台上執行。

+Node 可以執行在 Windows、macOS、各種 Linux、Docker 等等(nodejs 的[下載](https://nodejs.org/en/download/)頁面有完整的列表),在開發階段中個人電腦應該都有足夠的效能來執行 Node 。Express 執行在 Node 環境中,所以也能所有有安裝 Node 的平台上執行。 -

在這份教學中我們提供 Windows、macOS 和 Ubuntu Linux 的 Node 安裝教學。

+在這份教學中我們提供 Windows、macOS 和 Ubuntu Linux 的 Node 安裝教學。 -

該用什麼版本的 Node/Express?

+### 該用什麼版本的 Node/Express? -

Node 有許多版本,更新的版本代表著 bug 的修復、支援更新版本的 ECMAScript(JavaScript)標準和更好的 Node APIs 。

+Node 有許多[版本](https://nodejs.org/en/blog/release/),更新的版本代表著 bug 的修復、支援更新版本的 ECMAScript(JavaScript)標準和更好的 Node APIs 。 -

基本上你應該使用最新的 LTS 版本(long-term supported,長期維護版)。這種版本比『Current』版本更穩定而且還擁有最新的功能及持續性的更新維護。除非LTS不支援你需要的功能才使用『Current』版本。

+基本上你應該使用最新的 LTS 版本(*long-term supported,*長期維護版)。這種版本比『Current』版本更穩定而且還擁有最新的功能及持續性的更新維護。除非 LTS 不支援你需要的功能才使用『Current』版本。 -

而 Express ?永遠使用最新版!

+而 Express ?永遠使用最新版! -

關於資料庫和其他依賴項呢?

+### 關於資料庫和其他依賴項呢? -

諸如資料庫、模版引擎、驗證引擎等等都屬於應用程式的一部分,這些依賴項會透過NPM導入應用程式環境中,在後續的章節將會進一步探討。

+諸如資料庫、模版引擎、驗證引擎等等都屬於應用程式的一部分,這些依賴項會透過 NPM 導入應用程式環境中,在後續的章節將會進一步探討。 -

安裝Node

+## 安裝 Node -

為了使用Express,首先要在你的電腦上安裝Node和Node Package Manager (NPM)。接下來用最簡單的方法在 Ubuntu Linux 16.04、 macOS和 Windows 10上安裝Nodejs的 Long Term Supported (LTS)版本吧

+為了使用 Express,首先要在你的電腦上安裝 Node 和[Node Package Manager (NPM)](https://docs.npmjs.com/)。接下來用最簡單的方法在 Ubuntu Linux 16.04、 macOS 和 Windows 10 上安裝 Nodejs 的 Long Term Supported (LTS)版本吧 -
-

備註: 以下的部分用最簡單的方法在上述的作業系統中安裝Node和NPM。如果你使用其他作業系統或想看看其他平台的安裝方式,請查閱透過套件管理器安裝Node.js (nodejs.org)。

-
+> **備註:** 以下的部分用最簡單的方法在上述的作業系統中安裝 Node 和 NPM。如果你使用其他作業系統或想看看其他平台的安裝方式,請查閱[透過套件管理器安裝 Node.js](https://nodejs.org/en/download/package-manager/) (nodejs.org)。 -

Windows 和macOS

+### Windows 和 macOS -

直接使用安裝檔吧!

+直接使用安裝檔吧! -
    -
  1. 下載需要的安裝檔: -
      -
    1. 開啟 https://nodejs.org/en/
    2. -
    3. 對於大部分的使用者來說,直接下載LTS版本
    4. -
    -
  2. -
  3. 下載完成後雙擊安裝檔,並照著安裝流程繼續。
  4. -
+1. 下載需要的安裝檔: -

Ubuntu 16.04

+ 1. 開啟 + 2. 對於大部分的使用者來說,直接下載 LTS 版本 -

安裝Node 8.x LTS版本最簡單的方法是使用套件管理器,只要在terminal上執行兩行指令

+2. 下載完成後雙擊安裝檔,並照著安裝流程繼續。 -
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
-sudo apt-get install -y nodejs
+### Ubuntu 16.04
 
-
+安裝 Node 8.x LTS 版本最簡單的方法是使用[套件管理器](https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions),只要在 terminal 上執行兩行指令 -
-

警告: 不要直接從普通的Ubuntu repositories 安裝,那邊只有很舊的版本。

-
+```bash +curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - +sudo apt-get install -y nodejs +``` -

測試 Nodejs 和NPM 的安裝

+> **警告:** 不要直接從普通的 Ubuntu repositories 安裝,那邊只有很舊的版本。 -

測試Node安裝最簡單的方法是在terminal/command上執行"version"這個指令,它會顯示當前的Node版本:

+### 測試 Nodejs 和 NPM 的安裝 -
>node -v
-v8.9.4
+測試 Node 安裝最簡單的方法是在 terminal/command 上執行"version"這個指令,它會顯示當前的 Node 版本: -

NPM應該會隨著Node一起安裝,可以用相同的方法進行測試:

+```bash +>node -v +v8.9.4 +``` -
>npm -v
-5.6.0
+NPM 應該會隨著 Node 一起安裝,可以用相同的方法進行測試: -

接著用稍為令人興奮的方法來測試吧!讓我們創件一個非常基本的『純Node』伺服器,當你開啟正確的網頁時它會在瀏覽器上顯示"Hello World"

+```bash +>npm -v +5.6.0 +``` -
    -
  1. 複製以下的文字到名為hellonode.js的檔案中,目前我們只用到Node而已。 +接著用稍為令人興奮的方法來測試吧!讓我們創件一個非常基本的『純 Node』伺服器,當你開啟正確的網頁時它會在瀏覽器上顯示"Hello World" -
    //載入HTTP模組
    -var http = require("http");
    +1.  複製以下的文字到名為**hellonode.js**的檔案中,目前我們只用到 Node 而已。
     
    -//創建HTTP 伺服器並監聽8000埠
    -http.createServer(function (request, response) {
    +    ```js
    +    //載入HTTP模組
    +    var http = require("http");
     
    -   // Set the response HTTP header with HTTP status and Content type
    -   response.writeHead(200, {'Content-Type': 'text/plain'});
    +    //創建HTTP 伺服器並監聽8000埠
    +    http.createServer(function (request, response) {
     
    -   // Send the response body "Hello World"
    -   response.end('Hello World\n');
    -}).listen(8000);
    +       // Set the response HTTP header with HTTP status and Content type
    +       response.writeHead(200, {'Content-Type': 'text/plain'});
     
    -// Print URL for accessing server
    -console.log('Server running at http://127.0.0.1:8000/')
    -
    + // Send the response body "Hello World" + response.end('Hello World\n'); + }).listen(8000); -

    這段程式載入『http』模組,並創建一個伺服器 (createServer(),並在8000埠上監聽HTTP requests。 The script then prints a message to the console about what browser URL you can use to test the server. The createServer() function takes as an argument a callback function that will be invoked when an HTTP request is received — this simply returns a response with an HTTP status code of 200 ("OK") and the plain text "Hello World".

    -
  2. -
  3. -
    -

    備註: Don't worry if you don't understand exactly what this code is doing yet! We'll explain our code in greater detail once we start using Express!

    -
    -
  4. -
  5. Start the server by navigating into the same directory as your hellonode.js file in your command prompt, and calling node along with the script name, like so: -
    >node hellonode.js
    -Server running at http://127.0.0.1:8000/
    -
    -
  6. -
  7. Navigate to the URL (http://127.0.0.1:8000/). If everything is working, the browser should simply display the string "Hello World".
  8. -
+ // Print URL for accessing server + console.log('Server running at http://127.0.0.1:8000/') + ``` -

Using NPM

+ 這段程式載入『http』模組,並創建一個伺服器 (`createServer()`,並在 8000 埠上監聽 HTTP requests。 The script then prints a message to the console about what browser URL you can use to test the server. The `createServer()` function takes as an argument a callback function that will be invoked when an HTTP request is received — this simply returns a response with an HTTP status code of 200 ("OK") and the plain text "Hello World". -

Next to Node itself, NPM is the most important tool for working with Node applications. NPM is used to fetch any packages (JavaScript libraries) that an application needs for development, testing, and/or production, and may also be used to run tests and tools used in the development process.

+2. > **備註:** Don't worry if you don't understand exactly what this code is doing yet! We'll explain our code in greater detail once we start using Express! +3. Start the server by navigating into the same directory as your `hellonode.js` file in your command prompt, and calling `node` along with the script name, like so: -
-

備註: From Node's perspective, Express is just another package that you need to install using NPM and then require in your own code.

-
+ ```bash + >node hellonode.js + Server running at http://127.0.0.1:8000/ + ``` -

You can manually use NPM to separately fetch each needed package. Typically we instead manage dependencies using a plain-text definition file named package.json. This file lists all the dependencies for a specific JavaScript "package", including the package's name, version, description, initial file to execute, production dependencies, development dependencies, versions of Node it can work with, etc. The package.json file should contain everything NPM needs to fetch and run your application (if you were writing a reusable library you could use this definition to upload your package to the npm respository and make it available for other users).

+4. Navigate to the URL (). If everything is working, the browser should simply display the string "Hello World". -

Adding dependencies

+## Using NPM -

The following steps show how you can use NPM to download a package, save it into the project dependencies, and then require it in a Node application.

+Next to _Node_ itself, [NPM](https://docs.npmjs.com/) is the most important tool for working with _Node_ applications. NPM is used to fetch any packages (JavaScript libraries) that an application needs for development, testing, and/or production, and may also be used to run tests and tools used in the development process. -
-

備註: Here we show the instructions to fetch and install the Express package. Later on we'll show how this package, and others, are already specified for us using the Express Application Generator. This section is provided because it is useful to understand how NPM works and what is being created by the application generator.

-
+> **備註:** From Node's perspective, _Express_ is just another package that you need to install using NPM and then require in your own code. -
    -
  1. First create a directory for your new application and navigate into it: -
    mkdir myapp
    -cd myapp
    -
  2. -
  3. Use the npm init command to create a package.json file for your application. This command prompts you for a number of things, including the name and version of your application and the name of the initial entry point file (by default this is index.js). For now, just accept the defaults: -
    npm init
    +You can manually use NPM to separately fetch each needed package. Typically we instead manage dependencies using a plain-text definition file named [package.json](https://docs.npmjs.com/files/package.json). This file lists all the dependencies for a specific JavaScript "package", including the package's name, version, description, initial file to execute, production dependencies, development dependencies, versions of _Node_ it can work with, etc. The **package.json** file should contain everything NPM needs to fetch and run your application (if you were writing a reusable library you could use this definition to upload your package to the npm respository and make it available for other users). -

    If you display the package.json file (cat package.json), you will see the defaults that you accepted, ending with the license.

    +### Adding dependencies -
    {
    -  "name": "myapp",
    -  "version": "1.0.0",
    -  "description": "",
    -  "main": "index.js",
    -  "scripts": {
    -    "test": "echo \"Error: no test specified\" && exit 1"
    -  },
    -  "author": "",
    -  "license": "ISC"
    -}
    -
    -
  4. -
  5. Now install the Express library in the myapp directory. The package will automatically be saved to the dependencies list in your package.json file. -
    npm install express
    - -

    The dependencies section of your package.json will now appear at the end of the package.json file and will include Express.

    - -
    {
    -  "name": "myapp",
    -  "version": "1.0.0",
    -  "description": "",
    -  "main": "index.js",
    -  "scripts": {
    -    "test": "echo \"Error: no test specified\" && exit 1"
    -  },
    -  "author": "",
    -  "license": "ISC",
    -  "dependencies": {
    -    "express": "^4.16.2"
    -  }
    -}
    -
    -
  6. -
  7. To use the library you call the require() function as shown below. -
    var express = require('express')
    -var app = express()
    -
    -app.get('/', function (req, res) {
    -  res.send('Hello World!')
    -})
    -
    -app.listen(8000, function () {
    -  console.log('Example app listening on port 8000!')
    -})
    -
    - -

    This code shows a minimal "HelloWorld" Express web application. This imports the "express" module and uses it to create a server (app) that listens for HTTP requests on port 8000 and prints a message to the console explaining what browser URL you can use to test the server. The app.get() function only responds to HTTP GET requests with the specified URL path ('/'), in this case by calling a function to send our Hello World! message.
    -
    - Create a file named index.js in the root of the "myapp" application directory and give it the contents shown above.

    -
  8. -
  9. You can start the server by calling node with the script in your command prompt: -
    >node index.js
    -Example app listening on port 8000
    -
    -
  10. -
  11. Navigate to the URL (http://127.0.0.1:8000/). If everything is working, the browser should simply display the string "Hello World!".
  12. -
- -

Development dependencies

- -

If a dependency is only used during development, you should instead save it as a "development dependency" (so that your package users don't have to install it in production). For example, to use the popular JavaScript Linting tool eslint you would call NPM as shown:

- -
npm install eslint --save-dev
- -

The following entry would then be added to your application's package.json:

- -
  "devDependencies": {
-    "eslint": "^4.12.1"
-  }
-
+The following steps show how you can use NPM to download a package, save it into the project dependencies, and then require it in a Node application. -
-

備註: "Linters" are tools that perform static analysis on software in order to recognise and report adherence/non-adherance to some set of coding best practice.

-
+> **備註:** Here we show the instructions to fetch and install the _Express_ package. Later on we'll show how this package, and others, are already specified for us using the _Express Application Generator_. This section is provided because it is useful to understand how NPM works and what is being created by the application generator. -

Running tasks

+1. First create a directory for your new application and navigate into it: -

In addition to defining and fetching dependencies you can also define named scripts in your package.json files and call NPM to execute them with the run-script command. This approach is commonly used to automate running tests and parts of the development or build toolchain (e.g., running tools to minify JavaScript, shrink images, LINT/analyse your code, etc).

+ ```bash + mkdir myapp + cd myapp + ``` -
-

備註: Task runners like Gulp and Grunt can also be used to run tests and other external tools.

-
+2. Use the npm `init` command to create a **package.json** file for your application. This command prompts you for a number of things, including the name and version of your application and the name of the initial entry point file (by default this is **index.js**). For now, just accept the defaults: -

For example, to define a script to run the eslint development dependency that we specified in the previous section we might add the following script block to our package.json file (assuming that our application source is in a folder /src/js):

+ ```bash + npm init + ``` -
"scripts": {
-  ...
-  "lint": "eslint src/js"
-  ...
-}
-
+ If you display the **package.json** file (`cat package.json`), you will see the defaults that you accepted, ending with the license. -

To explain a little further, eslint src/js is a command that we could enter in our terminal/command line to run eslint on JavaScript files contained in the src/js directory inside our app directory. Including the above inside our app's package.json file provides a shortcut for this command — lint.

+ ```json + { + "name": "myapp", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" + } + ``` -

We would then be able to run eslint using NPM by calling:

+3. Now install the _Express_ library in the **myapp** directory. The package will automatically be saved to the dependencies list in your **package.json** file. -
npm run-script lint
-# OR (using the alias)
-npm run lint
-
+ ```bash + npm install express + ``` + + The dependencies section of your **package.json** will now appear at the end of the **package.json** file and will include _Express_. + + ```json + { + "name": "myapp", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.16.2" + } + } + ``` -

This example may not look any shorter than the original command, but you can include much bigger commands inside your npm scripts, including chains of multiple commands. You could identify a single npm script that runs all your tests at once.

+4. To use the library you call the `require()` function as shown below. -

Installing the Express Application Generator

+ ```plain + var express = require('express') + var app = express() -

The Express Application Generator tool generates an Express application "skeleton". Install the generator using NPM as shown (the -g flag installs the tool globally so that you can call it from anywhere):

+ app.get('/', function (req, res) { + res.send('Hello World!') + }) -
npm install express-generator -g
+ app.listen(8000, function () { + console.log('Example app listening on port 8000!') + }) + ``` -

To create an Express app named "helloworld" with the default settings, navigate to where you want to create it and run the app as shown:

+ This code shows a minimal "HelloWorld" Express web application. This imports the "express" module and uses it to create a server (`app`) that listens for HTTP requests on port 8000 and prints a message to the console explaining what browser URL you can use to test the server. The `app.get()` function only responds to HTTP `GET` requests with the specified URL path ('/'), in this case by calling a function to send our _Hello World!_ message. -
express helloworld
+ Create a file named **index.js** in the root of the "myapp" application directory and give it the contents shown above. -
-

備註: You can also specify the template library to use and a number of other settings. Use the help command to see all the options:

+5. You can start the server by calling node with the script in your command prompt: -
express --help
-
-
+ ```bash + >node index.js + Example app listening on port 8000 + ``` -

NPM will create the new Express app in a sub folder of your current location, displaying build progress on the console. On completion, the tool will display the commands you need to enter to install the Node dependencies and start the app.

+6. Navigate to the URL (). If everything is working, the browser should simply display the string "Hello World!". -
-

備註: The new app will have a package.json file in its root directory. You can open this to see what dependencies are installed, including Express and the template library Jade:

+### Development dependencies -
{
-  "name": "helloworld",
-  "version": "0.0.0",
-  "private": true,
-  "scripts": {
-    "start": "node ./bin/www"
-  },
-  "dependencies": {
-    "body-parser": "~1.18.2",
-    "cookie-parser": "~1.4.3",
-    "debug": "~2.6.9",
-    "express": "~4.15.5",
-    "jade": "~1.11.0",
-    "morgan": "~1.9.0",
-    "serve-favicon": "~2.4.5"
+If a dependency is only used during development, you should instead save it as a "development dependency" (so that your package users don't have to install it in production). For example, to use the popular JavaScript Linting tool [eslint](http://eslint.org/) you would call NPM as shown:
+
+```bash
+npm install eslint --save-dev
+```
+
+The following entry would then be added to your application's **package.json**:
+
+```js
+  "devDependencies": {
+    "eslint": "^4.12.1"
   }
-}
-
+``` -

Install all the dependencies for the helloworld app using NPM as shown:

+> **備註:** "[Linters]()" are tools that perform static analysis on software in order to recognise and report adherence/non-adherance to some set of coding best practice. -
cd helloworld
-npm install
-
+### Running tasks -

Then run the app (the commands are slightly different for Windows and Linux/macOS), as shown below:

+In addition to defining and fetching dependencies you can also define _named_ scripts in your **package.json** files and call NPM to execute them with the [run-script](https://docs.npmjs.com/cli/run-script) command. This approach is commonly used to automate running tests and parts of the development or build toolchain (e.g., running tools to minify JavaScript, shrink images, LINT/analyse your code, etc). -
# Run the helloworld on Windows
-SET DEBUG=helloworld:* & npm start
+> **備註:** Task runners like [Gulp](http://gulpjs.com/) and [Grunt](http://gruntjs.com/) can also be used to run tests and other external tools.
 
-# Run helloworld on Linux/macOS
-DEBUG=helloworld:* npm start
-
+For example, to define a script to run the _eslint_ development dependency that we specified in the previous section we might add the following script block to our **package.json** file (assuming that our application source is in a folder /src/js): + +```js +"scripts": { + ... + "lint": "eslint src/js" + ... +} +``` -

The DEBUG command creates useful logging, resulting in an output like that shown below.

+To explain a little further, `eslint src/js` is a command that we could enter in our terminal/command line to run `eslint` on JavaScript files contained in the `src/js` directory inside our app directory. Including the above inside our app's package.json file provides a shortcut for this command — `lint`. -
>SET DEBUG=helloworld:* & npm start
+We would then be able to run _eslint_ using NPM by calling:
 
-> helloworld@0.0.0 start D:\Github\expresstests\helloworld
-> node ./bin/www
+```bash
+npm run-script lint
+# OR (using the alias)
+npm run lint
+```
+
+This example may not look any shorter than the original command, but you can include much bigger commands inside your npm scripts, including chains of multiple commands. You could identify a single npm script that runs all your tests at once.
+
+## Installing the Express Application Generator
+
+The [Express Application Generator](https://expressjs.com/en/starter/generator.html) tool generates an Express application "skeleton". Install the generator using NPM as shown (the `-g` flag installs the tool globally so that you can call it from anywhere):
+
+```plain
+npm install express-generator -g
+```
+
+To create an _Express_ app named "helloworld" with the default settings, navigate to where you want to create it and run the app as shown:
+
+```bash
+express helloworld
+```
+
+> **備註:** You can also specify the template library to use and a number of other settings. Use the `help` command to see all the options:
+>
+> ```bash
+> express --help
+> ```
+
+NPM will create the new Express app in a sub folder of your current location, displaying build progress on the console. On completion, the tool will display the commands you need to enter to install the Node dependencies and start the app.
+
+> **備註:** The new app will have a **package.json** file in its root directory. You can open this to see what dependencies are installed, including Express and the template library Jade:
+>
+> ```js
+> {
+>   "name": "helloworld",
+>   "version": "0.0.0",
+>   "private": true,
+>   "scripts": {
+>     "start": "node ./bin/www"
+>   },
+>   "dependencies": {
+>     "body-parser": "~1.18.2",
+>     "cookie-parser": "~1.4.3",
+>     "debug": "~2.6.9",
+>     "express": "~4.15.5",
+>     "jade": "~1.11.0",
+>     "morgan": "~1.9.0",
+>     "serve-favicon": "~2.4.5"
+>   }
+> }
+> ```
+
+Install all the dependencies for the helloworld app using NPM as shown:
+
+```bash
+cd helloworld
+npm install
+```
+
+Then run the app (the commands are slightly different for Windows and Linux/macOS), as shown below:
+
+```bash
+# Run the helloworld on Windows
+SET DEBUG=helloworld:* & npm start
+
+# Run helloworld on Linux/macOS
+DEBUG=helloworld:* npm start
+```
 
-  helloworld:server Listening on port 3000 +0ms
+The DEBUG command creates useful logging, resulting in an output like that shown below. -

Open a browser and navigate to http://127.0.0.1:3000/ to see the default Express welcome page.

+```bash +>SET DEBUG=helloworld:* & npm start -

Express - Generated App Default Screen

+> helloworld@0.0.0 start D:\Github\expresstests\helloworld +> node ./bin/www -

We'll talk more about the generated app when we get to the article on generating a skeleton application.

+ helloworld:server Listening on port 3000 +0ms +``` -
    -
+Open a browser and navigate to to see the default Express welcome page. -

總結

+![Express - Generated App Default Screen](express_default_screen.png) -

你現在有一個 Node 開發環境在你的電腦上運行,可以用來創造 Express 網頁應用。你也看到如何用 NPM 來加載 Express到一個應用中,以及看到如何使用 Express 應用產生器,創建應用,然後執行它們。

+We'll talk more about the generated app when we get to the article on generating a skeleton application. -

下一篇文章,我們開始跟著教程一步一步實作,使用這個開發環境與搭配工具,建立一個完整的網頁應用。

+## 總結 -

See also

+你現在有一個 Node 開發環境在你的電腦上運行,可以用來創造 Express 網頁應用。你也看到如何用 NPM 來加載 Express 到一個應用中,以及看到如何使用 Express 應用產生器,創建應用,然後執行它們。 - +下一篇文章,我們開始跟著教程一步一步實作,使用這個開發環境與搭配工具,建立一個完整的網頁應用。 -

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Introduction", "Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs")}}

+## See also +- [Downloads](https://nodejs.org/en/download/) page (nodejs.org) +- [Installing Node.js via package manager](https://nodejs.org/en/download/package-manager/) (nodejs.org) +- [Installing Express](http://expressjs.com/en/starter/installing.html) (expressjs.com) +- [Express Application Generator](https://expressjs.com/en/starter/generator.html) (expressjs.com) +{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Introduction", "Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs")}} -

In this module

+## In this module - +- [Express/Node introduction](/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction) +- [Setting up a Node (Express) development environment](/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment) +- [Express Tutorial: The Local Library website](/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website) +- [Express Tutorial Part 2: Creating a skeleton website](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website) +- [Express Tutorial Part 3: Using a Database (with Mongoose)](/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose) +- [Express Tutorial Part 4: Routes and controllers](/en-US/docs/Learn/Server-side/Express_Nodejs/routes) +- [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms) +- [Express Tutorial Part 7: Deploying to production](/en-US/docs/Learn/Server-side/Express_Nodejs/deployment) diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.md index c19cb7588cbd39..2e01cd458b7918 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_detail_page/index.md @@ -3,23 +3,26 @@ title: 作者詳情頁面 slug: Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page --- -

作者細節頁面,需要呈現指定作者 Author 的信息,使用 _id 欄位的值(自動產生)識別,接著是這個作者 Author 的所有書本物件 Book 的列表。

+作者細節頁面,需要呈現指定作者 `Author `的信息,使用 `_id` 欄位的值(自動產生)識別,接著是這個作者 `Author `的所有書本物件 `Book `的列表。 -

Controller 控制器

+## Controller 控制器 -

打開 /controllers/authorController.js.

+打開 **/controllers/authorController.js**. -

在檔案最上方,加入底下幾行,引入 asyncBook 模組(作者細節頁面需要它們)。

+在檔案最上方,加入底下幾行,引入 _async_ 和 _Book_ 模組(作者細節頁面需要它們)。 -
var async = require('async');
-var Book = require('../models/book');
+```js +var async = require('async'); +var Book = require('../models/book'); +``` -

找到 exported author_detail() 控制器方法,並用底下代碼置換。

+找到 exported `author_detail()` 控制器方法,並用底下代碼置換。 -
// Display detail page for a specific Author.
+```js
+// Display detail page for a specific Author.
 exports.author_detail = function(req, res, next) {
 
-    async.parallel({
+    async.parallel({
         author: function(callback) {
             Author.findById(req.params.id)
               .exec(callback)
@@ -37,22 +40,23 @@ exports.author_detail = function(req, res, next) {
         }
         // Successful, so render.
         res.render('author_detail', { title: 'Author Detail', author: results.author, author_books: results.authors_books } );
-    });
+    });
 
 };
-
+``` -

此處的控制器方法使用 async.parallel(),用平行的方式,查詢作者 Author和相應的書本實例,並附加上繪製本頁面的回調,如果 2 個要求都成功完成,就運行回調。這個方式,就跟前面的種類細節頁面所說明的完全相同。

+此處的控制器方法使用` async.parallel()`,用平行的方式,查詢作者 `Author`和相應的書本實例,並附加上繪製本頁面的回調,如果 2 個要求都成功完成,就運行回調。這個方式,就跟前面的*種類細節頁面*所說明的完全相同。 -

View 視圖

+## View 視圖 -

創建 /views/author_detail.pug ,並複制貼上底下的文字。

+創建 **/views/author_detail.pug** ,並複制貼上底下的文字。 -
extends layout
+```js
+extends layout
 
 block content
 
-  h1 Author: #{author.name}
+  h1 Author: #{author.name}
   p #{author.date_of_birth} - #{author.date_of_death}
 
   div(style='margin-left:20px;margin-top:20px')
@@ -67,23 +71,19 @@ block content
 
       else
         p This author has no books.
-
+``` -

本模板裡的所有事物,都在先前的章節演示過了。

+本模板裡的所有事物,都在先前的章節演示過了。 -

它看起來像是?

+## 它看起來像是? -

運行本應用,並打開瀏覽器訪問 http://localhost:3000/。選擇 All Authors 連結,然後選擇一個作者。如果每個東西都設定正確了,你的網站看起來應該會像底下的截圖。

+運行本應用,並打開瀏覽器訪問 。選擇 _All Authors_ 連結,然後選擇一個作者。如果每個東西都設定正確了,你的網站看起來應該會像底下的截圖。 -

Author Detail Page - Express Local Library site

+![Author Detail Page - Express Local Library site](locallibary_express_author_detail.png) -
-

備註: 作者的出生與死亡日期的外觀很醜!我們將在本文最後的自我挑戰處理它。

-
+> **備註:** 作者的出生與死亡日期的外觀很醜!我們將在本文最後的自我挑戰處理它。 -

下一步

+## 下一步 - +- 回到 [Express 教學 5: 呈現圖書館資料](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- 繼續教學 5 的最後一個部分: [書本實例詳情頁面與自我挑戰](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge) diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_list_page/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_list_page/index.md index ade389748bdc5f..5f1b18b096454e 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_list_page/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/author_list_page/index.md @@ -3,15 +3,16 @@ title: Author list page and Genre list page challenge slug: Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page --- -

作者列表頁面,需要呈現數據庫中所有作者的列表,有每位作者的名字,並連結到作者詳細內容頁面。出生與死亡日期應該在名字後面,並且在同一列。

+作者列表頁面,需要呈現數據庫中所有作者的列表,有每位作者的名字,並連結到作者詳細內容頁面。出生與死亡日期應該在名字後面,並且在同一列。 -

Controller 控制器

+## Controller 控制器 -

作者列表控制器函數,需要獲取所有作者實例的列表,然後將這些實例傳遞給模板進行渲染。

+作者列表控制器函數,需要獲取所有作者實例的列表,然後將這些實例傳遞給模板進行渲染。 -

打開 /controllers/authorController.js。在文件頂部附近,找到導出的 author_list() 控制器方法,並將其替換為以下代碼(更改後的代碼以粗體顯示)。

+打開 **/controllers/authorController.js**。在文件頂部附近,找到導出的 `author_list() `控制器方法,並將其替換為以下代碼(更改後的代碼以**粗體**顯示)。 -
// Display list of all Authors.
+```js
+// Display list of all Authors.
 exports.author_list = function(req, res, next) {
 
   Author.find()
@@ -22,15 +23,17 @@ exports.author_list = function(req, res, next) {
       res.render('author_list', { title: 'Author List', author_list: list_authors });
     });
 
-};
+}; +``` -

The method uses the model's find(), sort() and exec() functions to return all Author objects sorted by family_name in alphabetic order. The callback passed to the exec() method is called with any errors (or null) as the first parameter, or a list of all authors on success. If there is an error it calls the next middleware function with the error value, and if not it renders the author_list(.pug) template, passing the page title and the list of authors (author_list).

+The method uses the model's `find()`, `sort()` and `exec()` functions to return all `Author` objects sorted by `family_name` in alphabetic order. The callback passed to the `exec()` method is called with any errors (or `null`) as the first parameter, or a list of all authors on success. If there is an error it calls the next middleware function with the error value, and if not it renders the **author_list**(.pug) template, passing the page `title` and the list of authors (`author_list`). -

View

+## View -

Create /views/author_list.pug and replace its content with the text below.

+Create **/views/author_list.pug** and replace its content with the text below. -
extends layout
+```js
+extends layout
 
 block content
   h1= title
@@ -42,44 +45,40 @@ block content
         |  (#{author.date_of_birth} - #{author.date_of_death})
 
     else
-      li There are no authors.
+ li There are no authors. +``` -

The view follows exactly the same pattern as our other templates.

+The view follows exactly the same pattern as our other templates. -

What does it look like?

+## What does it look like? -

Run the application and open your browser to http://localhost:3000/. Then select the All authors link. If everything is set up correctly, the page should look something like the following screenshot.

+Run the application and open your browser to . Then select the _All authors_ link. If everything is set up correctly, the page should look something like the following screenshot. -

Author List Page - Express Local Library site

+![Author List Page - Express Local Library site](locallibary_express_author_list.png) -
-

Note: The appearance of the author lifespan dates is ugly! You can improve this using the same approach as we used for the BookInstance list (adding the virtual property for the lifespan to the Author model). This time, however, there are missing dates, and references to nonexistent properties are ignored unless strict mode is in effect. moment() returns the current time, and you don't want missing dates to be formatted as if they were today. One way to deal with this is to define the body of the function that returns a formatted date so it returns a blank string unless the date actually exists. For example:

+> **備註:** The appearance of the author _lifespan_ dates is ugly! You can improve this using the [same approach](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data#date_formatting) as we used for the `BookInstance` list (adding the virtual property for the lifespan to the `Author` model). This time, however, there are missing dates, and references to nonexistent properties are ignored unless strict mode is in effect. `moment()` returns the current time, and you don't want missing dates to be formatted as if they were today. One way to deal with this is to define the body of the function that returns a formatted date so it returns a blank string unless the date actually exists. For example: +> +> `return this.date_of_birth ? moment(this.date_of_birth).format('YYYY-MM-DD') : '';` -

return this.date_of_birth ? moment(this.date_of_birth).format('YYYY-MM-DD') : '';

-
+## Genre list page—challenge\![Edit](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data$edit#Genre_list_page—challenge!) -

Genre list page—challenge!Edit

+In this section you should implement your own genre list page. The page should display a list of all genres in the database, with each genre linked to its associated detail page. A screenshot of the expected result is shown below. -

In this section you should implement your own genre list page. The page should display a list of all genres in the database, with each genre linked to its associated detail page. A screenshot of the expected result is shown below.

+![Genre List - Express Local Library site](locallibary_express_genre_list.png) -

Genre List - Express Local Library site

+The genre list controller function needs to get a list of all `Genre` instances, and then pass these to the template for rendering. -

The genre list controller function needs to get a list of all Genre instances, and then pass these to the template for rendering.

+1. You will need to edit `genre_list()` in **/controllers/genreController.js**. +2. The implementation is almost exactly the same as the `author_list()` controller. -
    -
  1. You will need to edit genre_list() in /controllers/genreController.js.
  2. -
  3. The implementation is almost exactly the same as the author_list() controller. -
      -
    • Sort the results by name, in ascending order.
    • -
    -
  4. -
  5. The template to be rendered should be named genre_list.pug.
  6. -
  7. The template to be rendered should be passed the variables title ('Genre List') and genre_list (the list of genres returned from your Genre.find() callback.
  8. -
  9. The view should match the screenshot/requirements above (this should have a very similar structure/format to the Author list view, except for the fact that genres do not have dates).
  10. -
+ - Sort the results by name, in ascending order. -

Next steps

+3. The template to be rendered should be named **genre_list.pug**. +4. The template to be rendered should be passed the variables `title` ('Genre List') and `genre_list` (the list of genres returned from your `Genre.find()` callback. +5. The view should match the screenshot/requirements above (this should have a very similar structure/format to the Author list view, except for the fact that genres do not have dates). -

Return to Express Tutorial Part 5: Displaying library data.

+## Next steps -

Proceed to the next subarticle of part 5: Genre detail page.

+Return to [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data). + +Proceed to the next subarticle of part 5: [Genre detail page](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page). diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.md index de50937d742504..88f6a795c3088a 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_detail_page/index.md @@ -3,16 +3,17 @@ title: 書本詳情頁面 slug: Learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page --- -

書本細節頁面需要呈現一本指定書本(Book)的信息, 使用它的 _id 字段值(自動產生)做為識別,接著是圖書館中書本實例(BookInstance)的信息。無論我們在哪裡呈現一個作者、種類、或書本實例,都應該連結到它的細節頁面。

+*書本細節頁面*需要呈現一本指定書本(`Book`)的信息, 使用它的 `_id` 字段值(自動產生)做為識別,接著是圖書館中書本實例(`BookInstance`)的信息。無論我們在哪裡呈現一個作者、種類、或書本實例,都應該連結到它的細節頁面。 -

Controller 控制器

+## Controller 控制器 -

打開 /controllers/bookController.js. ,找到 exported book_detail() 控制器方法,用底下的代碼置換。

+打開 **/controllers/bookController.js.** ,找到 exported `book_detail()` 控制器方法,用底下的代碼置換。 -
// Display detail page for a specific book.
+```js
+// Display detail page for a specific book.
 exports.book_detail = function(req, res, next) {
 
-    async.parallel({
+    async.parallel({
         book: function(callback) {
 
             Book.findById(req.params.id)
@@ -34,23 +35,21 @@ exports.book_detail = function(req, res, next) {
         }
         // Successful, so render.
         res.render('book_detail', { title: 'Title', book:  results.book, book_instances: results.book_instance } );
-    });
+    });
 
 };
+```
 
-
+> **備註:** 我們不需要用 require 導入 _async_ 和 _BookInstance_,當我們實作主頁面控制器的時候,我們就已經引入這些模組。 -
-

備註: 我們不需要用 require 導入 asyncBookInstance,當我們實作主頁面控制器的時候,我們就已經引入這些模組。

-
+此處的控制器方法使用 `async.parallel()`,用平行的方式找到 `Book` 以及它的相應複本 (`BookInstances`) 。這樣的處理方式,就跟上面的 _種類細節頁面_ 所說明的完全相同。 -

此處的控制器方法使用 async.parallel(),用平行的方式找到 Book 以及它的相應複本 (BookInstances) 。這樣的處理方式,就跟上面的 種類細節頁面 所說明的完全相同。

+## View 視圖 -

View 視圖

+創建 **/views/book_detail.pug** 並加入底下文字。 -

創建 /views/book_detail.pug 並加入底下文字。

- -
extends layout
+```js
+extends layout
 
 block content
   h1 #{title}: #{book.title}
@@ -59,10 +58,10 @@ block content
     a(href=book.author.url) #{book.author.name}
   p #[strong Summary:] #{book.summary}
   p #[strong ISBN:] #{book.isbn}
-  p #[strong Genre:]&nbsp;
+  p #[strong Genre:] 
     each val, index in book.genre
       a(href=val.url) #{val.name}
-      if index < book.genre.length - 1
+      if index < book.genre.length - 1
         |,
 
   div(style='margin-left:20px;margin-top:20px')
@@ -71,7 +70,7 @@ block content
     each val in book_instances
       hr
       if val.status=='Available'
-        p.text-success #{val.status}
+        p.text-success #{val.status}
       else if val.status=='Maintenance'
         p.text-danger #{val.status}
       else
@@ -79,34 +78,32 @@ block content
       p #[strong Imprint:] #{val.imprint}
       if val.status!='Available'
         p #[strong Due back:] #{val.due_back}
-      p #[strong Id:]&nbsp;
+      p #[strong Id:] 
         a(href=val.url) #{val._id}
 
     else
       p There are no copies of this book in the library.
-
- -

在這個模板裡,幾乎每個東西都在先前的章節演示過了。

+``` -
-

備註: 與該書相關的種類列表,在模板中的實作,如以下代碼。除了最後一本書之外,在與本書相關的每個種類之後,都會添加一個逗號。

+在這個模板裡,幾乎每個東西都在先前的章節演示過了。 -
  p #[strong Genre:]
-    each val, index in book.genre
-      a(href=val.url) #{val.name}
-      if index < book.genre.length - 1
-        |, 
-
+> **備註:** 與該書相關的種類列表,在模板中的實作,如以下代碼。除了最後一本書之外,在與本書相關的每個種類之後,都會添加一個逗號。 +> +> ```plain +> p #[strong Genre:] +> each val, index in book.genre +> a(href=val.url) #{val.name} +> if index < book.genre.length - 1 +> |, +> ``` -

它看起來像是?

+## 它看起來像是? -

運行本應用,並打開瀏覽器訪問 http://localhost:3000/。選擇 All books 連結,然後選擇其中一本書。如果每個東西都設定正確了,你的頁面看起來應該像是底下的截圖。

+運行本應用,並打開瀏覽器訪問 。選擇 _All books_ 連結,然後選擇其中一本書。如果每個東西都設定正確了,你的頁面看起來應該像是底下的截圖。 -

Book Detail Page - Express Local Library site

+![Book Detail Page - Express Local Library site](locallibary_express_book_detail.png) -

下一步

+## 下一步 - +- 回到 [Express 教學 5: 呈現圖書館資料](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- 繼續教學 5 下一個部分: [作者詳情頁面](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page) diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_list_page/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_list_page/index.md index 9021280c69e836..80e4e22519bbf8 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_list_page/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/book_list_page/index.md @@ -3,15 +3,16 @@ title: 書本清單頁面 slug: Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page --- -

接下做我們將實作書本列表頁面。這個頁面需要呈現數據庫中所有書本的列表,包含每本書的作者、標題,標題將成為一個超連結,連到書本詳細內容頁面。

+接下做我們將實作書本列表頁面。這個頁面需要呈現數據庫中所有書本的列表,包含每本書的作者、標題,標題將成為一個超連結,連到書本詳細內容頁面。 -

控制器

+## 控制器 -

書本列表控制器函數,需要獲取數據庫中所有 Book對象的列表,然後將這些對像傳給模板進行呈現。

+書本列表控制器函數,需要獲取數據庫中所有 `Book`對象的列表,然後將這些對像傳給模板進行呈現。 -

打開 /controllers/bookController.js. 找到導出的 book_list()控制器方法,並替換為下面的代碼。

+打開 **/controllers/bookController.js**. 找到導出的 `book_list()`控制器方法,並替換為下面的代碼。 -
// Display list of all Books.
+```js
+// Display list of all Books.
 exports.book_list = function(req, res, next) {
 
   Book.find({}, 'title author')
@@ -22,17 +23,19 @@ exports.book_list = function(req, res, next) {
       res.render('book_list', { title: 'Book List', book_list: list_books });
     });
 
-};
+}; +``` -

該方法使用模型的find()函數,返回所有 Book 對象,選擇僅返回標題 title 和作者 author,因為我們不需要其他字段(它也會返回 _id 和虛擬欄位字段)。這裡我們還調用 Book 上的 populate(),指定作者 author欄位字段 — 這將用完整的作者信息,替換儲存的書本作者 id。

+該方法使用模型的`find()`函數,返回所有 `Book `對象,選擇僅返回標題 `title `和作者 `author`,因為我們不需要其他字段(它也會返回 `_id `和虛擬欄位字段)。這裡我們還調用 `Book `上的 `populate()`,指定作者 `author`欄位字段 — 這將用完整的作者信息,替換儲存的書本作者 id。 -

成功時,傳遞給查詢的回調,將呈現 book_list(.pug) 模板,將標題 title book_list(包含作者的書本列表)作為變量傳遞。

+成功時,傳遞給查詢的回調,將呈現 **book_list**(.pug) 模板,將標題 `title `和`book_list`(包含作者的書本列表)作為變量傳遞。 -

View視圖

+## View 視圖 -

創建 /views/book_list.pug 並複制底下的文字。

+創建 **/views/book_list.pug** 並複制底下的文字。 -
extends layout
+```js
+extends layout
 
 block content
   h1= title
@@ -44,25 +47,22 @@ block content
         |  (#{book.author.name})
 
     else
-      li There are no books.
+ li There are no books. +``` -

這個視圖擴展了 layout.pug 基本模板,並覆蓋了名為 'content' 的 block 區塊 。它顯示我們從控制器傳入的標題 title(通過 render()方法),然後使用 each-in-else 語法,遍歷 book_list變量。為每本圖書創建一個列表項,以顯示書名,並作為書的詳細信息頁面的鏈接,後面跟著作者姓名。如果 book_list中沒有書,則執行 else 子句,並顯示文字 “沒有書本” 'There are no books'。

+這個視圖擴展了 **layout.pug** 基本模板,並覆蓋了名為 '**content**' 的 `block `區塊 。它顯示我們從控制器傳入的標題 `title`(通過 `render()`方法),然後使用 `each`-`in`-`else `語法,遍歷 `book_list`變量。為每本圖書創建一個列表項,以顯示書名,並作為書的詳細信息頁面的鏈接,後面跟著作者姓名。如果 `book_list`中沒有書,則執行 `else` 子句,並顯示文字 “沒有書本” 'There are no books'。 -
-

備註: 我們使用 book.url,為每本書提供詳細記錄鏈接(我們已經實現了此路由,但尚未實現此頁面)。這是 Book 模型的一個虛擬屬性,它使用模型實例的 _id 字段,生成唯一的 URL 路徑。

-
+> **備註:** 我們使用 `book.url`,為每本書提供詳細記錄鏈接(我們已經實現了此路由,但尚未實現此頁面)。這是 `Book` 模型的一個虛擬屬性,它使用模型實例的 `_id` 字段,生成唯一的 URL 路徑。 -

在這裡,我們感興趣的是,每本書被定義為兩行,第二行使用管道(上面高亮顯示)。這種方法是必要的,因為如果作者姓名位於上一行,那麼它將成為超鏈接的一部分。

+在這裡,我們感興趣的是,每本書被定義為兩行,第二行使用管道(上面高亮顯示)。這種方法是必要的,因為如果作者姓名位於上一行,那麼它將成為超鏈接的一部分。 -

它看起來像是?

+## 它看起來像是? -

運行本應用 (參見 測試路由 有相關的命令) ,並打開你的瀏覽器,訪問 http://localhost:3000/。然後選擇所有書本連結 All books。如果每樣東西都設定正確了,你的網站看起來應該像底下的截圖。

+運行本應用 (參見 [測試路由](/zh-TW/docs/Learn/Server-side/Express_Nodejs/routes#Testing_the_routes) 有相關的命令) ,並打開你的瀏覽器,訪問 。然後選擇所有書本連結 _All books_。如果每樣東西都設定正確了,你的網站看起來應該像底下的截圖。 -

Book List Page - Express Local Library site

+![Book List Page - Express Local Library site](new_book_list.png) -

下一步

+## 下一步 - +- 回到 [Express 教學 5: 呈現圖書館資料](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- 繼續教學 5 下個部分: [書本實例清單頁面](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page) diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.md index 1c3c2140d8c343..7d913cf1dda0f0 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_detail_page_and_challenge/index.md @@ -5,18 +5,19 @@ slug: >- translation_of: >- Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge --- -

書本實例詳情頁面

+## 書本實例詳情頁面 -

BookInstance 詳情頁面,需要呈現每一個 BookInstance 的信息,用 _id 欄位字段值(自動產生)做識別。它包含了 Book 名稱 (也是一個連結,連到 書本細節頁面),接著是紀錄中的其它的信息。

+`BookInstance` 詳情頁面,需要呈現每一個 `BookInstance `的信息,用 `_id` 欄位字段值(自動產生)做識別。它包含了 `Book` 名稱 (也是一個連結,連到 *書本細節*頁面),接著是紀錄中的其它的信息。 -

Controller 控制器

+### Controller 控制器 -

打開 /controllers/bookinstanceController.js. 找到 exported bookinstance_detail() 控制器方法,並用底下代碼置換。

+打開 **/controllers/bookinstanceController.js**. 找到 exported `bookinstance_detail()` 控制器方法,並用底下代碼置換。 -
// Display detail page for a specific BookInstance.
+```js
+// Display detail page for a specific BookInstance.
 exports.bookinstance_detail = function(req, res, next) {
 
-    BookInstance.findById(req.params.id)
+    BookInstance.findById(req.params.id)
     .populate('book')
     .exec(function (err, bookinstance) {
       if (err) { return next(err); }
@@ -27,22 +28,23 @@ exports.bookinstance_detail = function(req, res, next) {
         }
       // Successful, so render.
       res.render('bookinstance_detail', { title: 'Book:', bookinstance:  bookinstance});
-    })
+    })
 
 };
-
+``` -

該方法使用從 URL(使用路由)中提取的特定書本實例的ID,調用BookInstance.findById(),並通過請求參數(req.params.id),在控制器中訪問。然後調用populate()來獲取相關 Book 的詳細信息。

+該方法使用從 URL(使用路由)中提取的特定書本實例的 ID,調用`BookInstance.findById()`,並通過請求參數(`req.params.id`),在控制器中訪問。然後調用`populate()`來獲取相關 `Book `的詳細信息。 -

View 視圖

+### View 視圖 -

創建 /views/bookinstance_detail.pug ,並複製下面的內容。

+創建 **/views/bookinstance_detail.pug** ,並複製下面的內容。 -
extends layout
+```js
+extends layout
 
 block content
 
-  h1 ID: #{bookinstance._id}
+  h1 ID: #{bookinstance._id}
 
   p #[strong Title:]
     a(href=bookinstance.book.url) #{bookinstance.book.title}
@@ -58,34 +60,28 @@ block content
 
   if bookinstance.status!='Available'
     p #[strong Due back:] #{bookinstance.due_back}
-
+``` -

本模組中的所有東西,都在先前的章節演示過了。

+本模組中的所有東西,都在先前的章節演示過了。 -

它看起來像是?

+### 它看起來像是? -

運行本應用,並打開瀏覽器訪問 http://localhost:3000//。選擇 All book-instances 連結,然後選擇其中一本。如果每個東西都設定正確了,你的網站看起來應該像是底下的截圖。

+運行本應用,並打開瀏覽器訪問 /。選擇 _All book-instances_ 連結,然後選擇其中一本。如果每個東西都設定正確了,你的網站看起來應該像是底下的截圖。 -

BookInstance Detail Page - Express Local Library site

+![BookInstance Detail Page - Express Local Library site](locallibary_express_bookinstance_detail.png) -

自我挑戰

+## 自我挑戰 -

目前,我們網站上顯示的大多數日期,都使用默認的 JavaScript 格式(例如 Tue Dec 06 2016 15:49:58 GMT+1100 (AUS東部夏令時間))。本文的挑戰,是改善作者Author生命週期日期顯示的外觀信息(死亡/誔生日期)和BookInstance詳細信息頁面,使用格式:December 6th, 2016。

+目前,我們網站上顯示的大多數日期,都使用默認的 JavaScript 格式(例如 _Tue Dec 06 2016 15:49:58 GMT+1100_ (AUS 東部夏令時間))。本文的挑戰,是改善作者`Author`生命週期日期顯示的外觀信息(死亡/誔生日期)和*BookInstance 詳細信息*頁面,使用格式:December 6th, 2016。 -
-

備註: 您可以使用與我們用於 Book Instance List 的相同方法(將生命週期的虛擬屬性,添加到Author模型,並使用 moment 來設置日期字符串的格式)。

-
+> **備註:** 您可以使用與我們用於 _Book Instance List_ 的相同方法(將生命週期的虛擬屬性,添加到`Author`模型,並使用 [moment ](https://www.npmjs.com/package/moment)來設置日期字符串的格式)。 -

這個挑戰的要求:

+這個挑戰的要求: -
    -
  1. 用 BookInstance 詳細信息頁面中的 due_back_formatted 替換 due_back
  2. -
  3. 更新作者模塊以添加壽命虛擬屬性。壽命應該有兩個值: date_of_birth - date_of_death,這兩個值的格式與 BookInstance.due_back_formatted的日期格式相同。
  4. -
  5. 在當前使用 date_of_birthdate_of_death的所有視圖中,使用 Author.lifespan
  6. -
+1. 用 BookInstance 詳細信息頁面中的 `due_back_formatted` 替換 `due_back`。 +2. 更新作者模塊以添加壽命虛擬屬性。壽命應該有兩個值: _date_of_birth - date_of_death_,這兩個值的格式與 `BookInstance.due_back_formatted`的日期格式相同。 +3. 在當前使用 `date_of_birth` 和 `date_of_death`的所有視圖中,使用 `Author.lifespan` 。 -

下一步

+## 下一步 - +- 回到 [Express 教學 5: 呈現圖書館資料](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.md index 1bf5dfcf73c57c..c6cd89ebd9c546 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/bookinstance_list_page/index.md @@ -3,15 +3,16 @@ title: 書本實例清單頁面 slug: Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page --- -

接下來,我們將實作圖書館中所有書本實例 (BookInstance) 的列表頁面。這個頁面需要包含與每個 BookInstance (鏈接到其詳細信息頁面) 關聯的書本 Book 標題,以及 BookInstance模型中的其他信息,包含每個副本的狀態,印記和唯一ID。唯一ID的文字,應該鏈接到 BookInstance 詳細信息頁面。

+接下來,我們將實作圖書館中所有書本實例 (`BookInstance`) 的列表頁面。這個頁面需要包含與每個 `BookInstance` (鏈接到其詳細信息頁面) 關聯的書本 `Book` 標題,以及 `BookInstance`模型中的其他信息,包含每個副本的狀態,印記和唯一 ID。唯一 ID 的文字,應該鏈接到 `BookInstance` 詳細信息頁面。 -

Controller 控制器

+## Controller 控制器 -

BookInstance列表控制器函數,需要獲取所有書本實例的列表,填充關聯的書本信息,然後將列表傳遞給模板以進行呈現。

+`BookInstance`列表控制器函數,需要獲取所有書本實例的列表,填充關聯的書本信息,然後將列表傳遞給模板以進行呈現。 -

打開 /controllers/bookinstanceController.js。找到導出的 bookinstance_list()控制器方法,並用以下代碼替換它(更改後的代碼以粗體顯示)。

+打開 **/controllers/bookinstanceController.js**。找到導出的 `bookinstance_list()`控制器方法,並用以下代碼替換它(更改後的代碼以**粗體**顯示)。 -
// Display list of all BookInstances.
+```js
+// Display list of all BookInstances.
 exports.bookinstance_list = function(req, res, next) {
 
   BookInstance.find()
@@ -22,17 +23,19 @@ exports.bookinstance_list = function(req, res, next) {
       res.render('bookinstance_list', { title: 'Book Instance List', bookinstance_list: list_bookinstances });
     });
 
-};
+}; +``` -

此方法使用模型的find()函數,返回所有 BookInstance對象。然後它將一個調用,以菊花鏈方式連接到 populate(),附加書本 book欄位字段,這將使用完整的 Book文檔,替換每個 BookInstance儲存的書本 ID。

+此方法使用模型的`find()`函數,返回所有 `BookInstance`對象。然後它將一個調用,以菊花鏈方式連接到 `populate()`,附加書本 `book`欄位字段,這將使用完整的 `Book`文檔,替換每個 `BookInstance`儲存的書本 ID。 -

成功時,傳遞給查詢的回調,會呈現 bookinstance_list (.pug)模板,並將標題title和書籍實例列表 bookinstance_list作為變量傳遞。

+成功時,傳遞給查詢的回調,會呈現 **bookinstance_list** (.pug)模板,並將標題`title`和書籍實例列表 `bookinstance_list`作為變量傳遞。 -

View 視圖

+## View 視圖 -

創建 /views/bookinstance_list.pug ,並複制貼上底下的文字。

+創建 **/views/bookinstance_list.pug** ,並複制貼上底下的文字。 -
extends layout
+```js
+extends layout
 
 block content
   h1= title
@@ -51,21 +54,18 @@ block content
           span  (Due: #{val.due_back} )
 
     else
-      li There are no book copies in this library.
+ li There are no book copies in this library. +``` -

這個視圖與其他視圖非常相似。它擴展了佈局,替換內容區塊,顯示從控制器傳入的標題 title,並遍歷 bookinstance_list 中的所有書籍副本。對於每個副本,我們都會顯示它的狀態(用顏色編碼),如果書本不可用,則顯示其預期返回日期。這裡引入了一個新功能 — 我們可以在標籤之後使用點符號表示法,來指定一個類別。因此,span.text-success 將被編譯為 <span class="text-success"> (也可以用 Pug 編寫為 span(class="text-success")。

+這個視圖與其他視圖非常相似。它擴展了佈局,替換內容區塊,顯示從控制器傳入的標題 `title`,並遍歷 `bookinstance_list` 中的所有書籍副本。對於每個副本,我們都會顯示它的狀態(用顏色編碼),如果書本不可用,則顯示其預期返回日期。這裡引入了一個新功能 — 我們可以在標籤之後使用點符號表示法,來指定一個類別。因此,`span.text-success `將被編譯為 <`span class="text-success"`> (也可以用 Pug 編寫為 `span(class="text-success"`)。 -

+## 它看起來像是? -

它看起來像是?

+運行本應用,打開瀏覽器訪問 ,然後選擇 All book-instances 連結。假如每個東西都設定正確了,你的網站看起來應該像是底下的截圖。 -

運行本應用,打開瀏覽器訪問 http://localhost:3000/,然後選擇 All book-instances 連結。假如每個東西都設定正確了,你的網站看起來應該像是底下的截圖。

+![BookInstance List Page - Express Local Library site](locallibary_express_bookinstance_list.png) -

BookInstance List Page - Express Local Library site

+## 下一步 -

下一步

- - +- 回到 [Express 教學 5: 呈現圖書館資料](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- 繼續教學 5 下一個部分: [格式化日期 - 使用 moment](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment). diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.md index 6b0163461d8703..4e125f0cf7b17c 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/date_formatting_using_moment/index.md @@ -3,58 +3,54 @@ title: 使用 moment 做日期格式化 slug: Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment --- -

我們模型的日期預設呈現很難看: Tue Dec 06 2016 15:49:58 GMT+1100 (AUS Eastern Daylight Time)。在本節中,我們將展示如何更新上一節中的 書本實例 BookInstance 列表頁面,以更友好的格式顯示due_date欄位字段:December 6th, 2016。

+我們模型的日期預設呈現很難看: _Tue Dec 06 2016 15:49:58 GMT+1100 (AUS Eastern Daylight Time)_。在本節中,我們將展示如何更新上一節中的 _書本實例 BookInstance 列表頁面_,以更友好的格式顯示`due_date`欄位字段:December 6th, 2016。 -

我們將使用的方法,是在我們的BookInstance模型中,創建一個返回格式化日期的虛擬屬性。我們將使用 moment 來做實際的格式化,這是一個輕量級 JavaScript日期庫,用於解析,驗證,操作和格式化日期。

+我們將使用的方法,是在我們的`BookInstance`模型中,創建一個返回格式化日期的虛擬屬性。我們將使用 [moment](https://www.npmjs.com/package/moment) 來做實際的格式化,這是一個輕量級 JavaScript 日期庫,用於解析,驗證,操作和格式化日期。 -
-

備註: 我們可以直接在 Pug 模板中,使用 moment 格式化字符串,或者可以在許多其它地方格式化字符串。使用虛擬屬性,可以使我們獲得格式化的日期,這與我們當前獲取 due_date 的方式完全相同。

-
+> **備註:** 我們可以直接在 Pug 模板中,使用 moment 格式化字符串,或者可以在許多其它地方格式化字符串。使用虛擬屬性,可以使我們獲得格式化的日期,這與我們當前獲取 `due_date` 的方式完全相同。 -

安裝 moment

+## 安裝 moment -

在項目的根目錄,輸入下列命令

+在項目的根目錄,輸入下列命令 -

+```bash +npm install moment +``` -
npm install moment
+## 創建虛擬屬性 -

創建虛擬屬性

+1. 打開 **./models/bookinstance.js**. +2. 在此頁面最上方,引入 _moment_. -
    -
  1. 打開 ./models/bookinstance.js.
  2. -
  3. 在此頁面最上方,引入 moment. -
    var moment = require('moment');
    -
  4. -
+ ```js + var moment = require('moment'); + ``` -

在 url 屬性後面,加入虛擬屬性 due_back_formatted

+在 url 屬性後面,加入虛擬屬性 `due_back_formatted` 。 -
BookInstanceSchema
+```js
+BookInstanceSchema
 .virtual('due_back_formatted')
 .get(function () {
   return moment(this.due_back).format('MMMM Do, YYYY');
-});
+}); +``` -
-

備註: 格式化方法可以使用幾乎任何模式顯示日期。moment文檔中,可以找到表示不同日期組件的語法。

-
+> **備註:** 格式化方法可以使用幾乎任何模式顯示日期。[moment](http://momentjs.com/docs/#/displaying/)文檔中,可以找到表示不同日期組件的語法。 -

更新視圖

+## 更新視圖 -

打開 /views/bookinstance_list.pug ,然後用 due_back_formatted 取代 due_back

+打開 **/views/bookinstance_list.pug** ,然後用 `due_back_formatted` 取代 `due_back` 。 -
      if val.status!='Available'
+```js
+      if val.status!='Available'
         //span  (Due: #{val.due_back} )
-        span  (Due: #{val.due_back_formatted} )       
+ span (Due: #{val.due_back_formatted} ) +``` -

這就是本章節的全部了。如果你訪問側邊欄的 All book-instances ,你應該會看到所有的歸還日期都更吸引人了!

+這就是本章節的全部了。如果你訪問側邊欄的 _All book-instances_ ,你應該會看到所有的歸還日期都更吸引人了! -

下一步

+## 下一步 - - -

+- 回到 [Express 教學 5: 呈現圖書館資料](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- 繼續教學 5 的下一個部分: [Author list page and Genre list page challenge](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page). diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.md index 506261cea5e504..c793d8461bd5ac 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/flow_control_using_async/index.md @@ -3,52 +3,50 @@ title: 使用 async 進行非同步流控制 slug: Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async --- -

有些本地圖書館網頁的控制器代碼,會依賴多重非同步要求的結果,可能會需要以某種特定次序運行,或者以平行方式運行。為了管理流控制,並在我們所有需要用到的信息,都已經可以取用的時候,再繪製網頁,我們將使用許多人採用的 node async 模組。

+有些*本地圖書館*網頁的控制器代碼,會依賴多重非同步要求的結果,可能會需要以某種特定次序運行,或者以平行方式運行。為了管理流控制,並在我們所有需要用到的信息,都已經可以取用的時候,再繪製網頁,我們將使用許多人採用的 node [async](https://www.npmjs.com/package/async) 模組。 -
-

備註: 在 JavaScript 中有許多其他方法,可以管理異步行為和流控制,包括相對較新的 JavaScript 語言功能,如 Promises

-
+> **備註:** 在 JavaScript 中有許多其他方法,可以管理異步行為和流控制,包括相對較新的 JavaScript 語言功能,如 [Promises](/en-US/docs/Mozilla/Add-ons/Techniques/Promises)。 -

Async 有很多有用的方法(請查看文檔)。一些最重要的功能是:

+Async 有很多有用的方法(請查看[文檔](http://caolan.github.io/async/docs.html))。一些最重要的功能是: -
    -
  • async.parallel() 執行必須並行執行的任何操作。
  • -
  • async.series() 用於當需要確保異步操作是序列執行的。
  • -
  • async.waterfall() 用於必須序列運行的操作,每個操作取決於前面操作的結果。
  • -
+- [`async.parallel()`](http://caolan.github.io/async/docs.html#parallel) 執行必須並行執行的任何操作。 +- [`async.series()`](http://caolan.github.io/async/docs.html#series) 用於當需要確保異步操作是序列執行的。 +- [`async.waterfall()`](http://caolan.github.io/async/docs.html#waterfall) 用於必須序列運行的操作,每個操作取決於前面操作的結果。 -

為什麼需要這麼做?

+## 為什麼需要這麼做? -

我們在 Express 中使用的大多數方法,都是異步的 - 您指定要執行的操作,傳遞回調。該方法立即返回,並在請求的操作完成時,調用回調。按照 Express 中的慣例,回調函數將錯誤值作為第一個參數傳遞(或成功時為 null),並將函數的結果(如果有的話)作為第二個參數傳遞。

+我們在 _Express_ 中使用的大多數方法,都是異步的 - 您指定要執行的操作,傳遞回調。該方法立即返回,並在請求的操作完成時,調用回調。按照 _Express_ 中的慣例,回調函數將*錯誤值*作為第一個參數傳遞(或成功時為 `null`),並將函數的結果(如果有的話)作為第二個參數傳遞。 -

如果控制器只需要執行一個異步操作,來獲取呈現頁面所需的信息,那麼實現很簡單 - 我們只需在回調中呈現模板。下面的代碼片段,顯示了一個函數,該函數呈現模型 SomeModel 的計數(使用Mongoose count()方法):

+如果控制器只需要*執行**一個**異步操作*,來獲取呈現頁面所需的信息,那麼實現很簡單 - 我們只需在回調中呈現模板。下面的代碼片段,顯示了一個函數,該函數呈現模型 `SomeModel` 的計數(使用 Mongoose [`count()`](http://mongoosejs.com/docs/api.html#model_Model.count)方法): -
exports.some_model_count = function(req, res, next) {
+```js
+exports.some_model_count = function(req, res, next) {
 
-  SomeModel.count({ a_model_field: 'match_value' }, function (err, count) {
+  SomeModel.count({ a_model_field: 'match_value' }, function (err, count) {
     // ... do something if there is an err
 
     // On success, render the result by passing count into the render function (here, as the variable 'data').
     res.render('the_template', { data: count } );
   });
-}
-
+} +``` -

但是,如果您需要進行多個異步查詢,並且在完成所有操作之前,無法呈現頁面,該怎麼辦?一個單純的實現可以用 “菊花鏈” 連接請求,在先前請求的回調中,啟動後續請求,並在最終回調中呈現響應。這種方法的問題,是我們的請求必須串行運行,即使並行運行它們可能更有效。這也可能導致複雜的嵌套代碼,通常稱為回調地獄

+但是,如果您需要進行**多個**異步查詢,並且在完成所有操作之前,無法呈現頁面,該怎麼辦?一個單純的實現可以用 “菊花鏈” 連接請求,在先前請求的回調中,啟動後續請求,並在最終回調中呈現響應。這種方法的問題,是我們的請求必須串行運行,即使並行運行它們可能更有效。這也可能導致複雜的嵌套代碼,通常稱為[回調地獄](http://callbackhell.com/)。 -

一個更好的解決方案,是並行執行所有請求,然後在所有查詢完成後執行單個回調。這是 Async 模塊簡化的流操作!

+一個更好的解決方案,是並行執行所有請求,然後在所有查詢完成後執行單個回調。這是 _Async_ 模塊簡化的流操作! -

Asynchronous operations in parallel

+## Asynchronous operations in parallel -

The method async.parallel() is used to run multiple asynchronous operations in parallel.

+The method [`async.parallel()`](http://caolan.github.io/async/docs.html#parallel) is used to run multiple asynchronous operations in parallel. -

The first argument to async.parallel() is a collection of the asynchronous functions to run (an array, object or other iterable). Each function is passed a callback(err, result) which it must call on completion with an error err (which can be null) and an optional results value.

+The first argument to `async.parallel()` is a collection of the asynchronous functions to run (an array, object or other iterable). Each function is passed a `callback(err, result)` which it must call on completion with an error `err` (which can be `null`) and an optional `results` value. -

The optional second argument to async.parallel() is a callback that will be run when all the functions in the first argument have completed. The callback is invoked with an error argument and a result collection that contains the results of the individual asynchronous operations. The result collection is of the same type as the first argument (i.e. if you pass an array of asynchronous functions, the final callback will be invoked with an array of results). If any of the parallel functions reports an error the callback is invoked early (with the error value).

+The optional second argument to `async.parallel()` is a callback that will be run when all the functions in the first argument have completed. The callback is invoked with an error argument and a result collection that contains the results of the individual asynchronous operations. The result collection is of the same type as the first argument (i.e. if you pass an array of asynchronous functions, the final callback will be invoked with an array of results). If any of the parallel functions reports an error the callback is invoked early (with the error value). -

The example below shows how this works when we pass an object as the first argument. As you can see, the results are returned in an object with the same property names as the original functions that were passed in.

+The example below shows how this works when we pass an object as the first argument. As you can see, the results are _returned_ in an object with the same property names as the original functions that were passed in. -
async.parallel({
+```js
+async.parallel({
   one: function(callback) { ... },
   two: function(callback) { ... },
   ...
@@ -58,15 +56,17 @@ translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_us
   function(err, results) {
     // 'results' is now equal to: {one: 1, two: 2, ..., something_else: some_value}
   }
-);
+); +``` -

If you instead pass an array of functions as the first argument, the results will be an array (the array order results will match the original order that the functions were declared—not the order in which they completed).

+If you instead pass an array of functions as the first argument, the results will be an array (the array order results will match the original order that the functions were declared—not the order in which they completed). -

Asynchronous operations in series

+## Asynchronous operations in series -

The method async.series() is used to run multiple asynchronous operations in sequence, when subsequent functions do not depend on the output of earlier functions. It is essentially declared and behaves in the same way as async.parallel().

+The method [`async.series()`](http://caolan.github.io/async/docs.html#series) is used to run multiple asynchronous operations in sequence, when subsequent functions do not depend on the output of earlier functions. It is essentially declared and behaves in the same way as `async.parallel()`. -
async.series({
+```js
+async.series({
   one: function(callback) { ... },
   two: function(callback) { ... },
   ...
@@ -74,42 +74,44 @@ translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_us
   },
   // optional callback after the last asynchronous function completes.
   function(err, results) {
-    // 'results' is now equals to: {one: 1, two: 2, ..., something_else: some_value} 
+    // 'results' is now equals to: {one: 1, two: 2, ..., something_else: some_value}
   }
-);
+); +``` -
-

Note: The ECMAScript (JavaScript) language specification states that the order of enumeration of an object is undefined, so it is possible that the functions will not be called in the same order as you specify them on all platforms. If the order really is important, then you should pass an array instead of an object, as shown below.

-
+> **備註:** The ECMAScript (JavaScript) language specification states that the order of enumeration of an object is undefined, so it is possible that the functions will not be called in the same order as you specify them on all platforms. If the order really is important, then you should pass an array instead of an object, as shown below. -
async.series([
+```js
+async.series([
   function(callback) {
     // do some stuff ...
     callback(null, 'one');
   },
   function(callback) {
-    // do some more stuff ... 
+    // do some more stuff ...
     callback(null, 'two');
   }
  ],
   // optional callback
   function(err, results) {
-  // results is now equal to ['one', 'two'] 
+  // results is now equal to ['one', 'two']
   }
-);
+); +``` -

Dependent asynchronous operations in series

+## Dependent asynchronous operations in series -

The method async.waterfall() is used to run multiple asynchronous operations in sequence when each operation is dependent on the result of the previous operation.

+The method [`async.waterfall()`](http://caolan.github.io/async/docs.html#waterfall) is used to run multiple asynchronous operations in sequence when each operation is dependent on the result of the previous operation. -

The callback invoked by each asynchronous function contains null for the first argument and results in subsequent arguments. Each function in the series takes the results arguments of the previous callback as the first parameters, and then a callback function. When all operations are complete, a final callback is invoked with the result of the last operation. The way this works is more clear when you consider the code fragment below (this example is from the async documentation):

+The callback invoked by each asynchronous function contains `null` for the first argument and results in subsequent arguments. Each function in the series takes the results arguments of the previous callback as the first parameters, and then a callback function. When all operations are complete, a final callback is invoked with the result of the last operation. The way this works is more clear when you consider the code fragment below (this example is from the _async_ documentation): -
async.waterfall([
+```js
+async.waterfall([
   function(callback) {
     callback(null, 'one', 'two');
   },
   function(arg1, arg2, callback) {
-    // arg1 now equals 'one' and arg2 now equals 'two' 
+    // arg1 now equals 'one' and arg2 now equals 'two'
     callback(null, 'three');
   },
   function(arg1, callback) {
@@ -119,17 +121,18 @@ translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_us
 ], function (err, result) {
   // result now equals 'done'
 }
-);
+); +``` -

Installing async

+## Installing async -

Install the async module using the NPM package manager so that we can use it in our code. You do this in the usual way, by opening a prompt in the root of the LocalLibrary project and enter the following command:

+Install the async module using the NPM package manager so that we can use it in our code. You do this in the usual way, by opening a prompt in the root of the _LocalLibrary_ project and enter the following command: -
npm install async
+```bash +npm install async +``` -

Next steps

+## Next steps - +- Return to [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data). +- Proceed to the next subarticle of Part 5: [Template primer](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer). diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.md index 8e75ddb4e36b77..1b49d41e5a60e2 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/genre_detail_page/index.md @@ -3,24 +3,24 @@ title: Genre detail page slug: Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page --- -

種類細節頁面,需要利用_id 字段值 (自動生成) ,以呈現特定種類實例的信息。此頁面應該呈現種類名稱,各個種類的所有書本列表(每本書都連結到書本的細節頁面)。

+種類*細節*頁面,需要利用`_id` 字段值 (自動生成) ,以呈現特定種類實例的信息。此頁面應該呈現種類名稱,各個種類的所有書本列表(每本書都連結到書本的細節頁面)。 -

+## Controller 控制器 -

Controller 控制器

+打開 **/controllers/genreController.js** ,並在檔案最上方引用 _async_ 和 _Book_ 模組。 -

打開 /controllers/genreController.js ,並在檔案最上方引用 asyncBook 模組。

- -
var Book = require('../models/book');
+```js
+var Book = require('../models/book');
 var async = require('async');
-
+``` -

Find the exported genre_detail() controller method and replace it with the following code.

+Find the exported ` genre_detail``() ` controller method and replace it with the following code. -
// Display detail page for a specific Genre.
+```js
+// Display detail page for a specific Genre.
 exports.genre_detail = function(req, res, next) {
 
-    async.parallel({
+    async.parallel({
         genre: function(callback) {
             Genre.findById(req.params.id)
               .exec(callback);
@@ -40,39 +40,39 @@ exports.genre_detail = function(req, res, next) {
         }
         // Successful, so render
         res.render('genre_detail', { title: 'Genre Detail', genre: results.genre, genre_books: results.genre_books } );
-    });
+    });
 
 };
-
- -

The method uses async.parallel() to query the genre name and its associated books in parallel, with the callback rendering the page when (if) both requests complete successfully.

+``` -

The ID of the required genre record is encoded at the end of the URL and extracted automatically based on the route definition (/genre/:id). The ID is accessed within the controller via the request parameters: req.params.id. It is used in Genre.findById() to get the current genre. It is also used to get all Book objects that have the genre ID in their genre field: Book.find({ 'genre': req.params.id }).

+The method uses `async.parallel()` to query the genre name and its associated books in parallel, with the callback rendering the page when (if) both requests complete successfully. -
-

備註: If the genre does not exist in the database (i.e. it may have been deleted) then findById() will return successfully with no results. In this case we want to display a "not found" page, so we create an Error object and pass it to the next middleware function in the chain.

+The ID of the required genre record is encoded at the end of the URL and extracted automatically based on the route definition (**/genre/:id**). The ID is accessed within the controller via the request parameters: `req.params.id`. It is used in `Genre.findById()` to get the current genre. It is also used to get all `Book` objects that have the genre ID in their `genre` field: `Book.find({ 'genre': req.params.id })`. -
if (results.genre==null) { // No results.
-    var err = new Error('Genre not found');
-    err.status = 404;
-    return next(err);
-}
-
+> **備註:** If the genre does not exist in the database (i.e. it may have been deleted) then `findById()` will return successfully with no results. In this case we want to display a "not found" page, so we create an `Error` object and pass it to the `next` middleware function in the chain. +> +> ```js +> if (results.genre==null) { // No results. +> var err = new Error('Genre not found'); +> err.status = 404; +> return next(err); +> } +> ``` +> +> The message will then propagate through to our error handling code (this was set up when we [generated the app skeleton](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website#error_handling) - for more information see [Handling Errors](/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction#Handling_errors)). -

The message will then propagate through to our error handling code (this was set up when we generated the app skeleton - for more information see Handling Errors).

-
+The rendered view is **genre_detail** and it is passed variables for the `title`, `genre` and the list of books in this genre (`genre_books`). -

The rendered view is genre_detail and it is passed variables for the title, genre and the list of books in this genre (genre_books).

+## View -

View

+Create **/views/genre_detail.pug** and fill it with the text below: -

Create /views/genre_detail.pug and fill it with the text below:

- -
extends layout
+```js
+extends layout
 
 block content
 
-  h1 Genre: #{genre.name}
+  h1 Genre: #{genre.name}
 
   div(style='margin-left:20px;margin-top:20px')
 
@@ -86,38 +86,37 @@ block content
 
     else
       p This genre has no books
-
- -

The view is very similar to all our other templates. The main difference is that we don't use the title passed in for the first heading (though it is used in the underlying layout.pug template to set the page title).

- -

What does it look like?

- -

Run the application and open your browser to http://localhost:3000/. Select the All genres link, then select one of the genres (e.g. "Fantasy"). If everything is set up correctly, your page should look something like the following screenshot.

- -

Genre Detail Page - Express Local Library site

- -
-

備註: You might get an error similar to this:

- -
Cast to ObjectId failed for value " 59347139895ea23f9430ecbb" at path "_id" for model "Genre"
-
- -

This is a mongoose error coming from the req.params.id. To solve this problem, first you need to require mongoose on the genreController.js page like this:

- -
 var mongoose = require('mongoose');
-
- -

Then use mongoose.Types.ObjectId() to convert the id to a that can be used. For example:

- -
exports.genre_detail = function(req, res, next) {
-    var id = mongoose.Types.ObjectId(req.params.id);
-    ...
-
-
- -

Next steps

- - +``` + +The view is very similar to all our other templates. The main difference is that we don't use the `title` passed in for the first heading (though it is used in the underlying **layout.pug** template to set the page title). + +## What does it look like? + +Run the application and open your browser to . Select the _All genres_ link, then select one of the genres (e.g. "Fantasy"). If everything is set up correctly, your page should look something like the following screenshot. + +![Genre Detail Page - Express Local Library site](locallibary_express_genre_detail.png) + +> **備註:** You might get an error similar to this: +> +> ```bash +> Cast to ObjectId failed for value " 59347139895ea23f9430ecbb" at path "_id" for model "Genre" +> ``` +> +> This is a mongoose error coming from the **req.params.id**. To solve this problem, first you need to require mongoose on the **genreController.js** page like this: +> +> ```js +> var mongoose = require('mongoose'); +> ``` +> +> Then use **mongoose.Types.ObjectId()** to convert the id to a that can be used. For example: +> +> ```js +> exports.genre_detail = function(req, res, next) { +> var id = mongoose.Types.ObjectId(req.params.id); +> ... +> ``` + +## Next steps + +- Return to [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data). +- Proceed to the next subarticle of part 5: [Book detail page](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page). diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/home_page/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/home_page/index.md index c1ba2cf04c32d4..cbbdff89279fd3 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/home_page/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/home_page/index.md @@ -3,49 +3,56 @@ title: 主頁 slug: Learn/Server-side/Express_Nodejs/Displaying_data/Home_page translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Home_page --- -

我們創建的第一個頁面,是網站的主頁面,可以從網站的根目錄 ('/') ,或者 catalog 的根目錄 (catalog/) 訪問。這將呈現一些網站的靜態文字描述,以及動態計算數據庫中不同記錄類型的“計數”。

+我們創建的第一個頁面,是網站的主頁面,可以從網站的根目錄 (`'/'`) ,或者 catalog 的根目錄 (`catalog/`) 訪問。這將呈現一些網站的靜態文字描述,以及動態計算數據庫中不同記錄類型的“計數”。 -

我們已經為主頁創建了一個路由。為了完成頁面,我們需要更新控制器函數,以從數據庫中提取記錄的“計數”,並創建一個可用於呈現頁面的視圖(模板)。

+我們已經為主頁創建了一個路由。為了完成頁面,我們需要更新控制器函數,以從數據庫中提取記錄的“計數”,並創建一個可用於呈現頁面的視圖(模板)。 -

路由

+## 路由 -

前面的教程,我們創建 index 頁面路由。此處要提醒的是,所有的路由函式,都定義在 /routes/catalog.js:

+在[前面的教程](/zh-TW/docs/Learn/Server-side/Express_Nodejs/routes),我們創建 index 頁面路由。此處要提醒的是,所有的路由函式,都定義在 **/routes/catalog.js**: -
// GET catalog home page.
-router.get('/', book_controller.index);  //This actually maps to /catalog/ because we import the route with a /catalog prefix
+```js +// GET catalog home page. +router.get('/', book_controller.index); //This actually maps to /catalog/ because we import the route with a /catalog prefix +``` -

Where the callback function parameter (book_controller.index) is defined in /controllers/bookController.js:

+Where the callback function parameter (`book_controller.index`) is defined in **/controllers/bookController.js**: -
exports.index = function(req, res, next) {
+```js
+exports.index = function(req, res, next) {
     res.send('NOT IMPLEMENTED: Site Home Page');
-}
- -

It is this controller function that we extend to get information from our models and then render it using a template (view).

+} +``` -

Controller

+It is this controller function that we extend to get information from our models and then render it using a template (view). -

The index controller function needs to fetch information about how many Book, BookInstance, available BookInstance, Author, and Genre records we have in the database, render this data in a template to create an HTML page, and then return it in an HTTP response.

+## Controller -
-

Note: We use the countDocuments() method to get the number of instances of each model. This is called on a model with an optional set of conditions to match against in the first argument and a callback in the second argument (as discussed in Using a Database (with Mongoose), and you can also return a Query and then execute it with a callback later. The callback will be returned when the database returns the count, with an error value (or null) as the first parameter and the count of records (or null if there was an error) as the second parameter.

+The index controller function needs to fetch information about how many `Book`, `BookInstance`, available `BookInstance`, `Author`, and `Genre` records we have in the database, render this data in a template to create an HTML page, and then return it in an HTTP response. -
SomeModel.countDocuments({ a_model_field: 'match_value' }, function (err, count) {
- // ... do something if there is an err
- // ... do something with the count if there was no error
- });
-
+> **備註:** We use the [`countDocuments()`](http://mongoosejs.com/docs/api.html#model_Model.countDocuments) method to get the number of instances of each model. This is called on a model with an optional set of conditions to match against in the first argument and a callback in the second argument (as discussed in [Using a Database (with Mongoose)](/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose), and you can also return a `Query` and then execute it with a callback later. The callback will be returned when the database returns the count, with an error value (or `null`) as the first parameter and the count of records (or null if there was an error) as the second parameter. +> +> ```js +> SomeModel.countDocuments({ a_model_field: 'match_value' }, function (err, count) { +> // ... do something if there is an err +> // ... do something with the count if there was no error +> }); +> ``` -

Open /controllers/bookController.js. Near the top of the file you should see the exported index() function.

+Open **/controllers/bookController.js**. Near the top of the file you should see the exported `index()` function. -
var Book = require('../models/book')
+```python
+var Book = require('../models/book')
 
 exports.index = function(req, res, next) {
  res.send('NOT IMPLEMENTED: Site Home Page');
-}
+} +``` -

Replace all the code above with the following code fragment. The first thing this does is import (require()) all the models (highlighted in bold). We need to do this because we'll be using them to get our counts of records. It then imports the async module.

+Replace all the code above with the following code fragment. The first thing this does is import (`require()`) all the models (highlighted in bold). We need to do this because we'll be using them to get our counts of records. It then imports the _async_ module. -
var Book = require('../models/book');
+```js
+var Book = require('../models/book');
 var Author = require('../models/author');
 var Genre = require('../models/genre');
 var BookInstance = require('../models/bookinstance');
@@ -73,21 +80,21 @@ exports.index = function(req, res) {
     }, function(err, results) {
         res.render('index', { title: 'Local Library Home', error: err, data: results });
     });
-};
+}; +``` -

The async.parallel() method is passed an object with functions for getting the counts for each of our models. These functions are all started at the same time. When all of them have completed the final callback is invoked with the counts in the results parameter (or an error).

+The `async.parallel()` method is passed an object with functions for getting the counts for each of our models. These functions are all started at the same time. When all of them have completed the final callback is invoked with the counts in the results parameter (or an error). -

On success the callback function calls res.render(), specifying a view (template) named 'index' and an object containing the data that is to be inserted into it (this includes the results object that contains our model counts). The data is supplied as key-value pairs, and can be accessed in the template using the key.

+On success the callback function calls [`res.render()`](http://expressjs.com/en/4x/api.html#res.render), specifying a view (template) named '**index**' and an object containing the data that is to be inserted into it (this includes the results object that contains our model counts). The data is supplied as key-value pairs, and can be accessed in the template using the key. -
-

Note: The callback function from async.parallel() above is a little unusual in that we render the page whether or not there was an error (normally you might use a separate execution path for handling the display of errors).

-
+> **備註:** The callback function from `async.parallel()` above is a little unusual in that we render the page whether or not there was an error (normally you might use a separate execution path for handling the display of errors). -

View

+## View -

Open /views/index.pug and replace its content with the text below.

+Open **/views/index.pug** and replace its content with the text below. -
extends layout
+```js
+extends layout
 
 block content
   h1= title
@@ -105,29 +112,24 @@ block content
       li #[strong Copies:] !{data.book_instance_count}
       li #[strong Copies available:] !{data.book_instance_available_count}
       li #[strong Authors:] !{data.author_count}
-      li #[strong Genres:] !{data.genre_count}
+ li #[strong Genres:] !{data.genre_count} +``` -

The view is straightforward. We extend the layout.pug base template, overriding the block named 'content'. The first h1 heading will be the escaped text for the title variable that was passed into the render() function—note the use of the 'h1=' so that the following text is treated as a JavaScript expression. We then include a paragraph introducing the LocalLibrary.

+The view is straightforward. We extend the **layout.pug** base template, overriding the `block` named '**content**'. The first `h1` heading will be the escaped text for the `title` variable that was passed into the `render()` function—note the use of the '`h1=`' so that the following text is treated as a JavaScript expression. We then include a paragraph introducing the LocalLibrary. -

Under the Dynamic content heading we check whether the error variable passed in from the render() function has been defined. If so, we note the error. If not, we get and list the number of copies of each model from the data variable.

+Under the _Dynamic content_ heading we check whether the error variable passed in from the `render()` function has been defined. If so, we note the error. If not, we get and list the number of copies of each model from the `data` variable. -
-

Note: We didn't escape the count values (i.e. we used the !{} syntax) because the count values are calculated. If the information was supplied by end-users then we'd escape the variable for display.

-
+> **備註:** We didn't escape the count values (i.e. we used the `!{}` syntax) because the count values are calculated. If the information was supplied by end-users then we'd escape the variable for display. -

What does it look like?

+## What does it look like? -

At this point we should have created everything needed to display the index page. Run the application and open your browser to http://localhost:3000/. If everything is set up correctly, your site should look something like the following screenshot.

+At this point we should have created everything needed to display the index page. Run the application and open your browser to . If everything is set up correctly, your site should look something like the following screenshot. -

Home page - Express Local Library site

+![Home page - Express Local Library site](locallibary_express_home.png) -
-

Note: You won't be able to use the sidebar links yet because the urls, views, and templates for those pages haven't been defined. If you try you'll get errors like "NOT IMPLEMENTED: Book list" for example, depending on the link you click on. These string literals (which will be replaced with proper data) were specified in the different controllers that live inside your "controllers" file.

-
+> **備註:** You won't be able to use the sidebar links yet because the urls, views, and templates for those pages haven't been defined. If you try you'll get errors like "NOT IMPLEMENTED: Book list" for example, depending on the link you click on. These string literals (which will be replaced with proper data) were specified in the different controllers that live inside your "controllers" file. -

Next steps

+## Next steps - +- Return to [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data). +- Proceed to the next subarticle of part 5: [Book list page](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page). diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/index.md index d393586808422d..a8bfddb1d05b2f 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/index.md @@ -3,85 +3,76 @@ title: 'Express 教程 5: 呈現圖書館數據' slug: Learn/Server-side/Express_Nodejs/Displaying_data translation_of: Learn/Server-side/Express_Nodejs/Displaying_data --- -
{{LearnSidebar}}
+{{LearnSidebar}}{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}} -
{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}
- -

我們現在準備好要新增網頁,以顯示本地圖書館網站的書本與其它資料。這些網頁將包括一個主頁 ,顯示我們每個模型的型態有多少筆紀錄,以及我們所有模型的清單與細節頁面。藉此,我們將得到從數據庫取得紀錄、以及使用樣版的實務經驗。

+我們現在準備好要新增網頁,以顯示本地圖書館網站的書本與其它資料。這些網頁將包括一個主頁 ,顯示我們每個模型的型態有多少筆紀錄,以及我們所有模型的清單與細節頁面。藉此,我們將得到從數據庫取得紀錄、以及使用樣版的實務經驗。 - - - - - - - - - - + + + + + + + + + +
前置條件:完成先前教程主題 (包含 Express 教程 4: 路由與控制器)。
目標:了解如何使用非同步模組與 Pug 樣版語言,以及如何從我們的控制器函式中的 URL 得取資料。
前置條件:完成先前教程主題 (包含 Express 教程 4: 路由與控制器)。
目標: + 了解如何使用非同步模組與 Pug 樣版語言,以及如何從我們的控制器函式中的 + URL 得取資料。 +
-

概覽

- -

在我們先前的教程中,定義了可以用來跟資料庫互動的 Mongoose models ,並創建了一些初始的圖書館紀錄。我們接著創建本地圖書館網站需要的所有路由,但僅使用"空殼控制器" 函式(這些是骨架控制器函式,當一個網頁被存取時,只回傳一個"未實作" 信息)。

- -

下一步,是為這些顯示圖書館信息的網頁,提供充分的實作(我們將在後面的文章,檢視網頁表單的實作,像是創建、更新、刪除信息)。這包含了更新控制器函式,以利用我們的模型取得紀錄,並定義模板,為使用者顯示這些信息。

+## 概覽 -

我們在一開始,提供概略的總覽/重點主題,解釋在控制器函式中,如何管理非同步操作,以及如何使用 Pug 撰寫模板。然後我們將為每一個主要的 "唯讀" 網頁提供實作步驟,並且在使用到任何特別的、或新的特性時,會附上簡短的解釋說明。

+在我們先前的教程中,定義了可以用來跟資料庫互動的 [Mongoose models](/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose) ,並創建了一些初始的圖書館紀錄。我們接著[創建本地圖書館網站需要的所有路由](/zh-TW/docs/Learn/Server-side/Express_Nodejs/routes),但僅使用"空殼控制器" 函式(這些是骨架控制器函式,當一個網頁被存取時,只回傳一個"未實作" 信息)。 -

本教程的最後,你對路由、非同步函式、視圖、模型如何實際運作,應該有了更好的理解。

+下一步,是為這些顯示圖書館信息的網頁,提供充分的實作(我們將在後面的文章,檢視網頁表單的實作,像是創建、更新、刪除信息)。這包含了更新控制器函式,以利用我們的模型取得紀錄,並定義模板,為使用者顯示這些信息。 -

本教程的章節

+我們在一開始,提供概略的總覽/重點主題,解釋在控制器函式中,如何管理非同步操作,以及如何使用 Pug 撰寫模板。然後我們將為每一個主要的 "唯讀" 網頁提供實作步驟,並且在使用到任何特別的、或新的特性時,會附上簡短的解釋說明。 -

本教程分為下列章節,說明為了顯示圖書館網站頁面,如何新增各種特性 。在進入下一個教程之前,你需要閱讀並逐一實作下列章節。

+本教程的最後,你對路由、非同步函式、視圖、模型如何實際運作,應該有了更好的理解。 -
    -
  1. 使用 async 控制非同步流
  2. -
  3. 模板入門
  4. -
  5. 本地圖書館基礎模板
  6. -
  7. 主頁
  8. -
  9. 書本清單頁面
  10. -
  11. 書本實例清單頁面
  12. -
  13. 日期格式化 - 使用 moment
  14. -
  15. 作者清單頁面、分類清單頁面、與自我挑戰
  16. -
  17. 分類詳情頁面
  18. -
  19. 書本詳情頁面
  20. -
  21. 作者詳情頁面
  22. -
  23. 書本實例詳情頁面與自我挑戰
  24. -
+## 本教程的章節 -

總結

+本教程分為下列章節,說明為了顯示圖書館網站頁面,如何新增各種特性 。在進入下一個教程之前,你需要閱讀並逐一實作下列章節。 -

我們現在已經為我們的網站,創建了所有 "唯讀" 的頁面: 一個主頁,可以顯示每一個模組的實例數量,書本的列表與詳細信息頁面,書本的實例、作者、分類。沿著目前的學習路徑,我們學到了許多基本知識,有控制器、在非同步作業時管理流控制、使用 Pug 創建視圖、使用模型查詢數據庫、如何從視圖傳送信息到模板、如何創建並擴展模板。而完成挑戰的人,還會學到如何用 moment 處理日期。

+1. [使用 async 控制非同步流](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/flow_control_using_async) +2. [模板入門](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer) +3. [本地圖書館基礎模板](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template) +4. [主頁](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Home_page) +5. [書本清單頁面](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Book_list_page) +6. [書本實例清單頁面](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_list_page) +7. [日期格式化 - 使用 moment](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Date_formatting_using_moment) +8. [作者清單頁面、分類清單頁面、與自我挑戰](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_list_page) +9. [分類詳情頁面](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Genre_detail_page) +10. [書本詳情頁面](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Book_detail_page) +11. [作者詳情頁面](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Author_detail_page) +12. [書本實例詳情頁面與自我挑戰](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/BookInstance_detail_page_and_challenge) -

在下一篇文章,我們將依據目前為止學到的知識,創建HTML 表單以及表單管理代碼,開始修改儲存在網站中的資料。

+## 總結 -

參閱

+我們現在已經為我們的網站,創建了所有 "唯讀" 的頁面: 一個主頁,可以顯示每一個模組的實例數量,書本的列表與詳細信息頁面,書本的實例、作者、分類。沿著目前的學習路徑,我們學到了許多基本知識,有控制器、在非同步作業時管理流控制、使用 Pug 創建視圖、使用模型查詢數據庫、如何從視圖傳送信息到模板、如何創建並擴展模板。而完成挑戰的人,還會學到如何用 moment 處理日期。 - +在下一篇文章,我們將依據目前為止學到的知識,創建 HTML 表單以及表單管理代碼,開始修改儲存在網站中的資料。 -

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}}

+## 參閱 -

+- [Async module](http://caolan.github.io/async/docs.html) (Async docs) +- [Using Template engines with Express](https://expressjs.com/en/guide/using-template-engines.html) (Express docs) +- [Pug](https://pugjs.org/api/getting-started.html) (Pug docs) +- [Moment](http://momentjs.com/docs/) (Moment docs) -

本教學連結

+{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs/forms", "Learn/Server-side/Express_Nodejs")}} - +## 本教學連結 -

+- [Express/Node introduction](/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction) +- [Setting up a Node (Express) development environment](/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment) +- [Express Tutorial: The Local Library website](/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website) +- [Express Tutorial Part 2: Creating a skeleton website](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website) +- [Express Tutorial Part 3: Using a Database (with Mongoose)](/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose) +- [Express Tutorial Part 4: Routes and controllers](/en-US/docs/Learn/Server-side/Express_Nodejs/routes) +- [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms) +- [Express Tutorial Part 7: Deploying to production](/en-US/docs/Learn/Server-side/Express_Nodejs/deployment) diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.md index a2ea229c38ce44..dcd0cba4b30554 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/locallibrary_base_template/index.md @@ -3,13 +3,12 @@ title: 本地圖書館基礎模板 slug: Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template --- -

+現在我們了解如何使用 Pug 拓展模板,讓我們開始項目,創建一個基礎模板。這個模板會有一個側邊欄,連結到本教程中將要創建的各個頁面(例如,呈現並創建書本、種類、作者等等),以及一個主要內容區域,我們將在每個頁面中進行覆寫。 -

現在我們了解如何使用 Pug 拓展模板,讓我們開始項目,創建一個基礎模板。這個模板會有一個側邊欄,連結到本教程中將要創建的各個頁面(例如,呈現並創建書本、種類、作者等等),以及一個主要內容區域,我們將在每個頁面中進行覆寫。

+開啟 **/views/layout.pug** ,並以下列代碼,置換其內容。 -

開啟 /views/layout.pug ,並以下列代碼,置換其內容。

- -
doctype html
+```html
+doctype html
 html(lang='en')
   head
     title= title
@@ -47,25 +46,26 @@ html(lang='en')
                 a(href='/catalog/bookinstance/create') Create new book instance (copy)
 
         div(class='col-sm-10')
-          block content
+ block content +``` -

此模板使用(並包含)來自 Bootstrap 的 JavaScript 和 CSS ,以改進 HTML 頁面的佈局和呈現方式。使用 Bootstrap 或其它客戶端網頁框架,是一種快速的方式,可以創建吸引人的網頁,能夠良好地適應不同的瀏覽器尺寸,並且允許我們處理頁面的呈現,而不需要糾纒於任何不同尺寸的細節—此處我們只想專注於伺服端代碼!

+此模板使用(並包含)來自 [Bootstrap](http://getbootstrap.com/) 的 JavaScript 和 CSS ,以改進 HTML 頁面的佈局和呈現方式。使用 Bootstrap 或其它客戶端網頁框架,是一種快速的方式,可以創建吸引人的網頁,能夠良好地適應不同的瀏覽器尺寸,並且允許我們處理頁面的呈現,而不需要糾纒於任何不同尺寸的細節—此處我們只想專注於伺服端代碼! -

佈局的安排應該相當明白,假如你已經閱讀了之前的 模板入門。注意,使用 block content 當做定位符號,放到頁面內容將要放置的地方。

+佈局的安排應該相當明白,假如你已經閱讀了之前的 [模板入門](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data#Template_primer)。注意,使用 `block content` 當做定位符號,放到頁面內容將要放置的地方。 -

基礎模板也參考了一個本地 css 檔 (style.css) ,此檔提供了一些額外的樣式。打開 /public/stylesheets/style.css ,並用底下的 CSS 代碼,取代它的內容:

+基礎模板也參考了一個本地 css 檔 (**style.css**) ,此檔提供了一些額外的樣式。打開 **/public/stylesheets/style.css** ,並用底下的 CSS 代碼,取代它的內容: -
.sidebar-nav {
+```css
+.sidebar-nav {
     margin-top: 20px;
     padding: 0;
     list-style: none;
-}
+} +``` -

當我們開始運行網站時,我們應該看到側邊欄出現!在本教程的下個部分,我們將使用以上的佈局,來定義各個頁面。

+當我們開始運行網站時,我們應該看到側邊欄出現!在本教程的下個部分,我們將使用以上的佈局,來定義各個頁面。 -

下一步

+## 下一步 - +- 回到 [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data). +- 繼續教學 5 下個章節: [Home page](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Home_page). diff --git a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/template_primer/index.md b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/template_primer/index.md index 0d7264aef3f8d4..033201d9e3eb5f 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/displaying_data/template_primer/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/displaying_data/template_primer/index.md @@ -3,42 +3,44 @@ title: 模板入門 slug: Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer translation_of: Learn/Server-side/Express_Nodejs/Displaying_data/Template_primer --- -

模板是一個文字檔,定義了一個輸出檔的結構或者排版,使用定位符號表示,當模板被繪製時,資料將插入到何處(在Express,模板被稱為視圖)。

+模板是一個文字檔,定義了一個輸出檔的*結構*或者排版,使用定位符號表示,當模板被繪製時,資料將插入到何處(在*Express*,模板被稱為*視圖*)。 -

Express 模板選擇

+## Express 模板選擇 -

Express 可以與許多不同的模板渲染引擎一起使用。在本教程中,我們使用 Pug(以前稱為 Jade)作為模板。這是最流行的 Node 模板語言,並且官方將自身描述為 “用於編寫 HTML,語法乾淨且空格敏感,受 Haml 影響很大”。

+Express 可以與許多不同的[模板渲染引擎](https://expressjs.com/zh-tw/guide/using-template-engines.html)一起使用。在本教程中,我們使用 [Pug](https://pugjs.org/api/getting-started.html)(以前稱為 _Jade_)作為模板。這是最流行的 Node 模板語言,並且官方將自身描述為 “用於編寫 HTML,語法乾淨且空格敏感,受 [Haml ](http://haml.info/)影響很大”。 -

不同的模板語言使用不同的方法,來定義佈局和標記數據的佔位符 — 一些使用 HTML 來定義佈局,而另一些則使用可以編譯為 HTML 的不同標記格式。 Pug 是第二種類型;它使用 HTML 的表示形式,其中任何行中的第一個單詞,通常表示HTML元素,後續行中的縮進,用於表示嵌套在這些元素中的任何內容。結果是一個頁面定義直接轉換為 HTML,但可以說更簡潔,更容易閱讀。

+不同的模板語言使用不同的方法,來定義佈局和標記數據的佔位符 — 一些使用 HTML 來定義佈局,而另一些則使用可以編譯為 HTML 的不同標記格式。 Pug 是第二種類型;它使用 HTML 的*表示形式*,其中任何行中的第一個單詞,通常表示 HTML 元素,後續行中的縮進,用於表示嵌套在這些元素中的任何內容。結果是一個頁面定義直接轉換為 HTML,但可以說更簡潔,更容易閱讀。 -
-

備註: The downside of using Pug is that it is sensitive to indentation and whitespace (if you add an extra space in the wrong place you may get an unhelpful error code). However once you have your templates in place, they are very easy to read and maintain.

-
+> **備註:** The downside of using _Pug_ is that it is sensitive to indentation and whitespace (if you add an extra space in the wrong place you may get an unhelpful error code). However once you have your templates in place, they are very easy to read and maintain. -

Template configuration

+## Template configuration -

The LocalLibrary was configured to use Pug when we created the skeleton website. You should see the pug module included as a dependency in the website's package.json file, and the following configuration settings in the app.js file. The settings tell us that we're using pug as the view engine, and that Express should search for templates in the /views subdirectory.

+The _LocalLibrary_ was configured to use [Pug](https://pugjs.org/api/getting-started.html) when we [created the skeleton website](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website). You should see the pug module included as a dependency in the website's **package.json** file, and the following configuration settings in the **app.js** file. The settings tell us that we're using pug as the view engine, and that _Express_ should search for templates in the **/views** subdirectory. -
// View engine setup.
+```js
+// View engine setup.
 app.set('views', path.join(__dirname, 'views'));
-app.set('view engine', 'pug');
+app.set('view engine', 'pug'); +``` -

If you look in the views directory you will see the .pug files for the project's default views. These include the view for the home page (index.pug) and base template (layout.pug) that we will need to replace with our own content.

+If you look in the views directory you will see the .pug files for the project's default views. These include the view for the home page (**index.pug**) and base template (**layout.pug**) that we will need to replace with our own content. -
/express-locallibrary-tutorial  //the project root
+```plain
+/express-locallibrary-tutorial  //the project root
   /views
     error.pug
-    index.pug
-    layout.pug
-
+ index.pug + layout.pug +``` -

Template syntax

+## Template syntax -

The example template file below shows off many of Pug's most useful features.

+The example template file below shows off many of Pug's most useful features. -

The first thing to notice is that the file maps the structure of a typical HTML file, with the first word in (almost) every line being an HTML element, and indentation being used to indicate nested elements. So for example, the body element is inside an html element, and paragraph elements (p) are within the body element, etc. Non-nested elements (e.g. individual paragraphs) are on separate lines.

+The first thing to notice is that the file maps the structure of a typical HTML file, with the first word in (almost) every line being an HTML element, and indentation being used to indicate nested elements. So for example, the `body` element is inside an `html` element, and paragraph elements (`p`) are within the `body` element, etc. Non-nested elements (e.g. individual paragraphs) are on separate lines. -
doctype html
+```html
+doctype html
 html(lang="en")
   head
     title= title
@@ -47,11 +49,11 @@ html(lang="en")
     h1= title
 
     p This is a line with #[em some emphasis] and #[strong strong text] markup.
-    p This line has un-escaped data: !{'<em> is emphasised</em>'} and escaped data: #{'<em> is not emphasised</em>'}.
+    p This line has un-escaped data: !{' is emphasised'} and escaped data: #{' is not emphasised'}.
       | This line follows on.
-    p= 'Evaluated and <em>escaped expression</em>:' + title
+    p= 'Evaluated and escaped expression:' + title
 
-    <!-- You can add HTML comments directly -->
+    
     // You can add single line JavaScript comments and they are generated to HTML comments
     //- Introducing a single line JavaScript comment with "//-" ensures the comment isn't rendered to HTML
 
@@ -72,78 +74,87 @@ html(lang="en")
 
     ul
       each val in [1, 2, 3, 4, 5]
-        li= val
+ li= val +``` -

Element attributes are defined in parentheses after their associated element. Inside the parentheses, the attributes are defined in comma- or whitespace- separated lists of the pairs of attribute names and attribute values, for example:

+Element attributes are defined in parentheses after their associated element. Inside the parentheses, the attributes are defined in comma- or whitespace- separated lists of the pairs of attribute names and attribute values, for example: -
    -
  • script(type='text/javascript'), link(rel='stylesheet', href='/stylesheets/style.css')
  • -
  • meta(name='viewport' content='width=device-width initial-scale=1')
  • -
+- `script(type='text/javascript')`, `link(rel='stylesheet', href='/stylesheets/style.css')` +- `meta(name='viewport' content='width=device-width initial-scale=1')` -

The values of all attributes are escaped (e.g. characters like ">" are converted to their HTML code equivalents like "&gt;") to prevent injection of JavaScript/cross-site scripting attacks.

+The values of all attributes are _escaped_ (e.g. characters like "`>`" are converted to their HTML code equivalents like "`>"`) to prevent injection of JavaScript/cross-site scripting attacks. -

If a tag is followed by the equals sign, the following text is treated as a JavaScript expression. So for example, in the first line below, the content of the h1 tag will be variable title (either defined in the file or passed into the template from Express). In the second line the paragraph content is a text string concatented with the title variable. In both cases the default behaviour is to escape the line.

+If a tag is followed by the equals sign, the following text is treated as a JavaScript _expression_. So for example, in the first line below, the content of the `h1` tag will be _variable_ `title` (either defined in the file or passed into the template from Express). In the second line the paragraph content is a text string concatented with the `title` variable. In both cases the default behaviour is to _escape_ the line. -
h1= title
-p= 'Evaluated and <em>escaped expression</em>:' + title
+```html +h1= title +p= 'Evaluated and escaped expression:' + title +``` -

If there is no equals symbol after the tag then the content is treated as plain text. Within the plain text you can insert escaped and unescaped data using the #{} and !{} syntax, as shown below. You can also add raw HTML within the plain text.

+If there is no equals symbol after the tag then the content is treated as plain text. Within the plain text you can insert escaped and unescaped data using the `#{}` and` !{}` syntax, as shown below. You can also add raw HTML within the plain text. -
p This is a line with #[em some emphasis] and #[strong strong text] markup.
-p This line has an un-escaped string: !{'<em> is emphasised</em>'}, an escaped string: #{'<em> is not emphasised</em>'}, and escaped variables: #{title}.
+```html +p This is a line with #[em some emphasis] and #[strong strong text] markup. +p This line has an un-escaped string: !{' is emphasised'}, an escaped string: #{' is not emphasised'}, and escaped variables: #{title}. +``` -
-

備註: You will almost always want to escape data from users (via the #{} syntax). Data that can be trusted (e.g. generated counts of records, etc.) may be displayed without escaping the values.

-
+> **備註:** You will almost always want to escape data from users (via the **`#{}`** syntax). Data that can be trusted (e.g. generated counts of records, etc.) may be displayed without escaping the values. -

You can use the pipe ('|') character at the beginning of a line to indicate "plain text". For example, the additional text shown below will be displayed on the same line as the preceding anchor, but will not be linked.

+You can use the pipe ('**|**') character at the beginning of a line to indicate "[plain text](https://pugjs.org/language/plain-text.html)". For example, the additional text shown below will be displayed on the same line as the preceding anchor, but will not be linked. -
a(href='http://someurl/') Link text
-| Plain text
+```html +a(href='http://someurl/') Link text +| Plain text +``` -

Pug allows you to perform conditional operations using if, else , else if and unless—for example:

+Pug allows you to perform conditional operations using `if`, `else` , `else if` and `unless`—for example: -
if title
+```html
+if title
   p A variable named "title" exists
 else
-  p A variable named "title" does not exist
+ p A variable named "title" does not exist +``` -

You can also perform loop/iteration operations using each-in or while syntax. In the code fragment below we've looped through an array to display a list of variables (note the use of the 'li=' to evaluate the "val" as a variable below. The value you iterate across can also be passed into the template as a variable!

+You can also perform loop/iteration operations using `each-in` or `while` syntax. In the code fragment below we've looped through an array to display a list of variables (note the use of the 'li=' to evaluate the "val" as a variable below. The value you iterate across can also be passed into the template as a variable! -
ul
+```html
+ul
   each val in [1, 2, 3, 4, 5]
-    li= val
+ li= val +``` -

The syntax also supports comments (that can be rendered in the output—or not—as you choose), mixins to create reusable blocks of code, case statements, and many other features. For more detailed information see The Pug docs.

+The syntax also supports comments (that can be rendered in the output—or not—as you choose), mixins to create reusable blocks of code, case statements, and many other features. For more detailed information see [The Pug docs](https://pugjs.org/api/getting-started.html). -

Extending templates

+## Extending templates -

Across a site, it is usual for all pages to have a common structure, including standard HTML markup for the head, footer, navigation, etc. Rather than forcing developers to duplicate this "boilerplate" in every page, Pug allows you to declare a base template and then extend it, replacing just the bits that are different for each specific page.

+Across a site, it is usual for all pages to have a common structure, including standard HTML markup for the head, footer, navigation, etc. Rather than forcing developers to duplicate this "boilerplate" in every page, _Pug_ allows you to declare a base template and then extend it, replacing just the bits that are different for each specific page. -

For example, the base template layout.pug created in our skeleton project looks like this:

+For example, the base template **layout.pug** created in our [skeleton project](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website) looks like this: -
doctype html
+```html
+doctype html
 html
   head
     title= title
     link(rel='stylesheet', href='/stylesheets/style.css')
   body
-    block content
+ block content +``` -

The block tag is used to mark up sections of content that may be replaced in a derived template (if the block is not redefined then its implementation in the base class is used).

+The `block` tag is used to mark up sections of content that may be replaced in a derived template (if the block is not redefined then its implementation in the base class is used). -

The default index.pug (created for our skeleton project) shows how we override the base template. The extends tag identifies the base template to use, and then we use block section_name to indicate the new content of the section that we will override.

+The default **index.pug** (created for our skeleton project) shows how we override the base template. The `extends` tag identifies the base template to use, and then we use `block section_name` to indicate the new content of the section that we will override. -
extends layout
+```html
+extends layout
 
 block content
   h1= title
-  p Welcome to #{title}
+ p Welcome to #{title} +``` -

Next steps

+## Next steps - +- Return to [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data). +- Proceed to the next subarticle of part 5: [The LocalLibrary base template](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/LocalLibrary_base_template). diff --git a/files/zh-tw/learn/server-side/express_nodejs/forms/create_author_form/index.md b/files/zh-tw/learn/server-side/express_nodejs/forms/create_author_form/index.md index 38bfe22c018349..e1e33b5a538d54 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/forms/create_author_form/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/forms/create_author_form/index.md @@ -3,31 +3,36 @@ title: Create Author form slug: Learn/Server-side/Express_Nodejs/forms/Create_author_form translation_of: Learn/Server-side/Express_Nodejs/forms/Create_author_form --- -

Edi本章節演示,如何為創建作者對象 Author定義一個頁面。

+[Edi](/en-US/docs/Learn/Server-side/Express_Nodejs/forms$edit#Create_author_form)本章節演示,如何為創建作者對象 `Author`定義一個頁面。 -

導入驗證和清理方法

+## 導入驗證和清理方法 -

為了在種類表單使用 express 驗證器,我們必須用 require 導入我們想用的函式。

+為了在種類表單使用 _express_ 驗證器,我們必須用 _require_ 導入我們想用的函式。 -

打開 /controllers/authorController.js,並在檔案最上方,加入底下幾行:

+打開 **/controllers/authorController.js**,並在檔案最上方,加入底下幾行: -
const { body,validationResult } = require('express-validator/check');
-const { sanitizeBody } = require('express-validator/filter');
+```js +const { body,validationResult } = require('express-validator/check'); +const { sanitizeBody } = require('express-validator/filter'); +``` -

Controller—get route

+## Controller—get route -

Find the exported author_create_get() controller method and replace it with the following code. This simply renders the author_form.pug view, passing a title variable.

+Find the exported `author_create_get()` controller method and replace it with the following code. This simply renders the **author_form.pug** view, passing a `title` variable. -
// Display Author create form on GET.
+```js
+// Display Author create form on GET.
 exports.author_create_get = function(req, res, next) {
     res.render('author_form', { title: 'Create Author'});
-};
+}; +``` -

Controller—post route

+## Controller—post route -

Find the exported author_create_post() controller method, and replace it with the following code.

+Find the exported `author_create_post()` controller method, and replace it with the following code. -
// Handle Author create on POST.
+```js
+// Handle Author create on POST.
 exports.author_create_post = [
 
     // Validate fields.
@@ -45,7 +50,7 @@ exports.author_create_post = [
     sanitizeBody('date_of_death').toDate(),
 
     // Process request after validation and sanitization.
-    (req, res, next) => {
+    (req, res, next) => {
 
         // Extract the validation errors from a request.
         const errors = validationResult(req);
@@ -73,41 +78,43 @@ exports.author_create_post = [
             });
         }
     }
-];
+]; +``` -

The structure and behaviour of this code is almost exactly the same as for creating a Genre object. First we validate and sanitize the data. If the data is invalid then we re-display the form along with the data that was originally entered by the user and a list of error messages. If the data is valid then we save the new author record and redirect the user to the author detail page.

+The structure and behaviour of this code is almost exactly the same as for creating a `Genre` object. First we validate and sanitize the data. If the data is invalid then we re-display the form along with the data that was originally entered by the user and a list of error messages. If the data is valid then we save the new author record and redirect the user to the author detail page. -
-

Note: Unlike with the Genre post handler, we don't check whether the Author object already exists before saving it. Arguably we should, though as it is now we can have multiple authors with the same name.

-
+> **備註:** Unlike with the `Genre` post handler, we don't check whether the `Author` object already exists before saving it. Arguably we should, though as it is now we can have multiple authors with the same name. -

The validation code demonstrates several new features:

+The validation code demonstrates several new features: -
    -
  • We can daisy chain validators, using withMessage() to specify the error message to display if the previous validation method fails. This makes it very easy to provide specific error messages without lots of code duplication. +- We can daisy chain validators, using `withMessage()` to specify the error message to display if the previous validation method fails. This makes it very easy to provide specific error messages without lots of code duplication. -
    // Validate fields.
    -body('first_name').isLength({ min: 1 }).trim().withMessage('First name must be specified.')
    -    .isAlphanumeric().withMessage('First name has non-alphanumeric characters.'),
    -
    -
  • -
  • We can use the optional() function to run a subsequent validation only if a field has been entered (this allows us to validate optional fields). For example, below we check that the optional date of birth is an ISO8601-compliant date (the checkFalsy flag means that we'll accept either an empty string or null as an empty value). -
    body('date_of_birth', 'Invalid date of birth').optional({ checkFalsy: true }).isISO8601(),
    -
  • -
+ ```js + // Validate fields. + body('first_name').isLength({ min: 1 }).trim().withMessage('First name must be specified.') + .isAlphanumeric().withMessage('First name has non-alphanumeric characters.'), + ``` -
    -
  • Parameters are recieved from the request as strings. We can use toDate() (or toBoolean(), etc.) to cast these to the proper JavaScript types. +- We can use the `optional()` function to run a subsequent validation only if a field has been entered (this allows us to validate optional fields). For example, below we check that the optional date of birth is an ISO8601-compliant date (the `checkFalsy` flag means that we'll accept either an empty string or `null` as an empty value). -
    sanitizeBody('date_of_birth').toDate(),
    -
  • -
+ ```html + body('date_of_birth', 'Invalid date of birth').optional({ checkFalsy: true }).isISO8601(), + ``` -

View

+ -

Create /views/author_form.pug and copy in the text below.

+- Parameters are recieved from the request as strings. We can use `toDate()` (or `toBoolean()`, etc.) to cast these to the proper JavaScript types. -
extends layout
+  ```js
+  sanitizeBody('date_of_birth').toDate(),
+  ```
+
+## View
+
+Create **/views/author_form.pug** and copy in the text below.
+
+```html
+extends layout
 
 block content
   h1=title
@@ -125,31 +132,26 @@ block content
   if errors
     ul
       for error in errors
-        li!= error.msg
+ li!= error.msg +``` -

The structure and behaviour for this view is exactly the same as for the genre_form.pug template, so we won't describe it again.

+The structure and behaviour for this view is exactly the same as for the **genre_form.pug** template, so we won't describe it again. -
-

Note: Some browsers don’t support the input type=“date”, so you won’t get the datepicker widget or the default dd/mm/yyyy placeholder, but will instead get an empty plain text field. One workaround is to explicitly add the attribute placeholder='dd/mm/yyyy' so that on less capable browsers you will still get information about the desired text format.

-
+> **備註:** Some browsers don’t support the input `type=“date”`, so you won’t get the datepicker widget or the default _`dd/mm/yyyy`_ placeholder, but will instead get an empty plain text field. One workaround is to explicitly add the attribute `placeholder='dd/mm/yyyy'` so that on less capable browsers you will still get information about the desired text format. -

Challenge: Adding the date of death

+### Challenge: Adding the date of death -

The template above is missing a field for entering the date_of_death. Create the field following the same pattern as the date of birth form group!

+The template above is missing a field for entering the `date_of_death`. Create the field following the same pattern as the date of birth form group! -

What does it look like?

+## What does it look like? -

Run the application, open your browser to http://localhost:3000/, then select the Create new author link. If everything is set up correctly, your site should look something like the following screenshot. After you enter a value, it should be saved and you'll be taken to the author detail page.

+Run the application, open your browser to , then select the _Create new author_ link. If everything is set up correctly, your site should look something like the following screenshot. After you enter a value, it should be saved and you'll be taken to the author detail page. -

Author Create Page - Express Local Library site

+![Author Create Page - Express Local Library site](locallibary_express_author_create_empty.png) -
-

Note: If you experiment with various input formats for the dates, you may find that the format yyyy-mm-dd misbehaves. This is because JavaScript treats date strings as including the time of 0 hours, but additionally treats date strings in that format (the ISO 8601 standard) as including the time 0 hours UTC, rather than the local time. If your time zone is west of UTC, the date display, being local, will be one day before the date you entered. This is one of several complexities (such as multi-word family names and multi-author books) that we are not addressing here.

-
+> **備註:** If you experiment with various input formats for the dates, you may find that the format `yyyy-mm-dd` misbehaves. This is because JavaScript treats date strings as including the time of 0 hours, but additionally treats date strings in that format (the ISO 8601 standard) as including the time 0 hours UTC, rather than the local time. If your time zone is west of UTC, the date display, being local, will be one day before the date you entered. This is one of several complexities (such as multi-word family names and multi-author books) that we are not addressing here. -

Next steps

+## Next steps - +- Return to [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms). +- Proceed to the next subarticle of part 6: [Create Book form](/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_book_form). diff --git a/files/zh-tw/learn/server-side/express_nodejs/forms/create_book_form/index.md b/files/zh-tw/learn/server-side/express_nodejs/forms/create_book_form/index.md index ea055296e7e22e..aee1ea142a66ed 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/forms/create_book_form/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/forms/create_book_form/index.md @@ -3,22 +3,23 @@ title: Create Book form slug: Learn/Server-side/Express_Nodejs/forms/Create_book_form translation_of: Learn/Server-side/Express_Nodejs/forms/Create_book_form --- -

Edit此章節展示如何定義頁面/表單以創建Book對象。這比相同的作者Author或種類Genre頁面稍微複雜一點,因為我們需要在我們的書本表單中,獲取並顯示可用的作者和種類記錄。

+[Edit](/en-US/docs/Learn/Server-side/Express_Nodejs/forms$edit#Create_book_form)此章節展示如何定義頁面/表單以創建`Book`對象。這比相同的作者`Author`或種類`Genre`頁面稍微複雜一點,因為我們需要在我們的書本表單中,獲取並顯示可用的作者和種類記錄。 -

+## 導入驗證和清理方法 -

導入驗證和清理方法

+打開 **/controllers/bookController.js**,並在文件頂部添加以下幾行: -

打開 /controllers/bookController.js,並在文件頂部添加以下幾行:

+```js +const { body,validationResult } = require('express-validator/check'); +const { sanitizeBody } = require('express-validator/filter'); +``` -
const { body,validationResult } = require('express-validator/check');
-const { sanitizeBody } = require('express-validator/filter');
+## Controller—get route -

Controller—get route

+Find the exported `book_create_get()` controller method and replace it with the following code. -

Find the exported book_create_get() controller method and replace it with the following code.

- -
// Display book create form on GET.
+```js
+// Display book create form on GET.
 exports.book_create_get = function(req, res, next) {
 
     // Get all authors and genres, which we can use for adding to our book.
@@ -34,18 +35,20 @@ exports.book_create_get = function(req, res, next) {
         res.render('book_form', { title: 'Create Book', authors: results.authors, genres: results.genres });
     });
 
-};
+}; +``` -

This uses the async module (described in Express Tutorial Part 5: Displaying library data) to get all Author and Genre objects. These are then passed to the view book_form.pug as variables named authors and genres (along with the page title).

+This uses the async module (described in [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data)) to get all `Author` and `Genre` objects. These are then passed to the view **`book_form.pug`** as variables named `authors` and `genres` (along with the page `title`). -

Controller—post route

+## Controller—post route -

Find the exported book_create_post() controller method and replace it with the following code.

+Find the exported `book_create_post()` controller method and replace it with the following code. -
// Handle book create on POST.
+```js
+// Handle book create on POST.
 exports.book_create_post = [
     // Convert the genre to an array.
-    (req, res, next) => {
+    (req, res, next) => {
         if(!(req.body.genre instanceof Array)){
             if(typeof req.body.genre==='undefined')
             req.body.genre=[];
@@ -65,7 +68,7 @@ exports.book_create_post = [
     sanitizeBody('*').trim().escape(),
 
     // Process request after validation and sanitization.
-    (req, res, next) => {
+    (req, res, next) => {
 
         // Extract the validation errors from a request.
         const errors = validationResult(req);
@@ -94,8 +97,8 @@ exports.book_create_post = [
                 if (err) { return next(err); }
 
                 // Mark our selected genres as checked.
-                for (let i = 0; i < results.genres.length; i++) {
-                    if (book.genre.indexOf(results.genres[i]._id) > -1) {
+                for (let i = 0; i < results.genres.length; i++) {
+                    if (book.genre.indexOf(results.genres[i]._id) > -1) {
                         results.genres[i].checked='true';
                     }
                 }
@@ -112,18 +115,22 @@ exports.book_create_post = [
                 });
         }
     }
-];
+]; +``` -

The structure and behaviour of this code is almost exactly the same as for creating a Genre or Author object. First we validate and sanitize the data. If the data is invalid then we re-display the form along with the data that was originally entered by the user and a list of error messages. If the data is valid, we then save the new Book record and redirect the user to the book detail page.

+The structure and behaviour of this code is almost exactly the same as for creating a `Genre` or `Author` object. First we validate and sanitize the data. If the data is invalid then we re-display the form along with the data that was originally entered by the user and a list of error messages. If the data is valid, we then save the new `Book` record and redirect the user to the book detail page. -

The first main difference with respect to the other form handling code is that we use a wildcard to trim and escape all fields in one go (rather than sanitising them individually):

+The first main difference with respect to the other form handling code is that we use a wildcard to trim and escape all fields in one go (rather than sanitising them individually): -
sanitizeBody('*').trim().escape(),
+```js +sanitizeBody('*').trim().escape(), +``` -

The next main difference with respect to the other form handling code is how we sanitize the genre information. The form returns an array of Genre items (while for other fields it returns a string). In order to validate the information we first convert the request to an array (required for the next step).

+The next main difference with respect to the other form handling code is how we sanitize the genre information. The form returns an array of `Genre` items (while for other fields it returns a string). In order to validate the information we first convert the request to an array (required for the next step). -
// Convert the genre to an array.
-(req, res, next) => {
+```js
+// Convert the genre to an array.
+(req, res, next) => {
     if(!(req.body.genre instanceof Array)){
         if(typeof req.body.genre==='undefined')
         req.body.genre=[];
@@ -131,27 +138,33 @@ exports.book_create_post = [
         req.body.genre=new Array(req.body.genre);
     }
     next();
-},
+}, +``` -

We then use a wildcard (*) in the sanitiser to individually validate each of the genre array entries. The code below shows how - this translates to "sanitise every item below key genre".

+We then use a wildcard (`*`) in the sanitiser to individually validate each of the genre array entries. The code below shows how - this translates to "sanitise every item below key `genre`". -
sanitizeBody('genre.*').trim().escape(),
+```js +sanitizeBody('genre.*').trim().escape(), +``` -

The final difference with respect to the other form handling code is that we need to pass in all existing genres and authors to the form. In order to mark the genres that were checked by the user we iterate through all the genres and add the checked='true' parameter to those that were in our post data (as reproduced in the code fragment below).

+The final difference with respect to the other form handling code is that we need to pass in all existing genres and authors to the form. In order to mark the genres that were checked by the user we iterate through all the genres and add the `checked='true'` parameter to those that were in our post data (as reproduced in the code fragment below). -
// Mark our selected genres as checked.
-for (let i = 0; i < results.genres.length; i++) {
-    if (book.genre.indexOf(results.genres[i]._id) > -1) {
+```js
+// Mark our selected genres as checked.
+for (let i = 0; i < results.genres.length; i++) {
+    if (book.genre.indexOf(results.genres[i]._id) > -1) {
         // Current genre is selected. Set "checked" flag.
         results.genres[i].checked='true';
     }
-}
+} +``` -

View

+## View -

Create /views/book_form.pug and copy in the text below.

+Create **/views/book_form.pug** and copy in the text below. -
extends layout
+```html
+extends layout
 
 block content
   h1= title
@@ -186,29 +199,26 @@ block content
   if errors
     ul
       for error in errors
-        li!= error.msg
+ li!= error.msg +``` + +The view structure and behaviour is almost the same as for the **genre_form.pug** template. -

The view structure and behaviour is almost the same as for the genre_form.pug template.

+The main differences are in how we implement the selection-type fields: `Author` and `Genre`. -

The main differences are in how we implement the selection-type fields: Author and Genre.

+- The set of genres are displayed as checkboxes, using the `checked` value we set in the controller to determine whether or not the box should be selected. +- The set of authors are displayed as a single-selection drop-down list. In this case we determine what author to display by comparing the id of the current author option with the value previously entered by the user (passed in as the `book` variable). This is highlighted above! -
    -
  • The set of genres are displayed as checkboxes, using the checked value we set in the controller to determine whether or not the box should be selected.
  • -
  • The set of authors are displayed as a single-selection drop-down list. In this case we determine what author to display by comparing the id of the current author option with the value previously entered by the user (passed in as the book variable). This is highlighted above! -
    -

    Note: If there is an error in the submitted form, then, when the form is to be re-rendered, the new book's author is identified only with a string (the value of the selected option in the list of authors). By contrast, the existing books' authors have _id properties that are not strings. So to compare the new with the existing we must cast each existing book's author's _id to a string, as shown above.

    -
    -
  • -
+ > **備註:** If there is an error in the submitted form, then, when the form is to be re-rendered, the new book's author is identified only with a string (the value of the selected option in the list of authors). By contrast, the existing books' authors have `_id` properties that are not strings. So to compare the new with the existing we must cast each existing book's author's `_id` to a string, as shown above. -

What does it look like?

+## What does it look like? -

Run the application, open your browser to http://localhost:3000/, then select the Create new book link. If everything is set up correctly, your site should look something like the following screenshot. After you submit a valid book, it should be saved and you'll be taken to the book detail page.

+Run the application, open your browser to , then select the _Create new book_ link. If everything is set up correctly, your site should look something like the following screenshot. After you submit a valid book, it should be saved and you'll be taken to the book detail page. -

+![](locallibary_express_book_create_empty.png) -

Next steps

+## Next steps -

Return to Express Tutorial Part 6: Working with forms.

+Return to [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms). -

Proceed to the next subarticle of part 6: Create BookInstance form.

+Proceed to the next subarticle of part 6: [Create BookInstance form](/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_BookInstance_form). diff --git a/files/zh-tw/learn/server-side/express_nodejs/forms/create_bookinstance_form/index.md b/files/zh-tw/learn/server-side/express_nodejs/forms/create_bookinstance_form/index.md index 5fa029aaf5e03f..ee27d819f568b7 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/forms/create_bookinstance_form/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/forms/create_bookinstance_form/index.md @@ -3,24 +3,29 @@ title: Create BookInstance form slug: Learn/Server-side/Express_Nodejs/forms/Create_BookInstance_form translation_of: Learn/Server-side/Express_Nodejs/forms/Create_BookInstance_form --- -

Edi本章節演示如何定義一個頁面/表單,以創建 BookInstance 物件。這很像我們用來創建書本 Book 物件的表單。

+[Edi](/en-US/docs/Learn/Server-side/Express_Nodejs/forms$edit#Create_BookInstance_form)本章節演示如何定義一個頁面/表單,以創建 `BookInstance` 物件。這很像我們用來創建書本 `Book` 物件的表單。 -

導入驗證和清理方法

+## 導入驗證和清理方法 -

打開 /controllers/bookinstanceController.js,並在檔案最上方,加入以下幾行:

+打開 **/controllers/bookinstanceController.js**,並在檔案最上方,加入以下幾行: -
const { body,validationResult } = require('express-validator/check');
-const { sanitizeBody } = require('express-validator/filter');
+```js +const { body,validationResult } = require('express-validator/check'); +const { sanitizeBody } = require('express-validator/filter'); +``` -

Controller—get route

+## Controller—get route -

At the top of the file, require the Book module (needed because each BookInstance is associated with a particular Book).

+At the top of the file, require the _Book_ module (needed because each `BookInstance` is associated with a particular `Book`). -
var Book = require('../models/book');
+```js +var Book = require('../models/book'); +``` -

Find the exported bookinstance_create_get() controller method and replace it with the following code.

+Find the exported `bookinstance_create_get()` controller method and replace it with the following code. -
// Display BookInstance create form on GET.
+```js
+// Display BookInstance create form on GET.
 exports.bookinstance_create_get = function(req, res, next) {
 
     Book.find({},'title')
@@ -30,15 +35,17 @@ exports.bookinstance_create_get = function(req, res, next) {
       res.render('bookinstance_form', {title: 'Create BookInstance', book_list:books});
     });
 
-};
+}; +``` -

The controller gets a list of all books (book_list) and passes it to the view bookinstance_form.pug (along with the title)

+The controller gets a list of all books (`book_list`) and passes it to the view **`bookinstance_form.pug`** (along with the `title`) -

Controller—post route

+## Controller—post route -

Find the exported bookinstance_create_post() controller method and replace it with the following code.

+Find the exported `bookinstance_create_post()` controller method and replace it with the following code. -
// Handle BookInstance create on POST.
+```js
+// Handle BookInstance create on POST.
 exports.bookinstance_create_post = [
 
     // Validate fields.
@@ -53,7 +60,7 @@ exports.bookinstance_create_post = [
     sanitizeBody('due_back').toDate(),
 
     // Process request after validation and sanitization.
-    (req, res, next) => {
+    (req, res, next) => {
 
         // Extract the validation errors from a request.
         const errors = validationResult(req);
@@ -85,15 +92,17 @@ exports.bookinstance_create_post = [
                 });
         }
     }
-];
+]; +``` -

The structure and behaviour of this code is the same as for creating our other objects. First we validate and sanitize the data. If the data is invalid, we then re-display the form along with the data that was originally entered by the user and a list of error messages. If the data is valid, we save the new BookInstance record and redirect the user to the detail page.

+The structure and behaviour of this code is the same as for creating our other objects. First we validate and sanitize the data. If the data is invalid, we then re-display the form along with the data that was originally entered by the user and a list of error messages. If the data is valid, we save the new `BookInstance` record and redirect the user to the detail page. -

View

+## View -

Create /views/bookinstance_form.pug and copy in the text below.

+Create **/views/bookinstance_form.pug** and copy in the text below. -
extends layout
+```html
+extends layout
 
 block content
   h1=title
@@ -128,23 +137,20 @@ block content
   if errors
     ul
       for error in errors
-        li!= error.msg
+ li!= error.msg +``` -

The view structure and behaviour is almost the same as for the book_form.pug template, so we won't go over it again.

+The view structure and behaviour is almost the same as for the **book_form.pug** template, so we won't go over it again. -
-

Note: The above template hard-codes the Status values (Maintenance, Available, etc.) and does not "remember" the user's entered values. Should you so wish, consider reimplementing the list, passing in option data from the controller and setting the selected value when the form is re-displayed.

-
+> **備註:** The above template hard-codes the _Status_ values (Maintenance, Available, etc.) and does not "remember" the user's entered values. Should you so wish, consider reimplementing the list, passing in option data from the controller and setting the selected value when the form is re-displayed. -

What does it look like?

+## What does it look like? -

Run the application and open your browser to http://localhost:3000/. Then select the Create new book instance (copy) link. If everything is set up correctly, your site should look something like the following screenshot. After you submit a valid BookInstance, it should be saved and you'll be taken to the detail page.

+Run the application and open your browser to . Then select the _Create new book instance (copy)_ link. If everything is set up correctly, your site should look something like the following screenshot. After you submit a valid `BookInstance`, it should be saved and you'll be taken to the detail page. -

+![](locallibary_express_bookinstance_create_empty.png) -

Next steps

+## Next steps - +- Return to [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms). +- Proceed to the next subarticle of part 6: [Delete Author form](/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Delete_author_form). diff --git a/files/zh-tw/learn/server-side/express_nodejs/forms/create_genre_form/index.md b/files/zh-tw/learn/server-side/express_nodejs/forms/create_genre_form/index.md index 62623646f03128..caba6ce10d3db7 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/forms/create_genre_form/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/forms/create_genre_form/index.md @@ -3,33 +3,36 @@ title: 創建種類表單 slug: Learn/Server-side/Express_Nodejs/forms/Create_genre_form translation_of: Learn/Server-side/Express_Nodejs/forms/Create_genre_form --- -

本章節演示如何定義我們的頁面,創建Genre 物件(這是一個很好的起點,因為類型Genre只有一個欄位,就是它的名稱 name,沒有依賴項)。像任何其他頁面一樣,我們需要設置路由,控制器和視圖。

+本章節演示如何定義我們的頁面,創建`Genre` 物件(這是一個很好的起點,因為類型`Genre`只有一個欄位,就是它的名稱 `name`,沒有依賴項)。像任何其他頁面一樣,我們需要設置路由,控制器和視圖。 -

+## 引入驗證與無害化方法 -

引入驗證與無害化方法

+在我們的控制器中使用 _express-validator_ 驗證器,我們必須導入我們想要從 '**express-validator/check**' 和 '**express-validator/filter**' 模塊中使用的函數。 -

在我們的控制器中使用 express-validator 驗證器,我們必須導入我們想要從 'express-validator/check' 和 'express-validator/filter' 模塊中使用的函數。

+打開 **/controllers/genreController.js**,並在文件頂部添加以下幾行: -

打開 /controllers/genreController.js,並在文件頂部添加以下幾行:

+```js +const { body,validationResult } = require('express-validator/check'); +const { sanitizeBody } = require('express-validator/filter'); +``` -
const { body,validationResult } = require('express-validator/check');
-const { sanitizeBody } = require('express-validator/filter');
+## Controller—get route -

Controller—get route

+Find the exported `genre_create_get()` controller method and replace it with the following code. This simply renders the **genre_form.pug** view, passing a title variable. -

Find the exported genre_create_get() controller method and replace it with the following code. This simply renders the genre_form.pug view, passing a title variable.

- -
// Display Genre create form on GET.
+```js
+// Display Genre create form on GET.
 exports.genre_create_get = function(req, res, next) {
     res.render('genre_form', { title: 'Create Genre' });
-};
+}; +``` -

Controller—post route

+## Controller—post route -

Find the exported genre_create_post() controller method and replace it with the following code.

+Find the exported `genre_create_post()` controller method and replace it with the following code. -
// Handle Genre create on POST.
+```js
+// Handle Genre create on POST.
 exports.genre_create_post =  [
 
     // Validate that the name field is not empty.
@@ -39,7 +42,7 @@ exports.genre_create_post =  [
     sanitizeBody('name').trim().escape(),
 
     // Process request after validation and sanitization.
-    (req, res, next) => {
+    (req, res, next) => {
 
         // Extract the validation errors from a request.
         const errors = validationResult(req);
@@ -79,36 +82,30 @@ exports.genre_create_post =  [
                  });
         }
     }
-];
- -

The first thing to note is that instead of being a single middleware function (with arguments (req, res, next)) the controller specifies an array of middleware functions. The array is passed to the router function and each method is called in order.

+]; +``` -
    -
+The first thing to note is that instead of being a single middleware function (with arguments `(req, res, next)`) the controller specifies an _array_ of middleware functions. The array is passed to the router function and each method is called in order. -
-

Note: This approach is needed, because the sanitisers/validators are middleware functions.

-
+> **備註:** This approach is needed, because the sanitisers/validators are middleware functions. -

The first method in the array defines a validator (body) to check that the name field is not empty (calling trim() to remove any trailing/leading whitespace before performing the validation). The second method in the array (sanitizeBody()) creates a sanitizer to trim() the name field and escape() any dangerous HTML characters.

+The first method in the array defines a validator (`body`) to check that the _name_ field is not empty (calling `trim()` to remove any trailing/leading whitespace before performing the validation). The second method in the array (`sanitizeBody()`) creates a sanitizer to `trim()` the _name_ field and `escape()` any dangerous HTML characters. -
// Validate that the name field is not empty.
+```js
+// Validate that the name field is not empty.
 body('name', 'Genre name required').isLength({ min: 1 }).trim(),
 
 // Sanitize (trim and escape) the name field.
-sanitizeBody('name').trim().escape(),
+sanitizeBody('name').trim().escape(), +``` -
    -
+> **備註:** Sanitizers run during validation do not modify the request. That is why we have to call `trim()` in both steps above! -
-

Note: Sanitizers run during validation do not modify the request. That is why we have to call trim() in both steps above!

-
+After specifying the validators and sanitizers we create a middleware function to extract any validation errors. We use `isEmpty()` to check whether there are any errors in the validation result. If there are then we render the form again, passing in our sanitised genre object and the array of error messages (`errors.array()`). -

After specifying the validators and sanitizers we create a middleware function to extract any validation errors. We use isEmpty() to check whether there are any errors in the validation result. If there are then we render the form again, passing in our sanitised genre object and the array of error messages (errors.array()).

- -
// Process request after validation and sanitization.
-(req, res, next) => {
+```js
+// Process request after validation and sanitization.
+(req, res, next) => {
 
     // Extract the validation errors from a request.
     const errors = validationResult(req);
@@ -125,13 +122,15 @@ sanitizeBody('name').trim().escape(),
} else { // Data from form is valid. - ... <save the result> ... + ... ... } -} +} +``` -

If the genre name data is valid then we check if a Genre with the same name already exists (as we don't want to create duplicates). If it does we redirect to the existing genre's detail page. If not, we save the new Genre and redirect to its detail page.

+If the genre name data is valid then we check if a `Genre` with the same name already exists (as we don't want to create duplicates). If it does we redirect to the existing genre's detail page. If not, we save the new `Genre` and redirect to its detail page. -
// Check if Genre with same name already exists.
+```js
+// Check if Genre with same name already exists.
 Genre.findOne({ 'name': req.body.name })
     .exec( function(err, found_genre) {
     if (err) { return next(err); }
@@ -146,20 +145,24 @@ Genre.findOne({ 'name': req.body.name })
                     res.redirect(genre.url);
                 });
         }
-});
+}); +``` -

This same pattern is used in all our post controllers: we run validators, then sanitisers, then check for errors and either re-render the form with error information or save the data.

+This same pattern is used in all our post controllers: we run validators, then sanitisers, then check for errors and either re-render the form with error information or save the data. -

View

+## View -

The same view is rendered in both the GET and POST controllers/routes when we create a new Genre (and later on it is also used when we update a Genre). In the GET case the form is empty and we just pass a title variable. In the POST case the user has previously entered invalid data—in the genre variable we pass back a sanitized version of the entered data and in the errors variable we pass back an array of error messages.

+The same view is rendered in both the `GET` and `POST` controllers/routes when we create a new `Genre` (and later on it is also used when we _update_ a `Genre`). In the `GET` case the form is empty and we just pass a title variable. In the `POST` case the user has previously entered invalid data—in the `genre` variable we pass back a sanitized version of the entered data and in the `errors` variable we pass back an array of error messages. -
res.render('genre_form', { title: 'Create Genre'});
-res.render('genre_form', { title: 'Create Genre', genre: genre, errors: errors.array()});
+```js +res.render('genre_form', { title: 'Create Genre'}); +res.render('genre_form', { title: 'Create Genre', genre: genre, errors: errors.array()}); +``` -

Create /views/genre_form.pug and copy in the text below.

+Create **/views/genre_form.pug** and copy in the text below. -
extends layout
+```html
+extends layout
 
 block content
   h1 #{title}
@@ -173,39 +176,36 @@ block content
   if errors
     ul
       for error in errors
-        li!= error.msg
- -

Much of this template will be familiar from our previous tutorials. First we extend the layout.pug base template and override the block named 'content'. We then have a heading with the title we passed in from the controller (via the render() method).

+ li!= error.msg +``` -

Next we have the pug code for our HTML form that uses the POST method to send the data to the server, and because the action is an empty string, will send the data to the same URL as the page.

+Much of this template will be familiar from our previous tutorials. First we extend the **layout.pug** base template and override the `block` named '**content**'. We then have a heading with the `title` we passed in from the controller (via the `render()` method). -

The form defines a single required field of type "text" called "name". The default value of the field depends on whether the genre variable is defined. If called from the GET route it will be empty as this is a new form. If called from a POST route it will contain the (invalid) value originally entered by the user.

+Next we have the pug code for our HTML form that uses the `POST` `method` to send the data to the server, and because the `action` is an empty string, will send the data to the same URL as the page. -

The last part of the page is the error code. This simply prints a list of errors, if the error variable has been defined (in other words, this section will not appear when the template is rendered on the GET route).

+The form defines a single required field of type "text" called "name". The default _value_ of the field depends on whether the `genre` variable is defined. If called from the `GET` route it will be empty as this is a new form. If called from a `POST` route it will contain the (invalid) value originally entered by the user. -
-

Note: This is just one way to render the errors. You can also get the names of the affected fields from the error variable, and use these to control where the error messages are rendered, whether to apply custom CSS, etc.

-
+The last part of the page is the error code. This simply prints a list of errors, if the error variable has been defined (in other words, this section will not appear when the template is rendered on the `GET` route). -

What does it look like?

+> **備註:** This is just one way to render the errors. You can also get the names of the affected fields from the error variable, and use these to control where the error messages are rendered, whether to apply custom CSS, etc. -

Run the application, open your browser to http://localhost:3000/, then select the Create new genre link. If everything is set up correctly, your site should look something like the following screenshot. After you enter a value, it should be saved and you'll be taken to the genre detail page.

+## What does it look like? -

Genre Create Page - Express Local Library site

+Run the application, open your browser to , then select the _Create new genre_ link. If everything is set up correctly, your site should look something like the following screenshot. After you enter a value, it should be saved and you'll be taken to the genre detail page. -

The only error we validate against server-side is that the genre field must not be empty. The screenshot below shows what the error list would look like if you didn't supply a genre (highlighted in red).

+![Genre Create Page - Express Local Library site](locallibary_express_genre_create_empty.png) -

+The only error we validate against server-side is that the genre field must not be empty. The screenshot below shows what the error list would look like if you didn't supply a genre (highlighted in red). -
-

備註: Our validation uses trim() to ensure that whitespace is not accepted as a genre name. We can also validate that the field is not empty on the client side by adding the value required='true' to the field definition in the form:

+![](locallibary_express_genre_create_error.png) -
input#name.form-control(type='text', placeholder='Fantasy, Poetry etc.' name='name' value=(undefined===genre ? '' : genre.name), required='true' )
-
+> **備註:** Our validation uses `trim()` to ensure that whitespace is not accepted as a genre name. We can also validate that the field is not empty on the client side by adding the value `required='true'` to the field definition in the form: +> +> ```js +> input#name.form-control(type='text', placeholder='Fantasy, Poetry etc.' name='name' value=(undefined===genre ? '' : genre.name), required='true' ) +> ``` -

Next steps

+## Next steps - +- Return to [Express Tutorial Part 6: Working with forms.](/en-US/docs/Learn/Server-side/Express_Nodejs/forms) +- Proceed to the next subarticle of part 6: [Create Author form](/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_author_form). diff --git a/files/zh-tw/learn/server-side/express_nodejs/forms/delete_author_form/index.md b/files/zh-tw/learn/server-side/express_nodejs/forms/delete_author_form/index.md index 4871dee29d195e..7b3152e62a14c7 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/forms/delete_author_form/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/forms/delete_author_form/index.md @@ -3,17 +3,16 @@ title: Delete Author form slug: Learn/Server-side/Express_Nodejs/forms/Delete_author_form translation_of: Learn/Server-side/Express_Nodejs/forms/Delete_author_form --- -

+此子文檔展示,如何定義頁面以刪除 `Author`對象。 -

此子文檔展示,如何定義頁面以刪除 Author對象。

+正如[表單設計](/zh-TW/docs/Learn/Server-side/Express_Nodejs/forms#form_design)部分所討論的那樣,我們的策略是,只允許刪除“未被其他對象引用” 的對象(在這種情況下,這意味著如果作者`Author`被一本書`Book`引用,我們將不允許刪除作者)。在實現方面,這意味著,表單需要在刪除作者之前,先確認沒有關聯的書籍。如果存在關聯的書籍,則應顯示它們,並說明在刪除`Author`對象之前,必須刪除它們。 -

正如表單設計部分所討論的那樣,我們的策略是,只允許刪除“未被其他對象引用” 的對象(在這種情況下,這意味著如果作者Author被一本書Book引用,我們將不允許刪除作者)。在實現方面,這意味著,表單需要在刪除作者之前,先確認沒有關聯的書籍。如果存在關聯的書籍,則應顯示它們,並說明在刪除Author對象之前,必須刪除它們。

+## Controller—get route -

Controller—get route

+Open **/controllers/authorController.js**. Find the exported `author_delete_get()` controller method and replace it with the following code. -

Open /controllers/authorController.js. Find the exported author_delete_get() controller method and replace it with the following code.

- -
// Display Author delete form on GET.
+```js
+// Display Author delete form on GET.
 exports.author_delete_get = function(req, res, next) {
 
     async.parallel({
@@ -32,25 +31,27 @@ exports.author_delete_get = function(req, res, next) {
         res.render('author_delete', { title: 'Delete Author', author: results.author, author_books: results.authors_books } );
     });
 
-};
- -

The controller gets the id of the Author instance to be deleted from the URL parameter (req.params.id). It uses the async.parallel() method to get the author record and all associated books in parallel. When both operations have completed it renders the author_delete.pug view, passing variables for the title, author, and author_books.

+}; +``` -
-

Note: If findById() returns no results the author is not in the database. In this case there is nothing to delete, so we immediately render the list of all authors.

+The controller gets the id of the `Author` instance to be deleted from the URL parameter (`req.params.id`). It uses the `async.parallel()` method to get the author record and all associated books in parallel. When both operations have completed it renders the **`author_delete`**.pug view, passing variables for the `title`, `author`, and `author_books`. -
}, function(err, results) {
-    if (err) { return next(err); }
-    if (results.author==null) { // No results.
-        res.redirect('/catalog/authors')
-    }
-
+> **備註:** If `findById()` returns no results the author is not in the database. In this case there is nothing to delete, so we immediately render the list of all authors. +> +> ```js +> }, function(err, results) { +> if (err) { return next(err); } +> if (results.author==null) { // No results. +> res.redirect('/catalog/authors') +> } +> ``` -

Controller—post route

+## Controller—post route -

Find the exported author_delete_post() controller method, and replace it with the following code.

+Find the exported `author_delete_post()` controller method, and replace it with the following code. -
// Handle Author delete on POST.
+```js
+// Handle Author delete on POST.
 exports.author_delete_post = function(req, res, next) {
 
     async.parallel({
@@ -63,7 +64,7 @@ exports.author_delete_post = function(req, res, next) {
     }, function(err, results) {
         if (err) { return next(err); }
         // Success
-        if (results.authors_books.length > 0) {
+        if (results.authors_books.length > 0) {
             // Author has books. Render in same way as for GET route.
             res.render('author_delete', { title: 'Delete Author', author: results.author, author_books: results.authors_books } );
             return;
@@ -77,19 +78,19 @@ exports.author_delete_post = function(req, res, next) {
             })
         }
     });
-};
+}; +``` -

First we validate that an id has been provided (this is sent via the form body parameters, rather than using the version in the URL). Then we get the author and their associated books in the same way as for the GET route. If there are no books then we delete the author object and redirect to the list of all authors. If there are still books then we just re-render the form, passing in the author and list of books to be deleted.

+First we validate that an id has been provided (this is sent via the form body parameters, rather than using the version in the URL). Then we get the author and their associated books in the same way as for the `GET` route. If there are no books then we delete the author object and redirect to the list of all authors. If there are still books then we just re-render the form, passing in the author and list of books to be deleted. -
-

Note: We could check if the call to findById() returns any result, and if not, immediately render the list of all authors. We've left the code as it is above for brevity (it will still return the list of authors if the id is not found, but this will happen after findByIdAndRemove()).

-
+> **備註:** We could check if the call to `findById()` returns any result, and if not, immediately render the list of all authors. We've left the code as it is above for brevity (it will still return the list of authors if the id is not found, but this will happen after `findByIdAndRemove()`). -

View

+## View -

Create /views/author_delete.pug and copy in the text below.

+Create **/views/author_delete.pug** and copy in the text below. -
extends layout
+```html
+extends layout
 
 block content
   h1 #{title}: #{author.name}
@@ -116,52 +117,47 @@ block content
       div.form-group
         input#authorid.form-control(type='hidden',name='authorid', required='true', value=author._id )
 
-      button.btn.btn-primary(type='submit') Delete
+ button.btn.btn-primary(type='submit') Delete +``` -

The view extends the layout template, overriding the block named content. At the top it displays the author details. It then includes a conditional statement based on the number of author_books (the if and else clauses).

+The view extends the layout template, overriding the block named `content`. At the top it displays the author details. It then includes a conditional statement based on the number of **`author_books`** (the `if` and `else` clauses). -
    -
  • If there are books associated with the author then the page lists the books and states that these must be deleted before this Author may be deleted.
  • -
  • If there are no books then the page displays a confirmation prompt. If the Delete button is clicked then the author id is sent to the server in a POST request and that author's record will be deleted.
  • -
+- If there _are_ books associated with the author then the page lists the books and states that these must be deleted before this `Author` may be deleted. +- If there _are no_ books then the page displays a confirmation prompt. If the **Delete** button is clicked then the author id is sent to the server in a `POST` request and that author's record will be deleted. -

Add a delete control

+## Add a delete control -

Next we will add a Delete control to the Author detail view (the detail page is a good place from which to delete a record).

+Next we will add a **Delete** control to the _Author detail_ view (the detail page is a good place from which to delete a record). -
-

Note: In a full implementation the control would be made visible only to authorised users. However at this point we haven't got an authorisation system in place!

-
+> **備註:** In a full implementation the control would be made visible only to authorised users. However at this point we haven't got an authorisation system in place! -

Open the author_detail.pug view and add the following lines at the bottom.

+Open the **author_detail.pug** view and add the following lines at the bottom. -
hr
+```html
+hr
 p
-  a(href=author.url+'/delete') Delete author
+ a(href=author.url+'/delete') Delete author +``` -

The control should now appear as a link, as shown below on the Author detail page.

+The control should now appear as a link, as shown below on the _Author detail_ page. -

+![](locallibary_express_author_detail_delete.png) -

What does it look like?

+## What does it look like? -

Run the application and open your browser to http://localhost:3000/. Then select the All authors link, and then select a particular author. Finally select the Delete author link.

+Run the application and open your browser to . Then select the _All authors_ link, and then select a particular author. Finally select the _Delete author_ link. -

If the author has no books, you'll be presented with a page like this. After pressing delete, the server will delete the author and redirect to the author list.

+If the author has no books, you'll be presented with a page like this. After pressing delete, the server will delete the author and redirect to the author list. -

+![](locallibary_express_author_delete_nobooks.png) -

If the author does have books, then you'll be presented with a view like the following. You can then delete the books from their detail pages (once that code is implemented!).

+If the author does have books, then you'll be presented with a view like the following. You can then delete the books from their detail pages (once that code is implemented!). -

+![](locallibary_express_author_delete_withbooks.png) -
-

Note: The other pages for deleting objects can be implemented in much the same way. We've left that as a challenge.

-
+> **備註:** The other pages for deleting objects can be implemented in much the same way. We've left that as a challenge. -

Next steps

+## Next steps - +- Return to [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms). +- Proceed to the final subarticle of part 6: [Update Book form](/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Update_Book_form). diff --git a/files/zh-tw/learn/server-side/express_nodejs/forms/index.md b/files/zh-tw/learn/server-side/express_nodejs/forms/index.md index 6f773f670257ac..488e25db0339be 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/forms/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/forms/index.md @@ -3,266 +3,244 @@ title: 'Express 教學 6: 使用表單' slug: Learn/Server-side/Express_Nodejs/forms translation_of: Learn/Server-side/Express_Nodejs/forms --- -
{{LearnSidebar}}
+{{LearnSidebar}}{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs/deployment", "Learn/Server-side/Express_Nodejs")}} -
{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs/deployment", "Learn/Server-side/Express_Nodejs")}}
- -

在此教程中,我們會教你如何使用 Express ,並且結合 Pug 來實現 HTML 表單,並且如何從數據庫中創建、更新、和刪除文檔。

+在此教程中,我們會教你如何使用 Express ,並且結合 Pug 來實現 HTML 表單,並且如何從數據庫中創建、更新、和刪除文檔。 - - - - - - - - - - + + + + + + + + + +
前提條件:完成前面所有的教程,包括 Express 教程第5章: 展示圖書館數據。
目標:了解如何編寫表單獲取用戶信息,並且將這些數據更新到數據庫中。
前提條件:完成前面所有的教程,包括 Express 教程第5章: 展示圖書館數據。
目標:了解如何編寫表單獲取用戶信息,並且將這些數據更新到數據庫中。
-

概覽

+## 概覽 + +[HTML 表單](/zh-TW/docs/Web/Guide/HTML/Forms)是網頁中由一個、或多個字段/小工具形成的一個組合,它被用來收集用戶的信息,並將信息上傳到服務器。表單作為一種用來收集用戶的機制,非常的靈活,因為有各種合適的輸入框,來接受各種類型的數據——文本框,複選框,單選按鈕,時間選擇器等。表單和服務器交互數據也相對安全,因為它使用`POST`請求發送數據,保護不受跨站點請求偽造攻擊(cross-site request forgery)的威脅。 + +但是表單同樣也很複雜!開發者需要編寫給表單編寫 HTML,在服務器上驗證,並且正確去除有害的數據(瀏覽器上也可能需要),對於任何不合法的字段,需要傳給用戶相應的錯誤信息,當數據成功提交後,處理數據,並設法通知用戶提交成功。 -

HTML 表單是網頁中由一個、或多個字段/小工具形成的一個組合,它被用來收集用戶的信息,並將信息上傳到服務器。表單作為一種用來收集用戶的機制,非常的靈活,因為有各種合適的輸入框,來接受各種類型的數據——文本框,複選框,單選按鈕,時間選擇器等。表單和服務器交互數據也相對安全,因為它使用POST請求發送數據,保護不受跨站點請求偽造攻擊(cross-site request forgery)的威脅。

+此教程將展示上述的操作,如何在 Express 中實現。在此過程中,我們將擴展 LocalLibrary 網站,以允許用戶創建、編輯、和刪除圖書館中的項目。 -

但是表單同樣也很複雜!開發者需要編寫給表單編寫 HTML,在服務器上驗證,並且正確去除有害的數據(瀏覽器上也可能需要),對於任何不合法的字段,需要傳給用戶相應的錯誤信息,當數據成功提交後,處理數據,並設法通知用戶提交成功。

+> **備註:** 我們還沒有考慮如何將特定路由,限制為經過身份驗證或授權的用戶,因此在這個時間點,任何用戶都可以對數據庫進行更改。 -

此教程將展示上述的操作,如何在 Express 中實現。在此過程中,我們將擴展 LocalLibrary 網站,以允許用戶創建、編輯、和刪除圖書館中的項目。

+### HTML 表單 -
-

備註: 我們還沒有考慮如何將特定路由,限制為經過身份驗證或授權的用戶,因此在這個時間點,任何用戶都可以對數據庫進行更改。

-
+首先簡要概述 [HTML 表單](/zh-TW/docs/Web/Guide/HTML/Forms)。考慮一個簡單的 HTML 表單,其中包含一個文本字段,用於輸入某些 “團隊” 的名稱,及其相關標籤: -

HTML 表單

+![Simple name field example in HTML form](form_example_name_field.png) -

首先簡要概述 HTML 表單。考慮一個簡單的 HTML 表單,其中包含一個文本字段,用於輸入某些 “團隊” 的名稱,及其相關標籤:

+表單在 HTML 中,定義為 `
...
`標記內的元素集合,包含至少一個`type="submit"`的 `input`輸入元素。 -

Simple name field example in HTML form

+```html +
+ + + +
+``` -

表單在HTML中,定義為 <form>...</form>標記內的元素集合,包含至少一個type="submit"input輸入元素。

+雖然這裡,我們只包含一個(文本)字段,用於輸入團隊名稱,但表單可能包含任意數量的其他輸入元素,及其相關標籤。字段的 `type` 屬性,定義將顯示哪種窗口小部件。該字段的名稱`name`和`id` ,用於標識 JavaScript/CSS/HTML 中的字段,而 `value`定義字段首次顯示時的初始值。匹配團隊標籤使用 `label` 標籤,指定(請參閱上面的“輸入名稱” "Enter name"),其中 `for` 字段,包含 `input` 相關輸入的 `id`值。 -
<form action="/team_name_url/" method="post">
-    <label for="team_name">Enter name: </label>
-    <input id="team_name" type="text" name="name_field" value="Default name for team.">
-    <input type="submit" value="OK">
-</form>
+提交輸入(`submit`)將顯示為按鈕(默認情況下) - 用戶可以按此按鈕,將其他輸入元素包含的數據,上傳到服務器(在本例中,只有 `team_name`)。表單屬性,定義用於發送數據的 HTTP `method`方法,和服務器上數據的目標(`action`): -

雖然這裡,我們只包含一個(文本)字段,用於輸入團隊名稱,但表單可能包含任意數量的其他輸入元素,及其相關標籤。字段的 type 屬性,定義將顯示哪種窗口小部件。該字段的名稱nameid ,用於標識JavaScript/CSS/HTML 中的字段,而 value定義字段首次顯示時的初始值。匹配團隊標籤使用 label 標籤,指定(請參閱上面的“輸入名稱” "Enter name"),其中 for 字段,包含 input 相關輸入的 id值。

+- `action`: 提交表單時,要發送數據以進行處理的資源/ URL。如果未設置(或設置為空字符串),則表單將提交回當前頁面 URL。 +- `method`: 用於發送數據的 HTTP 方法:`POST` 或 `GET`。 -

提交輸入(submit)將顯示為按鈕(默認情況下) - 用戶可以按此按鈕,將其他輸入元素包含的數據,上傳到服務器(在本例中,只有 team_name)。表單屬性,定義用於發送數據的HTTP method方法,和服務器上數據的目標(action):

+ - `POST` 方法。如果數據將導致服務器數據庫的更改,則始終應該使用 `POST`方法,因為這更加可以抵抗跨站點偽造請求攻擊。 + - `GET` 方法只應用於不更改用戶數據的表單(例如,搜索表單)。當您希望能夠為 URL 添加書籤或共享時,建議使用此選項。 -
    -
  • action: 提交表單時,要發送數據以進行處理的資源/ URL。如果未設置(或設置為空字符串),則表單將提交回當前頁面 URL。
  • -
  • method: 用於發送數據的 HTTP 方法:POSTGET。 -
      -
    • POST 方法。如果數據將導致服務器數據庫的更改,則始終應該使用 POST方法,因為這更加可以抵抗跨站點偽造請求攻擊。
    • -
    • GET 方法只應用於不更改用戶數據的表單(例如,搜索表單)。當您希望能夠為URL添加書籤或共享時,建議使用此選項。
    • -
    -
  • -
+### 表單處理流程 -

表單處理流程

+表單處理使用的技術,與我們學習過、用來顯示有關模型的信息的所有技術,是相同的:路由將我們的請求發送到控制器函數,該函數執行所需的任何數據庫操作,包括從模型中讀取數據,然後生成並返回 HTML 頁面。使事情變得更複雜的是,服務器還需要能夠處理用戶提供的數據,並在出現任何問題時,重新顯示帶有錯誤信息的表單。 -

表單處理使用的技術,與我們學習過、用來顯示有關模型的信息的所有技術,是相同的:路由將我們的請求發送到控制器函數,該函數執行所需的任何數據庫操作,包括從模型中讀取數據,然後生成並返回 HTML 頁面。使事情變得更複雜的是,服務器還需要能夠處理用戶提供的數據,並在出現任何問題時,重新顯示帶有錯誤信息的表單。

+下面顯示了處理表單請求的流程圖,從包含表單的頁面請求開始(以綠色顯示): -

下面顯示了處理表單請求的流程圖,從包含表單的頁面請求開始(以綠色顯示):

+![](web_server_form_handling.png) -

+如上圖所示,構成處理代碼所需要做的主要是: -

如上圖所示,構成處理代碼所需要做的主要是:

+1. 在用戶第一次請求時顯示默認表單。 -
    -
  1. 在用戶第一次請求時顯示默認表單。 -
      -
    • 表單可能包含空白字段(例如,如果您正在創建新記錄),或者可能預先填充了初始值(例如,如果您要更改記錄,或者俱有有用的默認初始值)。
    • -
    -
  2. -
  3. 接收用戶提交的數據,通常是在HTTP POST請求中。
  4. -
  5. 驗證並清理數據。
  6. -
  7. 如果任何數據無效,請重新顯示表單 - 這次使用用戶填寫的任何值,和問題字段的錯誤消息。
  8. -
  9. 如果所有數據都有效,請執行所需的操作(例如,將數據保存在數據庫中,發送通知電子郵件,返回搜索結果,上傳文件等)
  10. -
  11. 完成所有操作之後,將用戶重定向到另一個頁面。
  12. -
+ - 表單可能包含空白字段(例如,如果您正在創建新記錄),或者可能預先填充了初始值(例如,如果您要更改記錄,或者俱有有用的默認初始值)。 -

表格處理代碼,通常使用 GET路由,以實現表單的初始顯示,以及 POST路由到同一路徑,以處理表單數據的驗證和處理。這是將在本教程中使用的方法!

+2. 接收用戶提交的數據,通常是在 HTTP `POST`請求中。 +3. 驗證並清理數據。 +4. 如果任何數據無效,請重新顯示表單 - 這次使用用戶填寫的任何值,和問題字段的錯誤消息。 +5. 如果所有數據都有效,請執行所需的操作(例如,將數據保存在數據庫中,發送通知電子郵件,返回搜索結果,上傳文件等) +6. 完成所有操作之後,將用戶重定向到另一個頁面。 -

Express 本身不提供表單處理操作的任何特定支持,但它可以使用中間件,以處理表單中的 POSTGET參數,並驗證/清理它們的值。

+表格處理代碼,通常使用 `GET`路由,以實現表單的初始顯示,以及 `POST`路由到同一路徑,以處理表單數據的驗證和處理。這是將在本教程中使用的方法! -

驗證和清理

+Express 本身不提供表單處理操作的任何特定支持,但它可以使用中間件,以處理表單中的 `POST`和 `GET`參數,並驗證/清理它們的值。 -

在儲存表單中的數據之前,必須對其進行驗證和清理:

+### 驗證和清理 -
    -
  • 驗證檢查輸入的值,適用於每個字段(範圍,格式等),並且已為所有必填字段提供了值。
  • -
  • 數據中的字符,可能用於將惡意內容送到服務器,為其進行清理刪除/替換。
  • -
+在儲存表單中的數據之前,必須對其進行驗證和清理: -

在本教程中,我們將使用流行的 express-validator 模塊,來執行表單數據的驗證和清理。

+- 驗證檢查輸入的值,適用於每個字段(範圍,格式等),並且已為所有必填字段提供了值。 +- 數據中的字符,可能用於將惡意內容送到服務器,為其進行清理刪除/替換。 -

安裝

+在本教程中,我們將使用流行的 [express-validator ](https://www.npmjs.com/package/express-validator)模塊,來執行表單數據的驗證和清理。 -

通過在項目的根目錄中,運行以下命令,來安裝模塊。

+#### 安裝 -
npm install express-validator
-
+通過在項目的根目錄中,運行以下命令,來安裝模塊。 -

使用 express-validator

+```bash +npm install express-validator +``` -
-

備註: Github上的 express-validator 指南,提供了API的良好概述。我們建議您閱讀該內容,以了解其所有功能(包括創建自定義驗證程序)。下面我們只介紹一個對 LocalLibrary 有用的子集。

-
+#### 使用 express-validator -

要在我們的控制器中使用驗證器,我們必須從 'express-validator/check'和'express-validator/filter'模塊中,導入我們想要使用的函數,如下所示:

+> **備註:** Github 上的 [express-validator](https://github.com/ctavan/express-validator#express-validator) 指南,提供了 API 的良好概述。我們建議您閱讀該內容,以了解其所有功能(包括創建自定義驗證程序)。下面我們只介紹一個對 LocalLibrary 有用的子集。 -
const { body,validationResult } = require('express-validator/check');
+要在我們的控制器中使用驗證器,我們必須從 'e**xpress-validator/check**'和'**express-validator/filter**'模塊中,導入我們想要使用的函數,如下所示:
+
+```js
+const { body,validationResult } = require('express-validator/check');
 const { sanitizeBody } = require('express-validator/filter');
-
+``` + +有許多可用的功能,允許您一次檢查和清理請求參數,正文,標頭,cookie 等數據,或所有數據。對於本教程,我們主要使用 `body`, `sanitizeBody`,和 `validationResult`(如上面 required 導入的 )。 -

有許多可用的功能,允許您一次檢查和清理請求參數,正文,標頭,cookie 等數據,或所有數據。對於本教程,我們主要使用 bodysanitizeBody,和 validationResult(如上面 required 導入的 )。

+功能定義如下: -

功能定義如下:

+- [`body(fields[, message])`](https://github.com/ctavan/express-validator#bodyfields-message): 指定請求本文中的一組字段(`POST`參數)以及可選的錯誤消息,如果測試失敗,則可以顯示該字段。驗證標準以菊花鏈形式連接到 `body()`方法。例如,下面的第一個檢查測試,“name”字段不為空,如果不是,則設置錯誤消息“Empty name”。第二個測試,檢查 age 字段是否為有效日期,並使用`optional()`指定 null 和空字符串不會驗證失敗。 -
    -
  • body(fields[, message]): 指定請求本文中的一組字段(POST參數)以及可選的錯誤消息,如果測試失敗,則可以顯示該字段。驗證標準以菊花鏈形式連接到 body()方法。例如,下面的第一個檢查測試,“name”字段不為空,如果不是,則設置錯誤消息“Empty name”。第二個測試,檢查age字段是否為有效日期,並使用optional()指定null和空字符串不會驗證失敗。 + ```js + body('name', 'Empty name').isLength({ min: 1 }), + body('age', 'Invalid age').optional({ checkFalsy: true }).isISO8601(), + ``` -
    body('name', 'Empty name').isLength({ min: 1 }),
    -body('age', 'Invalid age').optional({ checkFalsy: true }).isISO8601(),
    -
    您還可以用菊花鍊式連接不同的驗證器,並添加前面驗證器為真時顯示的消息。 -
    body('name').isLength({ min: 1 }).trim().withMessage('Name empty.')
    -    .isAlpha().withMessage('Name must be alphabet letters.'),
    -
    + ```js + body('name').isLength({ min: 1 }).trim().withMessage('Name empty.') + .isAlpha().withMessage('Name must be alphabet letters.'), + ``` + + > **備註:** 您還可以添加內聯清理器,如`trim()`,如上所示。但是,此處應用清理器,僅適用於驗證步驟。如果要對最終輸出進行消毒,則需要使用單獨的清理器方法,如下所示。 + +- [`sanitizeBody(fields)`](https://github.com/ctavan/express-validator#sanitizebodyfields): 指定一個正文要清理的字段。然後將清理操作,以菊花鏈形式連接到此方法。例如,下面的 `escape()`清理操作,會從名稱變量中,刪除可能在 JavaScript 跨站點腳本攻擊中使用的 HTML 字符。 + + ```js + sanitizeBody('name').trim().escape(), + sanitizeBody('date').toDate(), + ``` + +- [`validationResult(req)`](https://github.com/ctavan/express-validator#validationresultreq): 運行驗證,以 `validation `驗證結果對象的形式,提供錯誤。這是在單獨的回調中調用的,如下所示: + + ```js + (req, res, next) => { + // Extract the validation errors from a request. + const errors = validationResult(req); + + if (!errors.isEmpty()) { + // There are errors. Render form again with sanitized values/errors messages. + // Error messages can be returned in an array using `errors.array()`. + } + else { + // Data from form is valid. + } + } + ``` -
    -

    備註: 您還可以添加內聯清理器,如trim(),如上所示。但是,此處應用清理器,僅適用於驗證步驟。如果要對最終輸出進行消毒,則需要使用單獨的清理器方法,如下所示。

    -
    -
  • -
  • sanitizeBody(fields): 指定一個正文要清理的字段。然後將清理操作,以菊花鏈形式連接到此方法。例如,下面的 escape()清理操作,會從名稱變量中,刪除可能在 JavaScript 跨站點腳本攻擊中使用的 HTML 字符。 -
    sanitizeBody('name').trim().escape(),
    -sanitizeBody('date').toDate(),
    -
  • -
  • validationResult(req): 運行驗證,以 validation 驗證結果對象的形式,提供錯誤。這是在單獨的回調中調用的,如下所示: -
    (req, res, next) => {
    -    // Extract the validation errors from a request.
    -    const errors = validationResult(req);
    +  我們使用驗證結果的`isEmpty()`方法,來檢查是否存在錯誤,並使用其 `array()` 方法,來獲取錯誤消息集合。有關更多信息,請參閱驗證結果的 [Validation Result API](https://github.com/ctavan/express-validator#validation-result-api)。
     
    -    if (!errors.isEmpty()) {
    -        // There are errors. Render form again with sanitized values/errors messages.
    -        // Error messages can be returned in an array using `errors.array()`.
    -        }
    -    else {
    -        // Data from form is valid.
    -    }
    -}
    - 我們使用驗證結果的isEmpty()方法,來檢查是否存在錯誤,並使用其 array() 方法,來獲取錯誤消息集合。有關更多信息,請參閱驗證結果的 Validation Result API
  • -
+驗證和清理鏈,是應該傳遞給 Express 路由處理程序的中間件(我們通過控制器,間接地執行此操作)。中間件運行時,每個驗證器/清理程序都按指定的順序運行。 -

驗證和清理鏈,是應該傳遞給 Express 路由處理程序的中間件(我們通過控制器,間接地執行此操作)。中間件運行時,每個驗證器/清理程序都按指定的順序運行。

+當我們實現下面的 LocalLibrary 表單時,我們將介紹一些真實的例子。 -

當我們實現下面的LocalLibrary表單時,我們將介紹一些真實的例子。

+### 表單設計 -

表單設計

+圖書館中的許多模型都是相關/依賴的 - 例如,一本書需要一個作者,也可能有一個或多個種類。這提出了一個問題,即我們應該如何處理用戶希望的情況: -

圖書館中的許多模型都是相關/依賴的 - 例如,一本書需要一個作者,也可能有一個或多個種類。這提出了一個問題,即我們應該如何處理用戶希望的情況:

+- 在其相關對象尚不存在時,創建對象(例如,尚未定義作者對象的書)。 -
    -
  • 在其相關對象尚不存在時,創建對象(例如,尚未定義作者對象的書)。
    -
  • -
  • 刪除另一個對象仍在使用的對象(例如,刪除仍有書本 Book 使用的種類 Genre)。
  • -
+- 刪除另一個對象仍在使用的對象(例如,刪除仍有書本 `Book `使用的種類 `Genre`)。 -

在這個項目,我們為了簡化實作,將聲明表單只能:

+在這個項目,我們為了簡化實作,將聲明表單只能: -
    -
  • 使用已存在的對象創建對象(因此用戶在嘗試創建任何 Book對象之前,必須創建任何所需的 AuthorGenre實例)。
  • -
  • 如果對象未被其他對象引用,則刪除該對象(例如,在刪除所有關聯的 BookInstance對象之前,您將無法刪除該書)。
  • -
+- 使用已存在的對象創建對象(因此用戶在嘗試創建任何 `Book`對象之前,必須創建任何所需的 `Author`和 `Genre`實例)。 +- 如果對象未被其他對象引用,則刪除該對象(例如,在刪除所有關聯的 `BookInstance`對象之前,您將無法刪除該書)。 -
-

備註: 更“牢固”的實現,可能允許您在創建新對象時,創建依賴對象,並隨時刪除任何對象(例如,通過刪除依賴對象,或從數據庫中,刪除對已刪除對象的引用) 。

-
+> **備註:** 更“牢固”的實現,可能允許您在創建新對象時,創建依賴對象,並隨時刪除任何對象(例如,通過刪除依賴對象,或從數據庫中,刪除對已刪除對象的引用) 。 -

路由

+### 路由 -

為了實現我們的表單處理代碼,我們需要兩個具有相同 URL 模式的路由。

+為了實現我們的表單處理代碼,我們需要兩個具有相同 URL 模式的路由。 -

第一個(GET)路由,用於顯示用於創建對象的新空表單。第二個路由(POST),用於驗證用戶輸入的數據,然後保存信息,並重定向到詳細信息頁面(如果數據有效),或重新顯示有錯誤的表單(如果數據無效)。

+第一個(`GET`)路由,用於顯示用於創建對象的新空表單。第二個路由(`POST`),用於驗證用戶輸入的數據,然後保存信息,並重定向到詳細信息頁面(如果數據有效),或重新顯示有錯誤的表單(如果數據無效)。 -

我們已經在 /routes/catalog.js(在之前的教程中)為我們所有模型的創建頁面,創建了路徑。例如,種類路由如下所示:

+我們已經在 **/routes/catalog.js**(在之前的教程中)為我們所有模型的創建頁面,創建了路徑。例如,種類路由如下所示: -
// GET request for creating a Genre. NOTE This must come before route that displays Genre (uses id).
+```js
+// GET request for creating a Genre. NOTE This must come before route that displays Genre (uses id).
 router.get('/genre/create', genre_controller.genre_create_get);
 
 // POST request for creating Genre.
 router.post('/genre/create', genre_controller.genre_create_post);
-
+``` -

Express 表單子文件

+## Express 表單子文件 -

以下子文件,將帶我們完成向示例應用程序添加所需表單的過程。在進入下一個文件之前,您需要依次閱讀並解決每個問題。

+以下子文件,將帶我們完成向示例應用程序添加所需表單的過程。在進入下一個文件之前,您需要依次閱讀並解決每個問題。 -
    -
  1. 創建種類表單 — 定義我們的頁面以創建種類對象 Genre
  2. -
  3. 創建作者表單 — 定義用於創建作者對象 Author 的頁面。
  4. -
  5. 創建書本表單 — 定義頁面/表單以創建書本對象 Book
  6. -
  7. 創建書本實例表單 — 定義頁面/表單以創建書本實例對象 BookInstance
  8. -
  9. 刪除作者表單 — 定義要刪除作者對象 Author 的頁面。
  10. -
  11. 更新書本表單 — 定義頁面以更新書本對象 Book
  12. -
+1. [創建種類表單](/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_genre_form) — 定義我們的頁面以創建種類對象 `Genre `。 +2. [創建作者表單](/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_author_form) — 定義用於創建作者對象 `Author `的頁面。 +3. [創建書本表單](/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_book_form) — 定義頁面/表單以創建書本對象 `Book` 。 +4. [創建書本實例表單](/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_BookInstance_form) — 定義頁面/表單以創建書本實例對象 `BookInstance` 。 +5. [刪除作者表單 ](/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Delete_author_form)— 定義要刪除作者對象 `Author `的頁面。 +6. [更新書本表單](/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Update_Book_form) — 定義頁面以更新書本對象 `Book` 。 -

挑戰自我

+## 挑戰自我 -

實現 Book, BookInstance, 和 Genre模型的刪除頁面,用跟我們的作者刪除頁面相同的方式,將它們與關聯的詳細信息頁面,鏈接起來。頁面應遵循相同的設計方法:

+實現 `Book`, `BookInstance`, 和 `Genre`模型的刪除頁面,用跟我們的作者刪除頁面相同的方式,將它們與關聯的詳細信息頁面,鏈接起來。頁面應遵循相同的設計方法: -
    -
  • 如果有來自其他對象的、對於對象的引用,則應顯示註釋,列出這些對象,並說明在刪除列出的對象之前,無法刪除此記錄。
  • -
  • 如果沒有對該對象的其他引用,則視圖應提示刪除它。如果用戶按下“刪除”按鈕 Delete,則應刪除該記錄。
  • -
+- 如果有來自其他對象的、對於對象的引用,則應顯示註釋,列出這些對象,並說明在刪除列出的對象之前,無法刪除此記錄。 +- 如果沒有對該對象的其他引用,則視圖應提示刪除它。如果用戶按下“刪除”按鈕 **Delete**,則應刪除該記錄。 -

一些提示:

+一些提示: -
    -
  • 刪除種類 Genre就像刪除作者Author一樣,因為兩個對像都是 Book的依賴項(因此在這兩種情況下,只有在刪除相關書本時,才能刪除對象)。
  • -
  • 刪除書本 Book也很相似,但您需要檢查是否沒有關聯的書本實例 BookInstances
  • -
  • 刪除書本實例 BookInstances是最簡單的,因為沒有依賴對象。在這種情況下,您只需找到相關記錄並將其刪除即可。
  • -
+- 刪除種類 `Genre`就像刪除作者`Author`一樣,因為兩個對像都是 `Book`的依賴項(因此在這兩種情況下,只有在刪除相關書本時,才能刪除對象)。 +- 刪除書本 `Book`也很相似,但您需要檢查是否沒有關聯的書本實例 `BookInstances`。 +- 刪除書本實例 `BookInstances`是最簡單的,因為沒有依賴對象。在這種情況下,您只需找到相關記錄並將其刪除即可。 -

實現 BookInstance, Author, 和 Genre模型的更新頁面,以與我們的書本更新頁面相同的方式,將它們與關聯的詳細信息頁面,鏈接起來。

+實現 `BookInstance`, `Author`, 和 `Genre`模型的更新頁面,以與我們的書本更新頁面相同的方式,將它們與關聯的詳細信息頁面,鏈接起來。 -

一些提示:

+一些提示: -
    -
  • 我們剛剛實施的圖書更新頁面是最難的!相同的模式可用於其他對象的更新頁面。
  • -
  • 作者Author的死亡日期和出生日期字段,以及書本實例BookInstance 的 due_date 字段,是輸入到表單上日期輸入字段的錯誤格式(它需要 “YYYY-MM-DD” 形式的數據)。解決此問題的最簡單方法,是為適當格式化的日期,定義新的虛擬屬性,然後在關聯的視圖模板中,使用此字段。
  • -
  • 如果您遇到困難,此處示例中的更新頁面有一些示例的連結。
  • -
+- 我們剛剛實施的圖書更新頁面是最難的!相同的模式可用於其他對象的更新頁面。 +- 作者`Author`的死亡日期和出生日期字段,以及書本實例`BookInstance` 的 due_date 字段,是輸入到表單上日期輸入字段的錯誤格式(它需要 “YYYY-MM-DD” 形式的數據)。解決此問題的最簡單方法,是為適當格式化的日期,定義新的虛擬屬性,然後在關聯的視圖模板中,使用此字段。 +- 如果您遇到困難,此處示例中的更新頁面有一些示例[的連結。](https://github.com/mdn/express-locallibrary-tutorial) -

總結

+## 總結 -

Express, node, 與NPM上面的第三方套件,提供你需要的每樣東西 ,可用於新增表單到你的網站上。在本文中,您學習如何使用 Pug 創建表單,使用 express-validator 驗證和清理輸入,以及添加,刪除和修改數據庫中的記錄。

+Express, node, 與 NPM 上面的第三方套件,提供你需要的每樣東西 ,可用於新增表單到你的網站上。在本文中,您學習如何使用 Pug 創建表單,使用 express-validator 驗證和清理輸入,以及添加,刪除和修改數據庫中的記錄。 -

你現在應該了解如何新增基本表單,以及表單處理代碼到你的 node 網站!

+你現在應該了解如何新增基本表單,以及表單處理代碼到你的 node 網站! -

請參閱

+## 請參閱 - +- [express-validator](https://www.npmjs.com/package/express-validator) (npm docs). -

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs/deployment", "Learn/Server-side/Express_Nodejs")}}

+{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs/deployment", "Learn/Server-side/Express_Nodejs")}} -

本教程連結

+## 本教程連結 - +- [Express/Node introduction](/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction) +- [Setting up a Node (Express) development environment](/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment) +- [Express Tutorial: The Local Library website](/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website) +- [Express Tutorial Part 2: Creating a skeleton website](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website) +- [Express Tutorial Part 3: Using a Database (with Mongoose)](/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose) +- [Express Tutorial Part 4: Routes and controllers](/en-US/docs/Learn/Server-side/Express_Nodejs/routes) +- [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms) +- [Express Tutorial Part 7: Deploying to production](/en-US/docs/Learn/Server-side/Express_Nodejs/deployment) diff --git a/files/zh-tw/learn/server-side/express_nodejs/index.md b/files/zh-tw/learn/server-side/express_nodejs/index.md index c624eaad6714af..c81767279cd379 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/index.md @@ -12,62 +12,52 @@ tags: - 學習 translation_of: Learn/Server-side/Express_Nodejs --- -
{{LearnSidebar}}
+{{LearnSidebar}} -

Express 是一個流行的web框架,使用JavsScript實現,執行在node.js環境上。本系列解釋Express的優點、如何設定開發環境、完成常見的web開發和佈署。

+Express 是一個流行的 web 框架,使用 JavsScript 實現,執行在 node.js 環境上。本系列解釋 Express 的優點、如何設定開發環境、完成常見的 web 開發和佈署。 -

前置需求

+## 前置需求 -

在開始前你需要了解什麼是伺服器端web程式和什麼是web框架,推薦閱讀伺服器端網站開發第一步。建議了解基本的程式知識和JavaScript,但不需要知道核心概念。

+在開始前你需要了解什麼是伺服器端 web 程式和什麼是 web 框架,推薦閱讀[伺服器端網站開發第一步](/zh-TW/docs/Learn/Server-side/First_steps)。建議了解基本的程式知識和[JavaScript](/zh-TW/docs/Web/JavaScript),但不需要知道核心概念。 -
-

備註: 本網站有許多學習JavaScript應用在客戶端開發的有用資源,如:JavaScriptJavaScript 指南JavaScript 基礎JavaScript (learning)。使用Node.js開發伺服器端使用的JavaScript語言與概念和客戶端是一樣的。Node.js提供額外的APIs以支援無瀏覽器環境,例如:建立HTTP服務和讀取檔案系統。但不支援DOM及瀏覽器相關的 JavaScript API。

+> **備註:** 本網站有許多學習 JavaScript 應用在客戶端開發的有用資源,如:[JavaScript](/zh-TW/docs/Web/JavaScript)、[JavaScript 指南](/zh-TW/docs/Web/JavaScript/Guide)、[JavaScript 基礎](/zh-TW/docs/Learn/Getting_started_with_the_web/JavaScript_basics)、[JavaScript](/zh-TW/docs/Learn/JavaScript) (learning)。使用 Node.js 開發伺服器端使用的 JavaScript 語言與概念和客戶端是一樣的。Node.js 提供[額外的 APIs](https://nodejs.org/dist/latest-v6.x/docs/api/)以支援無瀏覽器環境,例如:建立 HTTP 服務和讀取檔案系統。但不支援 DOM 及瀏覽器相關的 JavaScript API。 +> +> 這份指南將提供一些使用 Node.js 和 Express 的資訊以及數個優秀的學習資源。部分連結由 [How do I get started with Node.js](http://stackoverflow.com/a/5511507/894359)(StackOverflow) 與 [What are the best resources for learning Node.js?](https://www.quora.com/What-are-the-best-resources-for-learning-Node-js?)(Quora) 提供。 -

這份指南將提供一些使用Node.js和Express的資訊以及數個優秀的學習資源。部分連結由 How do I get started with Node.js(StackOverflow) 與 What are the best resources for learning Node.js?(Quora) 提供。

-
+## 指南 -

指南

+- [Express/Node 介紹](/zh-TW/docs/Learn/Server-side/Express_Nodejs/Introduction) + - : 第一篇的系列文章中回答了「什麼是 Node」和「什麼是 Express?」並概略的說明為什麼 Express web 框架如此特別。此文章將重點放在主要的功能上,並展示一些 Express 應用常見的建構模塊(儘管此時你還沒有可供測試的開發環境) +- [設定 Node (Express) 開發環境](/zh-TW/docs/Learn/Server-side/Express_Nodejs/development_environment) + - : 現在你已經了解 Express 的目的了,接下來繼續說明如何設定和測試 Windows、Linux (Ubuntu)和 Mac OS X 上的 Node/Express 開發環境。不管你用的是什麼作業系統,你都能在本文中找到開發 Express 應用的入門需知。 +- [Express 教學(1): The Local Library website](/zh-TW/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website) + - : 在第一篇實務教學系列文章中將說明你將會學到什麼?以及提供範例網站 local library 的概覽,我們將在後續的文章中繼續改進它。 +- [Express 教學(2): 建構網站骨架](/zh-TW/docs/Learn/Server-side/Express_Nodejs/skeleton_website) + - : 本文章展示如何建構網站的骨架,接著你可以自己添加路由、模板/畫面和資料庫。 +- [Express 教學(3): 使用資料庫(以 Mongoose 為例)](/zh-TW/docs/Learn/Server-side/Express_Nodejs/mongoose) + - : 本文簡短的介紹 Node/Express 如何使用資料庫。接下來展示 LocalLibray 網站如何透過[Mongoose](http://mongoosejs.com/)進行資料庫的存取。說明物件綱要(object schema)和模型(models)如何宣告、the main field types 和基本驗證。同時簡單的展示幾個讀取資料的主要方法。 +- [Express 教學(4): 路由和控制器](/zh-TW/docs/Learn/Server-side/Express_Nodejs/routes) + - : 在本教學中,我們將為 LocalLibrary 網站中的所有資源終端設定“虛擬”處理函數的路由(URL 處理代碼)。 完成後,我們將為我們的路由處理程式提供模組化結構,以便我們可以在後續的教學中擴展真正的處理函數。 我們也將了解如何使用 Express 創建模組化路由。 +- [Express 教學(5): 顯示圖書館的資料](/zh-TW/docs/Learn/Server-side/Express_Nodejs/Displaying_data) + - : 現在已經準備好新增頁面來展示館藏和其他資料了。這些頁面包括一個展示我們有多少種 model 型態的首頁、所有 models 的列表和詳細資料頁面。透過本教學你可以得到從資料庫取得紀錄和使用模板的實務經驗。 +- [Express 教學(6): 使用表單](/zh-TW/docs/Learn/Server-side/Express_Nodejs/forms) + - : 本教學中展示如何使用 Express 的插件-Pug 來使用 HTML Forms,以及如何編寫表單來創造、更新和刪除資料庫的文件。 +- [Express 教學(7): 網站佈署](/zh-TW/docs/Learn/Server-side/Express_Nodejs/deployment) + - : 現在你完成了很棒的*LocalLibrary* 網站,你希望圖書館的員工和會員可以透過網路讀取它。本教學概略說明如何找到主機來佈署你的網站以及為了使你的網站正式上線所需做的準備。 -
-
Express/Node 介紹
-
第一篇的系列文章中回答了「什麼是Node」和「什麼是Express?」並概略的說明為什麼Express web框架如此特別。此文章將重點放在主要的功能上,並展示一些Express應用常見的建構模塊(儘管此時你還沒有可供測試的開發環境)
-
設定 Node (Express) 開發環境
-
現在你已經了解Express的目的了,接下來繼續說明如何設定和測試 Windows、Linux (Ubuntu)和Mac OS X上的Node/Express開發環境。不管你用的是什麼作業系統,你都能在本文中找到開發Express應用的入門需知。
-
Express 教學(1): The Local Library website
-
在第一篇實務教學系列文章中將說明你將會學到什麼?以及提供範例網站local library的概覽,我們將在後續的文章中繼續改進它。
-
Express 教學(2): 建構網站骨架
-
本文章展示如何建構網站的骨架,接著你可以自己添加路由、模板/畫面和資料庫。
-
Express 教學(3): 使用資料庫(以Mongoose為例)
-
本文簡短的介紹Node/Express如何使用資料庫。接下來展示LocalLibray網站如何透過Mongoose進行資料庫的存取。說明物件綱要(object schema)和模型(models)如何宣告、the main field types和基本驗證。同時簡單的展示幾個讀取資料的主要方法。
-
Express 教學(4): 路由和控制器
-
在本教學中,我們將為LocalLibrary網站中的所有資源終端設定“虛擬”處理函數的路由(URL處理代碼)。 完成後,我們將為我們的路由處理程式提供模組化結構,以便我們可以在後續的教學中擴展真正的處理函數。 我們也將了解如何使用Express創建模組化路由。
-
Express 教學(5): 顯示圖書館的資料
-
現在已經準備好新增頁面來展示館藏和其他資料了。這些頁面包括一個展示我們有多少種model 型態的首頁、所有models的列表和詳細資料頁面。透過本教學你可以得到從資料庫取得紀錄和使用模板的實務經驗。
-
Express 教學(6): 使用表單
-
本教學中展示如何使用Express的插件-Pug來使用HTML Forms,以及如何編寫表單來創造、更新和刪除資料庫的文件。
-
Express 教學(7): 網站佈署
-
現在你完成了很棒的LocalLibrary 網站,你希望圖書館的員工和會員可以透過網路讀取它。本教學概略說明如何找到主機來佈署你的網站以及為了使你的網站正式上線所需做的準備。
-
+## 或許你也想看 -

或許你也想看

+- [在 PWS/Cloud Foundry 上安裝 LocalLibrary](/zh-TW/docs/Learn/Server-side/Express_Nodejs/Installing_on_PWS_Cloud_Foundry) + - : 本文展示如何在[Pivotal Web Services PaaS cloud](http://run.pivotal.io)上安裝*LocalLibrary* ,PWS/Cloud Foundry 是一個完整且開源的 Heroku 替代品,可使用於教學(7)。如果你正在尋找 Heroku 或其他 PaaS 的替代品或只是想玩點不同的東西,那 PWS/Cloud Foundry 絕對值得一試。 -
-
在 PWS/Cloud Foundry上安裝LocalLibrary
-
本文展示如何在Pivotal Web Services PaaS cloud上安裝LocalLibrary ,PWS/Cloud Foundry是一個完整且開源的Heroku替代品,可使用於教學(7)。如果你正在尋找Heroku或其他PaaS的替代品或只是想玩點不同的東西,那PWS/Cloud Foundry絕對值得一試。
-
+## 新增其他教學 -

新增其他教學

+現在已經有了很多教學,但你可能會想寫其他有趣主題的模塊,包括: -
-

現在已經有了很多教學,但你可能會想寫其他有趣主題的模塊,包括:

+- Using sessions +- 使用者認證 +- 使用者授權與權限 +- 測試 Express web 應用 +- Express web 應用之安全性 -
    -
  • Using sessions
  • -
  • 使用者認證
  • -
  • 使用者授權與權限
  • -
  • 測試Express web應用
  • -
  • Express web 應用之安全性
  • -
- -

當然,如果能作個評估模塊就更好了!

-
+當然,如果能作個評估模塊就更好了! diff --git a/files/zh-tw/learn/server-side/express_nodejs/introduction/index.md b/files/zh-tw/learn/server-side/express_nodejs/introduction/index.md index c876e28abe4452..370c57a5847b98 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/introduction/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/introduction/index.md @@ -10,47 +10,53 @@ tags: - 學習 translation_of: Learn/Server-side/Express_Nodejs/Introduction --- -
-

{{LearnSidebar}}

+{{LearnSidebar}} -

{{NextMenu("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs")}}

-
+{{NextMenu("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs")}} -

在這篇文章中回答了「什麼是Node?」和「什麼是Express」,同時概述是什麼讓Express框架如此特別。本文將概述主要特性、展示一些Express應用的主要建構模塊(雖然此時你還沒有能測試它的開發環境)

+在這篇文章中回答了「什麼是 Node?」和「什麼是 Express」,同時概述是什麼讓 Express 框架如此特別。本文將概述主要特性、展示一些 Express 應用的主要建構模塊(雖然此時你還沒有能測試它的開發環境) - - - - - - - - - - + + + + + + + + + +
前置需求:基本的電腦知識。 對伺服器端網站程式設計的基本了解,特別是網站中客戶端 - 伺服器交互的機制
目標:提升對Express的了解、如何與Node搭配使用、提供的功能和Express應用的主要建構模塊。
前置需求: + 基本的電腦知識。 對伺服器端網站程式設計的基本了解,特別是網站中客戶端 - 伺服器交互的機制。 +
目標: + 提升對Express的了解、如何與Node搭配使用、提供的功能和Express應用的主要建構模塊。 +
-

什麼是Express和Node?

+## 什麼是 Express 和 Node? -

Node (或者說Node.js) 是一個開源、跨平台和允許開發者使用Javascript創造伺服器端工具和應用的執行環境。運行的目的是為了能在瀏覽器外使用,例如:直接執行在電腦或伺服器上。所以該環境捨棄了瀏覽器限定的JavaScript APIs並增加更多傳統OS APIs的支援,例如:HTTP和檔案系統的程式庫。

+[Node](https://nodejs.org/) (或者說*Node.js*) 是一個開源、跨平台和允許開發者使用[Javascript](/en-US/docs/Glossary/JavaScript)創造伺服器端工具和應用的執行環境。運行的目的是為了能在瀏覽器外使用,例如:直接執行在電腦或伺服器上。所以該環境捨棄了瀏覽器限定的 JavaScript APIs 並增加更多傳統 OS APIs 的支援,例如:HTTP 和檔案系統的程式庫。 -

從網站伺服器開發的觀點來看Node有幾項優點:

+從網站伺服器開發的觀點來看 Node 有幾項優點: -
    -
  • 高效能!Node 旨在提升生產率和網頁應用的可擴充性。而且它非常適合網站開發常見的問題,例如:即時網站應用
  • -
  • 使用舊版本的JavaScript進行程式編寫,這表示不用多花力氣在轉換瀏覽器和伺服器上的程式碼
  • -
  • 與其他傳統的Web伺服器語言(例如Python,PHP等)相比,JavaScript是一種相對新的程式語言,它受益於語言設計的改進。許多其他新的和流行的語言都可以編譯/轉換成JavaScript,因此你還可以使用CoffeeScript, ClojureScript,Scala,LiveScript等
  • -
  • Node Package Manager(NPM) 提供數十萬個第三方套件,是最佳的依賴解決方案也可以用來自動化大部分構建工具鏈。
  • -
  • 它是可移植的,能夠在Windows, OS x, Linux, Solaris, FreeBSD, OpenBSD, WebOS和NonStop OS上執行。許多web主機提供方也支援使用Node,通常會提供特定的基礎設施和文件
  • -
  • 擁有非常活耀的第三方生態系統和開發者社群,許多人樂意提供幫助
  • -
+- 高效能!Node 旨在提升生產率和網頁應用的可擴充性。而且它非常適合網站開發常見的問題,例如:即時網站應用 +- 使用舊版本的 JavaScript 進行程式編寫,這表示不用多花力氣在轉換瀏覽器和伺服器上的程式碼 +- 與其他傳統的 Web 伺服器語言(例如 Python,PHP 等)相比,JavaScript 是一種相對新的程式語言,它受益於語言設計的改進。許多其他新的和流行的語言都可以編譯/轉換成 JavaScript,因此你還可以使用 CoffeeScript, ClojureScript,Scala,LiveScript 等 +- Node Package Manager(NPM) 提供數十萬個第三方套件,是最佳的依賴解決方案也可以用來自動化大部分構建工具鏈。 +- 它是可移植的,能夠在 Windows, OS x, Linux, Solaris, FreeBSD, OpenBSD, WebOS 和 NonStop OS 上執行。許多 web 主機提供方也支援使用 Node,通常會提供特定的基礎設施和文件 +- 擁有非常活耀的第三方生態系統和開發者社群,許多人樂意提供幫助 -

你可以只用Node的HTTP模組創造一個簡單的web伺服器來回應任何請求,如下所示。此教學不會告訴建議的檔案名稱或如何執行該檔案 ;-)

+你可以只用 Node 的 HTTP 模組創造一個簡單的 web 伺服器來回應任何請求,如下所示。此教學不會告訴建議的檔案名稱或如何執行該檔案 ;-) -

這將創造一個伺服器並會監聽http://127.0.0.1:8000/上任何種類的HTTP請求,當接收到任何請求時回傳一個「Hello World」的純文字回應。

+這將創造一個伺服器並會監聽`http://127.0.0.1:8000/`上任何種類的 HTTP 請求,當接收到任何請求時回傳一個「Hello World」的純文字回應。 -
// 載入 HTTP 模組
+```js
+// 載入 HTTP 模組
 var http = require("http");
 
 // 創建 HTTP 伺服器並監聽8000 port
@@ -64,121 +70,116 @@ http.createServer(function(request, response) {
 }).listen(8000);
 
 // Print URL for accessing server
-console.log('Server running at http://127.0.0.1:8000/');
+console.log('Server running at http://127.0.0.1:8000/'); +``` -

Node並不原生支持其他常見的web開發任務,如果你想為不同的HTTP方法(例如:GET, POST, DELETE等)增加特定的處理、替不同的URL路徑提供靜態檔案、使用樣板或動態性的產生response,你需要自己完成相關的程式或者是避免重新造輪子 - 使用web框架!

+Node 並不原生支持其他常見的 web 開發任務,如果你想為不同的 HTTP 方法(例如:`GET`, `POST`, `DELETE`等)增加特定的處理、替不同的 URL 路徑提供靜態檔案、使用樣板或動態性的產生 response,你需要自己完成相關的程式**或者是**避免重新造輪子 - 使用 web 框架! -

Express 是最受歡迎的Node web框架,還是其他許多流行的Node web框架的底層庫,它提供:

+[Express](https://expressjs.com/) 是最受歡迎的 Node web 框架,還是其他許多流行的[Node web 框架](https://expressjs.com/en/resources/frameworks.html)的底層庫,它提供: -
    -
  • 替不同HTTP Method、不同URL路徑的requests編寫不同的處理方法
  • -
  • 透過整合「畫面」的渲染引擎來達到插入資料到樣板中產生response
  • -
  • 設定常見的web應用設定,例如:連線用的port和產生response的樣板位置
  • -
  • 在request的處理流程中增加額外的「中間層」進行處理
  • -
+- 替不同 HTTP Method、不同 URL 路徑的 requests 編寫不同的處理方法 +- 透過整合「畫面」的渲染引擎來達到插入資料到樣板中產生 response +- 設定常見的 web 應用設定,例如:連線用的 port 和產生 response 的樣板位置 +- 在 request 的處理流程中增加額外的「中間層」進行處理 -

雖然Express本身非常簡單,但開發者們已經創造相容的中間層套件來解決大部份web開發的問題,這些套件能處理cookies, sessions,登入,URL參數,POST資料,安全標頭等等,你能在Express Middleware中找到這些套件的列表(以及其他流行的第三方套件)

+雖然 Express 本身非常簡單,但開發者們已經創造相容的中間層套件來解決大部份 web 開發的問題,這些套件能處理 cookies, sessions,登入,URL 參數,POST 資料,安全標頭等等,你能在[Express Middleware](http://expressjs.com/en/resources/middleware.html)中找到這些套件的列表(以及其他流行的第三方套件) -
-

備註: 這種靈活性是一把雙刃劍。有一些中間層套件能解決大部份的問題或需求,但使用正確的套件有時會是一個問題。也沒有「正確的方法」來創建應用,你在網路上找到的範例也並非都是最佳解或是只有開發上所需要做的一小部份。

-
+> **備註:** 這種靈活性是一把雙刃劍。有一些中間層套件能解決大部份的問題或需求,但使用正確的套件有時會是一個問題。也沒有「正確的方法」來創建應用,你在網路上找到的範例也並非都是最佳解或是只有開發上所需要做的一小部份。 -

歷史

+## 歷史 -

2009年Node在Linux平台上初次發佈. 2010年NPM套件管利器發佈, 2012年增加Windows的原生支援. 現在的LTS版本為Node v8.11.2,最新版本為Node v10.1.0。這只是它深厚歷史的一小片斷,欲知更多詳情請洽 Wikipedia

+2009 年 Node 在 Linux 平台上初次發佈. 2010 年 NPM 套件管利器發佈, 2012 年增加 Windows 的原生支援. 現在的 LTS 版本為 Node v8.11.2,最新版本為 Node v10.1.0。這只是它深厚歷史的一小片斷,欲知更多詳情請洽 [Wikipedia](https://en.wikipedia.org/wiki/Node.js#History)。 -

2010年11月Express初次發佈,現在的API版本為 4.16。你可以查閱更新紀錄來了解此版本做了甚麼更改或是從GitHub中了解詳細的歷史紀錄。

+2010 年 11 月 Express 初次發佈,現在的 API 版本為 4.16。你可以查閱[更新紀錄](https://expressjs.com/en/changelog/4x.html)來了解此版本做了甚麼更改或是從[GitHub](https://github.com/expressjs/express/blob/master/History.md)中了解詳細的歷史紀錄。 -

Node/Express有多流行?

+## Node/Express 有多流行? -

對於web 框架而言流行度很重要,這代表他會不會被繼續更新、文件、附加套件和技術支援方面有多少資源

+對於 web 框架而言流行度很重要,這代表他會不會被繼續更新、文件、附加套件和技術支援方面有多少資源 -

現在沒有一個明確的指標來評斷伺服器端框架的流行度,雖然有 Hot Frameworks透過計算GitHub的專案數量和StackOverflow的問題來衡量流行度。更好的問題是,Node和Express是否「夠流行」以避免成為不流行的平台。有沒有持續進步?需要時是否能得到幫助?能不能找到Express相關的工作?

+現在沒有一個明確的指標來評斷伺服器端框架的流行度,雖然有 [Hot Frameworks](http://hotframeworks.com/)透過計算 GitHub 的專案數量和 StackOverflow 的問題來衡量流行度。更好的問題是,Node 和 Express 是否「夠流行」以避免成為不流行的平台。有沒有持續進步?需要時是否能得到幫助?能不能找到 Express 相關的工作? -

從眾多使用Express的公司、貢獻程式碼的人數和那些提供免費/收費支援的人員來看,是的!Express是一個流行的框架。

+從眾多使用 Express 的[公司](https://expressjs.com/en/resources/companies-using-express.html)、貢獻程式碼的人數和那些提供免費/收費支援的人員來看,是的!Express 是一個流行的框架。 -

Is Express opinionated?

+## Is Express opinionated? -

Web 框架通常自稱為 "opinionated" 或 "unopinionated".

+Web 框架通常自稱為 "opinionated" 或 "unopinionated". -

Opinionated指的是那些有「正確」方法解決特定問題的框架。在特定的需求上他們通常能快速開發,因為正確的方法通常易懂且有良好的文件,然而在面對其他問題時則會失去靈活性。這類型的框架通常傾向於提供較少的選擇和套件來解決問題。

+Opinionated 指的是那些有「正確」方法解決特定問題的框架。在特定的需求上他們通常能快速開發,因為正確的方法通常易懂且有良好的文件,然而在面對其他問題時則會失去靈活性。這類型的框架通常傾向於提供較少的選擇和套件來解決問題。 -

反過來說Unopinionated 框架,對於如何組合套件來解決問題尚有較少的限制,開發者可以更輕易的使用適當的套件來解決特定問題,儘管代價是你需要自己找到適合的套件。

+反過來說 Unopinionated 框架,對於如何組合套件來解決問題尚有較少的限制,開發者可以更輕易的使用適當的套件來解決特定問題,儘管代價是你需要自己找到適合的套件。 -

Express是Unopinionated 框架,你可以在request處理流程中使用任何相容套件,使用單一或複數個檔案來建構應用,有時候甚至會覺得擁有太多選擇了。

+Express 是 Unopinionated 框架,你可以在 request 處理流程中使用任何相容套件,使用單一或複數個檔案來建構應用,有時候甚至會覺得擁有太多選擇了。 -

Express的程式碼長怎樣?

+## Express 的程式碼長怎樣? -

傳統的資料驅動網站中,web應用程式會等待來自瀏覽器(或其他客戶端)的HTTP Request,接收到Request後根據URL和可能夾帶的POST/GET資料來決定需要回應什麼動作,根據需要可能對資料庫進行讀寫或執行滿足Request所需的其他任務。web應用程式會回應Response給瀏覽器,通常是藉由插入檢所到的資料到HTML 模板中動態產生HTML頁面讓瀏覽器顯示。

+傳統的資料驅動網站中,web 應用程式會等待來自瀏覽器(或其他客戶端)的 HTTP Request,接收到 Request 後根據 URL 和可能夾帶的`POST`/`GET`資料來決定需要回應什麼動作,根據需要可能對資料庫進行讀寫或執行滿足 Request 所需的其他任務。web 應用程式會回應 Response 給瀏覽器,通常是藉由插入檢所到的資料到 HTML 模板中動態產生 HTML 頁面讓瀏覽器顯示。 +Express provides methods to specify what function is called for a particular HTTP verb (`GET`, `POST`, `SET`, etc.) and URL pattern ("Route"), and methods to specify what template ("view") engine is used, where template files are located, and what template to use to render a response. You can use Express middleware to add support for cookies, sessions, and users, getting `POST`/`GET` parameters, etc. You can use any database mechanism supported by Node (Express does not define any database-related behaviour). +The following sections explain some of the common things you'll see when working with _Express_ and _Node_ code. -

Express provides methods to specify what function is called for a particular HTTP verb (GET, POST, SET, etc.) and URL pattern ("Route"), and methods to specify what template ("view") engine is used, where template files are located, and what template to use to render a response. You can use Express middleware to add support for cookies, sessions, and users, getting POST/GET parameters, etc. You can use any database mechanism supported by Node (Express does not define any database-related behaviour).

+### Helloworld Express -

The following sections explain some of the common things you'll see when working with Express and Node code.

+First lets consider the standard Express [Hello World](https://expressjs.com/en/starter/hello-world.html) example (we discuss each part of this below, and in the following sections). -

Helloworld Express

+> **備註:** If you have Node and Express already installed (or if you install them as shown in the [next article](/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment)), you can save this code in a text file called **app.js** and run it in a bash command prompt by calling: +> +> **`./node ./app.js`** -

First lets consider the standard Express Hello World example (we discuss each part of this below, and in the following sections).

- -
-

備註: If you have Node and Express already installed (or if you install them as shown in the next article), you can save this code in a text file called app.js and run it in a bash command prompt by calling:

- -

./node ./app.js

-
- -
var express = require('express');
+```js
+var express = require('express');
 var app = express();
 
-app.get('/', function(req, res) {
+app.get('/', function(req, res) {
   res.send('Hello World!');
-});
+});
 
 app.listen(3000, function() {
   console.log('Example app listening on port 3000!');
 });
-
+``` -

The first two lines require() (import) the express module and create an Express application. This object, which is traditionally named app, has methods for routing HTTP requests, configuring middleware, rendering HTML views, registering a template engine, and modifying application settings that control how the application behaves (e.g. the environment mode, whether route definitions are case sensitive, etc.)

+The first two lines `require()` (import) the express module and create an [Express application](https://expressjs.com/en/4x/api.html#app). This object, which is traditionally named `app`, has methods for routing HTTP requests, configuring middleware, rendering HTML views, registering a template engine, and modifying [application settings](https://expressjs.com/en/4x/api.html#app.settings.table) that control how the application behaves (e.g. the environment mode, whether route definitions are case sensitive, etc.) -

The middle part of the code (the three lines starting with app.get) shows a route definition. The app.get() method specifies a callback function that will be invoked whenever there is an HTTP GET request with a path ('/') relative to the site root. The callback function takes a request and a response object as arguments, and simply calls send() on the response to return the string "Hello World!"

+The middle part of the code (the three lines starting with `app.get`) shows a _route definition_. The `app.get()` method specifies a callback function that will be invoked whenever there is an HTTP `GET` request with a path (`'/'`) relative to the site root. The callback function takes a request and a response object as arguments, and simply calls [`send()`](https://expressjs.com/en/4x/api.html#res.send) on the response to return the string "Hello World!" -

The final block starts up the server on port '3000' and prints a log comment to the console. With the server running, you could go to localhost:3000 in your browser to see the example response returned.

+The final block starts up the server on port '3000' and prints a log comment to the console. With the server running, you could go to `localhost:3000` in your browser to see the example response returned. -

Importing and creating modules

+### Importing and creating modules -

A module is a JavaScript library/file that you can import into other code using Node's require() function. Express itself is a module, as are the middleware and database libraries that we use in our Express applications.

+A module is a JavaScript library/file that you can import into other code using Node's `require()` function. _Express_ itself is a module, as are the middleware and database libraries that we use in our _Express_ applications. -

The code below shows how we import a module by name, using the Express framework as an example. First we invoke the require() function, specifying the name of the module as a string ('express'), and calling the returned object to create an Express application. We can then access the properties and functions of the application object.

+The code below shows how we import a module by name, using the _Express_ framework as an example. First we invoke the `require()` function, specifying the name of the module as a string (`'express'`), and calling the returned object to create an [Express application](https://expressjs.com/en/4x/api.html#app). We can then access the properties and functions of the application object. -
var express = require('express');
+```js
+var express = require('express');
 var app = express();
-
+``` -

You can also create your own modules that can be imported in the same way.

+You can also create your own modules that can be imported in the same way. -
-

備註: You will want to create your own modules, because this allows you to organise your code into managable parts — a monolithic single-file application is hard to understand and maintain. Using modules also helps you manage your namespace, because only the variables you explicitly export are imported when you use a module.

-
+> **備註:** You will _want_ to create your own modules, because this allows you to organise your code into managable parts — a monolithic single-file application is hard to understand and maintain. Using modules also helps you manage your namespace, because only the variables you explicitly export are imported when you use a module. -

To make objects available outside of a module you just need to assign them to the exports object. For example, the square.js module below is a file that exports area() and perimeter() methods:

+To make objects available outside of a module you just need to assign them to the `exports` object. For example, the **square.js** module below is a file that exports `area()` and `perimeter()` methods: -
exports.area = function(width) { return width * width; };
+```js
+exports.area = function(width) { return width * width; };
 exports.perimeter = function(width) { return 4 * width; };
-
+``` -

We can import this module using require(), and then call the exported method(s) as shown:

+We can import this module using `require()`, and then call the exported method(s) as shown: -
var square = require('./square'); // Here we require() the name of the file without the (optional) .js file extension
-console.log('The area of a square with a width of 4 is ' + square.area(4));
+```js +var square = require('./square'); // Here we require() the name of the file without the (optional) .js file extension +console.log('The area of a square with a width of 4 is ' + square.area(4)); +``` -
-

備註: You can also specify an absolute path to the module (or a name, as we did initially).

-
+> **備註:** You can also specify an absolute path to the module (or a name, as we did initially). -

If you want to export a complete object in one assignment instead of building it one property at a time, assign it to module.exports as shown below (you can also do this to make the root of the exports object a constructor or other function):

+If you want to export a complete object in one assignment instead of building it one property at a time, assign it to `module.exports` as shown below (you can also do this to make the root of the exports object a constructor or other function): -
module.exports = {
+```js
+module.exports = {
   area: function(width) {
     return width * width;
   },
@@ -187,67 +188,67 @@ console.log('The area of a square with a width of 4 is ' + square.area(4));
+``` -

For a lot more information about modules see Modules (Node API docs).

+For a lot more information about modules see [Modules](https://nodejs.org/api/modules.html#modules_modules) (Node API docs). -

Using asynchronous APIs

+### Using asynchronous APIs -

JavaScript code frequently uses asynchronous rather than synchronous APIs for operations that may take some time to complete. A synchronous API is one in which each operation must complete before the next operation can start. For example, the following log functions are synchronous, and will print the text to the console in order (First, Second).

+JavaScript code frequently uses asynchronous rather than synchronous APIs for operations that may take some time to complete. A synchronous API is one in which each operation must complete before the next operation can start. For example, the following log functions are synchronous, and will print the text to the console in order (First, Second). -
console.log('First');
+```js
+console.log('First');
 console.log('Second');
-
+``` -

By contrast, an asynchronous API is one in which the API will start an operation and immediately return (before the operation is complete). Once the operation finishes, the API will use some mechanism to perform additional operations. For example, the code below will print out "Second, First" because even though setTimeout() method is called first, and returns immediately, the operation doesn't complete for several seconds.

+By contrast, an asynchronous API is one in which the API will start an operation and immediately return (before the operation is complete). Once the operation finishes, the API will use some mechanism to perform additional operations. For example, the code below will print out "Second, First" because even though `setTimeout()` method is called first, and returns immediately, the operation doesn't complete for several seconds. -
setTimeout(function() {
+```js
+setTimeout(function() {
    console.log('First');
    }, 3000);
 console.log('Second');
-
+``` -

Using non-blocking asynchronous APIs is even more important on Node than in the browser, because Node is a single threaded event-driven execution environment. "single threaded" means that all requests to the server are run on the same thread (rather than being spawned off into separate processes). This model is extremely efficient in terms of speed and server resources, but it does mean that if any of your functions call synchronous methods that take a long time to complete, they will block not just the current request, but every other request being handled by your web application.

+Using non-blocking asynchronous APIs is even more important on Node than in the browser, because _Node_ is a single threaded event-driven execution environment. "single threaded" means that all requests to the server are run on the same thread (rather than being spawned off into separate processes). This model is extremely efficient in terms of speed and server resources, but it does mean that if any of your functions call synchronous methods that take a long time to complete, they will block not just the current request, but every other request being handled by your web application. -

There are a number of ways for an asynchronous API to notify your application that it has completed. The most common way is to register a callback function when you invoke the asynchronous API, that will be called back when the operation completes. This is the approach used above.

+There are a number of ways for an asynchronous API to notify your application that it has completed. The most common way is to register a callback function when you invoke the asynchronous API, that will be called back when the operation completes. This is the approach used above. -
-

備註: Using callbacks can be quite "messy" if you have a sequence of dependent asynchronous operations that must be performed in order, because this results in multiple levels of nested callbacks. This problem is commonly known as "callback hell". This problem can be reduced by good coding practices (see http://callbackhell.com/), using a module like async, or even moving to ES6 features like Promises.

-
+> **備註:** Using callbacks can be quite "messy" if you have a sequence of dependent asynchronous operations that must be performed in order, because this results in multiple levels of nested callbacks. This problem is commonly known as "callback hell". This problem can be reduced by good coding practices (see ), using a module like [async](https://www.npmjs.com/package/async), or even moving to ES6 features like [Promises](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). -
-

備註: A common convention for Node and Express is to use error-first callbacks. In this convention the first value in your callback functions is an error value, while subsequent arguments contain success data. There is a good explanation of why this approach is useful in this blog: The Node.js Way - Understanding Error-First Callbacks (fredkschott.com).

-
+> **備註:** A common convention for Node and Express is to use error-first callbacks. In this convention the first value in your _callback functions_ is an error value, while subsequent arguments contain success data. There is a good explanation of why this approach is useful in this blog: [The Node.js Way - Understanding Error-First Callbacks](http://fredkschott.com/post/2014/03/understanding-error-first-callbacks-in-node-js) (fredkschott.com). -

Creating route handlers

+### Creating route handlers -

In our Hello World Express example (see above), we defined a (callback) route handler function for HTTP GET requests to the site root ('/').

+In our _Hello World_ Express example (see above), we defined a (callback) route handler function for HTTP `GET` requests to the site root (`'/'`). -
app.get('/', function(req, res) {
+```js
+app.get('/', function(req, res) {
   res.send('Hello World!');
 });
-
+``` -

The callback function takes a request and a response object as arguments. In this case the method simply calls send() on the response to return the string "Hello World!" There are a number of other response methods for ending the request/response cycle, for example you could call res.json() to send a JSON response or res.sendFile() to send a file.

+The callback function takes a request and a response object as arguments. In this case the method simply calls [`send()`](https://expressjs.com/en/4x/api.html#res.send) on the response to return the string "Hello World!" There are a [number of other response methods](https://expressjs.com/en/guide/routing.html#response-methods) for ending the request/response cycle, for example you could call [`res.json()`](https://expressjs.com/en/4x/api.html#res.json) to send a JSON response or [`res.sendFile()`](https://expressjs.com/en/4x/api.html#res.sendFile) to send a file. -
-

備註: You can use any argument names you like in the callback functions; when the callback is invoked the first argument will always be the request and the second will always be the response. It makes sense to name them such that you can identify the object you're working with in the body of the callback.

-
+> **備註:** You can use any argument names you like in the callback functions; when the callback is invoked the first argument will always be the request and the second will always be the response. It makes sense to name them such that you can identify the object you're working with in the body of the callback. -

The Express application object also provides methods to define route handlers for all the other HTTP verbs, which are mostly used in exactly the same way: post(), put(), delete(), options(), trace(), copy(), lock(), mkcol(), move(), purge(), propfind(), proppatch(), unlock(), report(), mkactivity(), checkout(), merge(), m-search(), notify(), subscribe(), unsubscribe(), patch(), search(), and connect().

+The _Express application_ object also provides methods to define route handlers for all the other HTTP verbs, which are mostly used in exactly the same way: `post()`, `put()`, `delete()`, `options()`, `trace()`, `copy()`, `lock()`, `mkcol()`, `move()`, `purge()`, `propfind()`, `proppatch()`, `unlock()`, `report()`, `mkactivity()`, `checkout()`, `merge()`, ` m-``search() `, `notify()`, `subscribe()`, `unsubscribe()`, `patch()`, `search()`, and `connect()`. -

There is a special routing method, app.all(), which will be called in response to any HTTP method. This is used for loading middleware functions at a particular path for all request methods. The following example (from the Express documentation) shows a handler that will be executed for requests to /secret irrespective of the HTTP verb used (provided it is supported by the http module).

+There is a special routing method, `app.all()`, which will be called in response to any HTTP method. This is used for loading middleware functions at a particular path for all request methods. The following example (from the Express documentation) shows a handler that will be executed for requests to `/secret` irrespective of the HTTP verb used (provided it is supported by the [http module](https://nodejs.org/api/http.html#http_http_methods)). -
app.all('/secret', function(req, res, next) {
+```js
+app.all('/secret', function(req, res, next) {
   console.log('Accessing the secret section ...');
   next(); // pass control to the next handler
-});
+}); +``` -

Routes allow you to match particular patterns of characters in a URL, and extract some values from the URL and pass them as parameters to the route handler (as attributes of the request object passed as a parameter).

+Routes allow you to match particular patterns of characters in a URL, and extract some values from the URL and pass them as parameters to the route handler (as attributes of the request object passed as a parameter). -

Often it is useful to group route handlers for a particular part of a site together and access them using a common route-prefix (e.g. a site with a Wiki might have all wiki-related routes in one file and have them accessed with a route prefix of /wiki/). In Express this is achieved by using the express.Router object. For example, we can create our wiki route in a module named wiki.js, and then export the Router object, as shown below:

+Often it is useful to group route handlers for a particular part of a site together and access them using a common route-prefix (e.g. a site with a Wiki might have all wiki-related routes in one file and have them accessed with a route prefix of _/wiki/_). In _Express_ this is achieved by using the [`express.Router`](http://expressjs.com/en/guide/routing.html#express-router) object. For example, we can create our wiki route in a module named **wiki.js**, and then export the `Router` object, as shown below: -
// wiki.js - Wiki route module
+```js
+// wiki.js - Wiki route module
 
 var express = require('express');
 var router = express.Router();
@@ -263,58 +264,58 @@ router.get('/about', function(req, res) {
 });
 
 module.exports = router;
-
+``` -
-

備註: Adding routes to the Router object is just like adding routes to the app object (as shown previously).

-
+> **備註:** Adding routes to the `Router` object is just like adding routes to the `app` object (as shown previously). -

To use the router in our main app file we would then require() the route module (wiki.js), then call use() on the Express application to add the Router to the middleware handling path. The two routes will then be accessible from /wiki/ and /wiki/about/.

+To use the router in our main app file we would then `require()` the route module (**wiki.js**), then call `use()` on the _Express_ application to add the Router to the middleware handling path. The two routes will then be accessible from `/wiki/` and `/wiki/about/`. -
var wiki = require('./wiki.js');
+```js
+var wiki = require('./wiki.js');
 // ...
-app.use('/wiki', wiki);
+app.use('/wiki', wiki); +``` -

We'll show you a lot more about working with routes, and in particular about using the Router, later on in the linked section Routes and controllers .

+We'll show you a lot more about working with routes, and in particular about using the `Router`, later on in the linked section[ Routes and controllers .](/en-US/docs/Learn/Server-side/Express_Nodejs/routes) -

Using middleware

+### Using middleware -

Middleware is used extensively in Express apps, for tasks from serving static files to error handling, to compressing HTTP responses. Whereas route functions end the HTTP request-response cycle by returning some response to the HTTP client, middleware functions typically perform some operation on the request or response and then call the next function in the "stack", which might be more middleware or a route handler. The order in which middleware is called is up to the app developer.

+Middleware is used extensively in Express apps, for tasks from serving static files to error handling, to compressing HTTP responses. Whereas route functions end the HTTP request-response cycle by returning some response to the HTTP client, middleware functions _typically_ perform some operation on the request or response and then call the next function in the "stack", which might be more middleware or a route handler. The order in which middleware is called is up to the app developer. -
-

備註: The middleware can perform any operation, execute any code, make changes to the request and response object, and it can also end the request-response cycle. If it does not end the cycle then it must call next() to pass control to the next middleware function (or the request will be left hanging).

-
+> **備註:** The middleware can perform any operation, execute any code, make changes to the request and response object, and it can _also end the request-response cycle_. If it does not end the cycle then it must call `next()` to pass control to the next middleware function (or the request will be left hanging). -

Most apps will use third-party middleware in order to simplify common web development tasks like working with cookies, sessions, user authentication, accessing request POST and JSON data, logging, etc. You can find a list of middleware packages maintained by the Express team (which also includes other popular 3rd party packages). Other Express packages are available on the NPM package manager.

+Most apps will use _third-party_ middleware in order to simplify common web development tasks like working with cookies, sessions, user authentication, accessing request `POST` and JSON data, logging, etc. You can find a [list of middleware packages maintained by the Express team](http://expressjs.com/en/resources/middleware.html) (which also includes other popular 3rd party packages). Other Express packages are available on the NPM package manager. -

To use third party middleware you first need to install it into your app using NPM. For example, to install the morgan HTTP request logger middleware, you'd do this:

+To use third party middleware you first need to install it into your app using NPM. For example, to install the [morgan](http://expressjs.com/en/resources/middleware/morgan.html) HTTP request logger middleware, you'd do this: -
$ npm install morgan
-
+```bash +$ npm install morgan +``` -

You could then call use() on the Express application object to add the middleware to the stack:

+You could then call `use()` on the _Express application object_ to add the middleware to the stack: -
var express = require('express');
-var logger = require('morgan');
+```js
+var express = require('express');
+var logger = require('morgan');
 var app = express();
-app.use(logger('dev'));
-...
+app.use(logger('dev')); +... +``` -
-

備註: Middleware and routing functions are called in the order that they are declared. For some middleware the order is important (for example if session middleware depends on cookie middleware, then the cookie handler must be added first). It is almost always the case that middleware is called before setting routes, or your route handlers will not have access to functionality added by your middleware.

-
+> **備註:** Middleware and routing functions are called in the order that they are declared. For some middleware the order is important (for example if session middleware depends on cookie middleware, then the cookie handler must be added first). It is almost always the case that middleware is called before setting routes, or your route handlers will not have access to functionality added by your middleware. -

You can write your own middleware functions, and you are likely to have to do so (if only to create error handling code). The only difference between a middleware function and a route handler callback is that middleware functions have a third argument next, which middleware functions are expected to call if they are not that which completes the request cycle (when the middleware function is called, this contains the next function that must be called).

+You can write your own middleware functions, and you are likely to have to do so (if only to create error handling code). The **only** difference between a middleware function and a route handler callback is that middleware functions have a third argument `next`, which middleware functions are expected to call if they are not that which completes the request cycle (when the middleware function is called, this contains the _next_ function that must be called). -

You can add a middleware function to the processing chain with either app.use() or app.add(), depending on whether you want to apply the middleware to all responses or to responses with a particular HTTP verb (GET, POST, etc). You specify routes the same in both cases, though the route is optional when calling app.use().

+You can add a middleware function to the processing chain with either `app.use()` or `app.add()`, depending on whether you want to apply the middleware to all responses or to responses with a particular HTTP verb (`GET`, `POST`, etc). You specify routes the same in both cases, though the route is optional when calling **app.use()**. -

The example below shows how you can add the middleware function using both methods, and with/without a route.

+The example below shows how you can add the middleware function using both methods, and with/without a route. -
var express = require('express');
+```js
+var express = require('express');
 var app = express();
 
 // An example middleware function
-var a_middleware_function = function(req, res, next) {
+var a_middleware_function = function(req, res, next) {
   // ... perform some operations
   next(); // Call next() so Express will call the next middleware function in the chain.
 }
@@ -328,85 +329,88 @@ app.use('/someroute', a_middleware_function);
 // A middleware function added for a specific HTTP verb and route
 app.get('/', a_middleware_function);
 
-app.listen(3000);
+app.listen(3000); +``` -
-

備註: Above we declare the middleware function separately and then set it as the callback. In our previous route handler function we declared the callback function when it was used. In JavaScript, either approach is valid.

-
+> **備註:** Above we declare the middleware function separately and then set it as the callback. In our previous route handler function we declared the callback function when it was used. In JavaScript, either approach is valid. -

The Express documentation has a lot more excellent documentation about using and writing Express middleware.

+The Express documentation has a lot more excellent documentation about [using](https://expressjs.com/en/guide/using-middleware.html) and [writing](http://expressjs.com/en/guide/writing-middleware.html) Express middleware. -

Serving static files

+### Serving static files -

You can use the express.static middleware to serve static files, including your images, CSS and JavaScript (static() is the only middleware function that is actually part of Express). For example, you would use the line below to serve images, CSS files, and JavaScript files from a directory named 'public' at the same level as where you call node:

+You can use the [express.static](http://expressjs.com/en/4x/api.html#express.static) middleware to serve static files, including your images, CSS and JavaScript (`static()` is the only middleware function that is actually **part** of _Express_). For example, you would use the line below to serve images, CSS files, and JavaScript files from a directory named '**public'** at the same level as where you call node: -
app.use(express.static('public'));
-
+```js +app.use(express.static('public')); +``` -

Any files in the public directory are served by adding their filename (relative to the base "public" directory) to the base URL. So for example:

+Any files in the public directory are served by adding their filename (_relative_ to the base "public" directory) to the base URL. So for example: -
http://localhost:3000/images/dog.jpg
+```plain
+http://localhost:3000/images/dog.jpg
 http://localhost:3000/css/style.css
 http://localhost:3000/js/app.js
 http://localhost:3000/about.html
-
+``` -

You can call static() multiple times to serve multiple directories. If a file cannot be found by one middleware function then it will simply be passed on to the subsequent middleware (the order that middleware is called is based on your declaration order).

+You can call `static()` multiple times to serve multiple directories. If a file cannot be found by one middleware function then it will simply be passed on to the subsequent middleware (the order that middleware is called is based on your declaration order). -
app.use(express.static('public'));
+```js
+app.use(express.static('public'));
 app.use(express.static('media'));
-
+``` -

You can also create a virtual prefix for your static URLs, rather than having the files added to the base URL. For example, here we specify a mount path so that the files are loaded with the prefix "/media":

+You can also create a virtual prefix for your static URLs, rather than having the files added to the base URL. For example, here we [specify a mount path](http://expressjs.com/en/4x/api.html#app.use) so that the files are loaded with the prefix "/media": -
app.use('/media', express.static('public'));
-
+```js +app.use('/media', express.static('public')); +``` -

Now, you can load the files that are in the public directory from the /media path prefix.

+Now, you can load the files that are in the `public` directory from the `/media` path prefix. -
http://localhost:3000/media/images/dog.jpg
+```plain
+http://localhost:3000/media/images/dog.jpg
 http://localhost:3000/media/video/cat.mp4
-http://localhost:3000/media/cry.mp3
-
+http://localhost:3000/media/cry.mp3 +``` -

For more information, see Serving static files in Express.

+For more information, see [Serving static files in Express](). -

Handling errors

+### Handling errors -

Errors are handled by one or more special middleware functions that have four arguments, instead of the usual three: (err, req, res, next). For example:

+Errors are handled by one or more special middleware functions that have four arguments, instead of the usual three: `(err, req, res, next)`. For example: -
app.use(function(err, req, res, next) {
+```js
+app.use(function(err, req, res, next) {
   console.error(err.stack);
   res.status(500).send('Something broke!');
 });
-
+``` -

These can return any content required, but must be called after all other app.use() and routes calls so that they are the last middleware in the request handling process!

+These can return any content required, but must be called after all other `app.use()` and routes calls so that they are the last middleware in the request handling process! -

Express comes with a built-in error handler, which takes care of any remaining errors that might be encountered in the app. This default error-handling middleware function is added at the end of the middleware function stack. If you pass an error to next() and you do not handle it in an error handler, it will be handled by the built-in error handler; the error will be written to the client with the stack trace.

+Express comes with a built-in error handler, which takes care of any remaining errors that might be encountered in the app. This default error-handling middleware function is added at the end of the middleware function stack. If you pass an error to `next()` and you do not handle it in an error handler, it will be handled by the built-in error handler; the error will be written to the client with the stack trace. -
-

Note: The stack trace is not included in the production environment. To run it in production mode you need to set the the environment variable NODE_ENV to 'production'.

-
+> **備註:** The stack trace is not included in the production environment. To run it in production mode you need to set the the environment variable `NODE_ENV` to '`production'`. -
-

Note: HTTP404 and other "error" status codes are not treated as errors. If you want to handle these, you can add a middleware function to do so. For more information see the FAQ.

-
+> **備註:** HTTP404 and other "error" status codes are not treated as errors. If you want to handle these, you can add a middleware function to do so. For more information see the [FAQ](http://expressjs.com/en/starter/faq.html#how-do-i-handle-404-responses). -

For more information see Error handling (Express docs).

+For more information see [Error handling](http://expressjs.com/en/guide/error-handling.html) (Express docs). -

Using databases

+### Using databases -

Express apps can use any database mechanism supported by Node (Express itself doesn't define any specific additional behaviour/requirements for database management). There are many options, including PostgreSQL, MySQL, Redis, SQLite, MongoDB, etc.

+_Express_ apps can use any database mechanism supported by _Node_ (_Express_ itself doesn't define any specific additional behaviour/requirements for database management). There are many options, including PostgreSQL, MySQL, Redis, SQLite, MongoDB, etc. -

In order to use these you have to first install the database driver using NPM. For example, to install the driver for the popular NoSQL MongoDB you would use the command:

+In order to use these you have to first install the database driver using NPM. For example, to install the driver for the popular NoSQL MongoDB you would use the command: -
$ npm install mongodb
-
+```bash +$ npm install mongodb +``` -

The database itself can be installed locally or on a cloud server. In your Express code you require the driver, connect to the database, and then perform create, read, update, and delete (CRUD) operations. The example below (from the Express documentation) shows how you can find "mammal" records using MongoDB.

+The database itself can be installed locally or on a cloud server. In your Express code you require the driver, connect to the database, and then perform create, read, update, and delete (CRUD) operations. The example below (from the Express documentation) shows how you can find "mammal" records using MongoDB. -
//this works with older versions of  mongodb version ~ 2.2.33
+```js
+//this works with older versions of  mongodb version ~ 2.2.33
 var MongoClient = require('mongodb').MongoClient;
 
 MongoClient.connect('mongodb://localhost:27017/animals', function(err, db) {
@@ -432,25 +436,20 @@ MongoClient.connect('mongodb://localhost:27017/animals', function(err, client){
      client.close();
    });
 }
-
+``` +Another popular approach is to access your database indirectly, via an Object Relational Mapper ("ORM"). In this approach you define your data as "objects" or "models" and the ORM maps these through to the underlying database format. This approach has the benefit that as a developer you can continue to think in terms of JavaScript objects rather than database semantics, and that there is an obvious place to perform validation and checking of incoming data. We'll talk more about databases in a later article. +For more information see [Database integration](https://expressjs.com/en/guide/database-integration.html) (Express docs). +### Rendering data (views) +Template engines (referred to as "view engines" by _Express_) allow you to specify the _structure_ of an output document in a template, using placeholders for data that will be filled in when a page is generated. Templates are often used to create HTML, but can also create other types of documents. Express has support for [a number of template engines](https://github.com/expressjs/express/wiki#template-engines), and there is a useful comparison of the more popular engines here: [Comparing JavaScript Templating Engines: Jade, Mustache, Dust and More](https://strongloop.com/strongblog/compare-javascript-templates-jade-mustache-dust/). +In your application settings code you set the template engine to use and the location where Express should look for templates using the 'views' and 'view engines' settings, as shown below (you will also have to install the package containing your template library too!) - -

Another popular approach is to access your database indirectly, via an Object Relational Mapper ("ORM"). In this approach you define your data as "objects" or "models" and the ORM maps these through to the underlying database format. This approach has the benefit that as a developer you can continue to think in terms of JavaScript objects rather than database semantics, and that there is an obvious place to perform validation and checking of incoming data. We'll talk more about databases in a later article.

- -

For more information see Database integration (Express docs).

- -

Rendering data (views)

- -

Template engines (referred to as "view engines" by Express) allow you to specify the structure of an output document in a template, using placeholders for data that will be filled in when a page is generated. Templates are often used to create HTML, but can also create other types of documents. Express has support for a number of template engines, and there is a useful comparison of the more popular engines here: Comparing JavaScript Templating Engines: Jade, Mustache, Dust and More.

- -

In your application settings code you set the template engine to use and the location where Express should look for templates using the 'views' and 'view engines' settings, as shown below (you will also have to install the package containing your template library too!)

- -
var express = require('express');
+```js
+var express = require('express');
 var app = express();
 
 // Set directory to contain the templates ('views')
@@ -458,59 +457,54 @@ app.set('views', path.join(__dirname, 'views'));
 
 // Set view engine to use, in this case 'some_template_engine_name'
 app.set('view engine', 'some_template_engine_name');
-
+``` -

The appearance of the template will depend on what engine you use. Assuming that you have a template file named "index.<template_extension>" that contains placeholders for data variables named 'title' and "message", you would call Response.render() in a route handler function to create and send the HTML response:

+The appearance of the template will depend on what engine you use. Assuming that you have a template file named "index.\" that contains placeholders for data variables named 'title' and "message", you would call [`Response.render()`](http://expressjs.com/en/4x/api.html#res.render) in a route handler function to create and send the HTML response: -
app.get('/', function(req, res) {
+```js
+app.get('/', function(req, res) {
   res.render('index', { title: 'About dogs', message: 'Dogs rock!' });
-});
- -

For more information see Using template engines with Express (Express docs).

+}); +``` -

File structure

+For more information see [Using template engines with Express](http://expressjs.com/en/guide/using-template-engines.html) (Express docs). -

Express makes no assumptions in terms of structure or what components you use. Routes, views, static files, and other application-specific logic can live in any number of files with any directory structure. While it is perfectly possible to have the whole Express application in one file, typically it makes sense to split your application into files based on function (e.g. account management, blogs, discussion boards) and architectural problem domain (e.g. model, view or controller if you happen to be using an MVC architecture).

+### File structure -

In a later topic we'll use the Express Application Generator, which creates a modular app skeleton that we can easily extend for creating web applications.

+Express makes no assumptions in terms of structure or what components you use. Routes, views, static files, and other application-specific logic can live in any number of files with any directory structure. While it is perfectly possible to have the whole _Express_ application in one file, typically it makes sense to split your application into files based on function (e.g. account management, blogs, discussion boards) and architectural problem domain (e.g. model, view or controller if you happen to be using an [MVC architecture](/en-US/docs/Web/Apps/Fundamentals/Modern_web_app_architecture/MVC_architecture)). -
    -
+In a later topic we'll use the _Express Application Generator_, which creates a modular app skeleton that we can easily extend for creating web applications. -

總結

+## 總結 -

恭喜,您已完成 Express / Node之旅的第一步!您現在應該了解 Express 和 Node 的主要優點,以及 Express 應用程序的主要部分(路由,中間件,錯誤處理和模板代碼)。您還應該明白,Express 是一個不固執己見的框架,您將這些組件組合在一起的方式以及您使用的函式庫,在很大程度上取決於您!

+恭喜,您已完成 Express / Node 之旅的第一步!您現在應該了解 Express 和 Node 的主要優點,以及 Express 應用程序的主要部分(路由,中間件,錯誤處理和模板代碼)。您還應該明白,Express 是一個不固執己見的框架,您將這些組件組合在一起的方式以及您使用的函式庫,在很大程度上取決於您! -

當然,Express是一個非常輕量級的 Web 應用程序框架,它的許多好處和潛力來自第三方函式庫和功能。我們將在以下文章中更詳細地介紹這些內容。在下一篇文章中,我們將介紹如何設置 Node 開發環境,以便您可以開始查看一些 Express 代碼。

+當然,Express 是一個非常輕量級的 Web 應用程序框架,它的許多好處和潛力來自第三方函式庫和功能。我們將在以下文章中更詳細地介紹這些內容。在下一篇文章中,我們將介紹如何設置 Node 開發環境,以便您可以開始查看一些 Express 代碼。 -

See also

+## See also - +- [Venkat.R - Manage Multiple Node versions](https://medium.com/@ramsunvtech/manage-multiple-node-versions-e3245d5ede44) +- [Modules](https://nodejs.org/api/modules.html#modules_modules) (Node API docs) +- [Express](https://expressjs.com/) (home page) +- [Basic routing](http://expressjs.com/en/starter/basic-routing.html) (Express docs) +- [Routing guide](http://expressjs.com/en/guide/routing.html) (Express docs) +- [Using template engines with Express](http://expressjs.com/en/guide/using-template-engines.html) (Express docs) +- [Using middleware](https://expressjs.com/en/guide/using-middleware.html) (Express docs) +- [Writing middleware for use in Express apps](http://expressjs.com/en/guide/writing-middleware.html) (Express docs) +- [Database integration](https://expressjs.com/en/guide/database-integration.html) (Express docs) +- [Serving static files in Express](http://expressjs.com/en/starter/static-files.html) (Express docs) +- [Error handling](http://expressjs.com/en/guide/error-handling.html) (Express docs) -
{{NextMenu("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs")}}
+{{NextMenu("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs")}} -

In this module

+## In this module - +- [Express/Node introduction](/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction) +- [Setting up a Node (Express) development environment](/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment) +- [Express Tutorial: The Local Library website](/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website) +- [Express Tutorial Part 2: Creating a skeleton website](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website) +- [Express Tutorial Part 3: Using a Database (with Mongoose)](/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose) +- [Express Tutorial Part 4: Routes and controllers](/en-US/docs/Learn/Server-side/Express_Nodejs/routes) +- [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms) +- [Express Tutorial Part 7: Deploying to production](/en-US/docs/Learn/Server-side/Express_Nodejs/deployment) diff --git a/files/zh-tw/learn/server-side/express_nodejs/mongoose/index.md b/files/zh-tw/learn/server-side/express_nodejs/mongoose/index.md index a0213f288da205..62247d00fc3140 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/mongoose/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/mongoose/index.md @@ -3,138 +3,124 @@ title: 'Express 教學 3: 使用資料庫 ( Mongoose)' slug: Learn/Server-side/Express_Nodejs/mongoose translation_of: Learn/Server-side/Express_Nodejs/mongoose --- -
{{LearnSidebar}}
+{{LearnSidebar}}{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs")}} -
{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs")}}
- -

本文簡短介紹數據庫,以及如何搭配 Node / Express 應用,使用數據庫。接下來會演示我們如何使用 Mongoose,為本地圖書館提供數據庫存取。本文說明物件要求與模型如何宣告,主要的欄位型態,以及基本驗證。本文也簡短演示一些存取模型數據的主要方法。

+本文簡短介紹數據庫,以及如何搭配 Node / Express 應用,使用數據庫。接下來會演示我們如何使用 [Mongoose](http://mongoosejs.com/),為本地圖書館提供數據庫存取。本文說明物件要求與模型如何宣告,主要的欄位型態,以及基本驗證。本文也簡短演示一些存取模型數據的主要方法。 - - - - - - - - - - + + + + + + + + + +
前置條件:Express 教學 2: 創建一個骨架網站
目標:能夠使用Mongoose設計並創造自己的模型。
前置條件: + Express 教學 2: 創建一個骨架網站 +
目標:能夠使用Mongoose設計並創造自己的模型。
-

概覽

+## 概覽 -

圖書館職員會使用本地圖書館網站,存放書本和借書者訊息。圖書館使用者會用網站瀏覽與尋找書本,看看是否有可以藉閱的書本複本,然後預約或者藉閱。為了有效率地存放與取用訊息,我們將把它存放到數據庫。

+圖書館職員會使用本地圖書館網站,存放書本和借書者訊息。圖書館使用者會用網站瀏覽與尋找書本,看看是否有可以藉閱的書本複本,然後預約或者藉閱。為了有效率地存放與取用訊息,我們將把它存放到數據庫。 -

Express 應用可以使用許多不同的數據庫,並且有好幾種方法可以執行創建 Create、讀取 Read、更新 Update 和刪除 Delete (CRUD) 操作。本教程為一些可用的選項,提供簡短的概覽,然後接著詳細演示該選項的特定運行機制。

+Express 應用可以使用許多不同的數據庫,並且有好幾種方法可以執行創建 **C**reate、讀取 **R**ead、更新 **U**pdate 和刪除 **D**elete (CRUD) 操作。本教程為一些可用的選項,提供簡短的概覽,然後接著詳細演示該選項的特定運行機制。 -

我可以使用什麼數據庫?

+### 我可以使用什麼數據庫? -

Express 應用程序可以使用 Node 支持的任何數據庫(Express 本身不會為數據庫管理,定義任何特定的附加行為/要求)。有許多流行的選項,包括 PostgreSQL,MySQL,Redis,SQLite 和 MongoDB。

+Express 應用程序可以使用 Node 支持的任何數據庫(Express 本身不會為數據庫管理,定義任何特定的附加行為/要求)。有許多[流行的選項](https://expressjs.com/en/guide/database-integration.html),包括 PostgreSQL,MySQL,Redis,SQLite 和 MongoDB。 -

在選擇數據庫時,您應該考慮時間 - 生產力/學習曲線,性能,易複製/備份,成本,社區支持等等。雖然沒有單一的 “最佳” 數據庫,但幾乎任何流行的解決方案,我們的本地圖書館這樣的中小型網站,應該都可以接受。

+在選擇數據庫時,您應該考慮時間 - 生產力/學習曲線,性能,易複製/備份,成本,社區支持等等。雖然沒有單一的 “最佳” 數據庫,但幾乎任何流行的解決方案,我們的本地圖書館這樣的中小型網站,應該都可以接受。 -

有關選項的更多訊息,請參閱:數據庫集成(Express docs)

+有關選項的更多訊息,請參閱:[數據庫集成(Express docs)](https://expressjs.com/en/guide/database-integration.html)。 -

與數據庫互動的最好方式是什麼?

+### 與數據庫互動的最好方式是什麼? -

有兩種與數據庫互動的方法:

+有兩種與數據庫互動的方法: -
    -
  • 使用數據庫的原生查詢語言(例如SQL)
  • -
  • 使用對像數據模型(“ODM”)/對象關係模型(“ORM”)。 ODM / ORM將網站的數據表示為JavaScript對象,然後將其映射到底層數據庫。一些ORM綁定到特定的數據庫,而另一些則提供了一個不特定數據庫的後端。
  • -
+- 使用數據庫的原生查詢語言(例如 SQL) +- 使用對像數據模型(“ODM”)/對象關係模型(“ORM”)。 ODM / ORM 將網站的數據表示為 JavaScript 對象,然後將其映射到底層數據庫。一些 ORM 綁定到特定的數據庫,而另一些則提供了一個不特定數據庫的後端。 -

通過使用 SQL 或數據庫支持的任何查詢語言,都可以獲得最佳性能。 ODM通常比較慢,因為它們使用翻譯代碼,在對象和數據庫格式之間進行映射,這可能不會使用最有效的數據庫查詢(尤其是如果ODM支持不同的數據庫後端,並且必須在各個數據庫所支持的功能方面,做出更大的折衷)。

+通過使用 SQL 或數據庫支持的任何查詢語言,都可以獲得最佳性能。 ODM 通常比較慢,因為它們使用翻譯代碼,在對象和數據庫格式之間進行映射,這可能不會使用最有效的數據庫查詢(尤其是如果 ODM 支持不同的數據庫後端,並且必須在各個數據庫所支持的功能方面,做出更大的折衷)。 -

使用 ORM 的好處是,程序員可以繼續用 JavaScript 對象而不是數據庫語義來思考 — 如果您需要使用不同數據庫(在相同或不同的網站上),那麼尤其如此。他們還提供了一個明顯的地方來執行數據驗證和檢查。

+使用 ORM 的好處是,程序員可以繼續用 JavaScript 對象而不是數據庫語義來思考 — 如果您需要使用不同數據庫(在相同或不同的網站上),那麼尤其如此。他們還提供了一個明顯的地方來執行數據驗證和檢查。 -
-

備註: 使用ODM / ORM通常可以降低開發和維護成本!除非您非常熟悉本地查詢語言,或者性能對您至關重要,否則您應該強烈考慮使用 ODM。

-
+> **備註:** 使用 ODM / ORM 通常可以降低開發和維護成本!除非您非常熟悉本地查詢語言,或者性能對您至關重要,否則您應該強烈考慮使用 ODM。 -

我應該使用哪個 ORM/ODM ?

+### 我應該使用哪個 ORM/ODM ? -

NPM 套件管理器站點上,有許多ODM / ORM 解決方案(查看 odmorm 標籤的子集合!)。

+NPM 套件管理器站點上,有許多 ODM / ORM 解決方案(查看 [odm](https://www.npmjs.com/browse/keyword/odm) 和 [orm](https://www.npmjs.com/browse/keyword/orm) 標籤的子集合!)。 -

在撰寫本文時,受歡迎的幾種解決方案是:

+在撰寫本文時,受歡迎的幾種解決方案是: -
    -
  • Mongoose: Mongoose 是一個 MongoDB對象建模工具,用於在異步環境中工作。
  • -
  • Waterline: 它是從基於Express的 Sails web 框架中提取的 ORM。它提供了一個統一的 API,來訪問眾多不同的數據庫,包括Redis,mySQL,LDAP,MongoDB 和 Postgres。
  • -
  • Bookshelf: 提供基於 promise 和傳統回調的接口,提供事務支持,eager/嵌套 eager 關係加載,多態關聯以及對一對一,一對多和多對多關係的支持。適用於PostgreSQL,MySQL 和 SQLite3。
  • -
  • Objection: 以盡可能簡單的方式,使用 SQL 的全部功能,和底層數據庫引擎(支持SQLite3,Postgres 和 MySQL)。
  • -
  • -

    Sequelize 是 Node.js 和 io.js 基於 promise 的 ORM。它支持以下數據庫方言,PostgreSQL,MySQL,MariaDB,SQLite 和 MSSQL,並具有可靠的事務支持,關係,唯讀複本等功能。

    -
  • -
+- [Mongoose](https://www.npmjs.com/package/mongoose): Mongoose 是一個 [MongoDB](https://www.mongodb.org/)對象建模工具,用於在異步環境中工作。 +- [Waterline](https://www.npmjs.com/package/waterline): 它是從基於 Express 的 Sails web 框架中提取的 ORM。它提供了一個統一的 API,來訪問眾多不同的數據庫,包括 Redis,mySQL,LDAP,MongoDB 和 Postgres。 +- [Bookshelf](https://www.npmjs.com/package/bookshelf): 提供基於 promise 和傳統回調的接口,提供事務支持,eager/嵌套 eager 關係加載,多態關聯以及對一對一,一對多和多對多關係的支持。適用於 PostgreSQL,MySQL 和 SQLite3。 +- [Objection](https://www.npmjs.com/package/objection): 以盡可能簡單的方式,使用 SQL 的全部功能,和底層數據庫引擎(支持 SQLite3,Postgres 和 MySQL)。 +- [Sequelize](https://www.npmjs.com/package/sequelize) 是 Node.js 和 io.js 基於 promise 的 ORM。它支持以下數據庫方言,PostgreSQL,MySQL,MariaDB,SQLite 和 MSSQL,並具有可靠的事務支持,關係,唯讀複本等功能。 -

一般來說,在選擇解決方案時,您應該考慮提供的功能和 “社區活動” (下載,貢獻,錯誤報告,文檔質量等)。在撰寫本文時,Mongoose 是迄今為止最受歡迎的 ODM,如果您將MongoDB 用於你的數據庫,那麼它是一個合理的選擇。

+一般來說,在選擇解決方案時,您應該考慮提供的功能和 “社區活動” (下載,貢獻,錯誤報告,文檔質量等)。在撰寫本文時,Mongoose 是迄今為止最受歡迎的 ODM,如果您將 MongoDB 用於你的數據庫,那麼它是一個合理的選擇。 -

在本地圖書館使用 Mongoose 和 MongoDb

+### 在本地圖書館使用 Mongoose 和 MongoDb -

對於本地圖書館示例(以及本主題的其餘部分),我們將使用 Mongoose ODM 來訪問我們的圖書館數據。 Mongoose 是 MongoDB 的前端,MongoDB 是一個使用面向文檔數據模型的開源 NoSQL 數據庫。在 MongoDB 數據庫中,“文檔” 的 “集合” ,類似於關係數據庫中 “行” 的 “表”。

+對於本地圖書館示例(以及本主題的其餘部分),我們將使用 [Mongoose ODM ](https://www.npmjs.com/package/mongoose)來訪問我們的圖書館數據。 Mongoose 是 [MongoDB](https://www.mongodb.com/what-is-mongodb) 的前端,MongoDB 是一個使用面向文檔數據模型的開源 [NoSQL](https://en.wikipedia.org/wiki/NoSQL) 數據庫。在 MongoDB 數據庫中,“文檔” 的 “集合” ,[類似於](https://docs.mongodb.com/manual/core/databases-and-collections/#collections)關係數據庫中 “行” 的 “表”。 -

這種 ODM 和數據庫的結合在 Node 社區中非常流行,部分原因是文檔存儲和查詢系統,看起來非常像 JSON,因此對 JavaScript 開發人員來說很熟悉。

+這種 ODM 和數據庫的結合在 Node 社區中非常流行,部分原因是文檔存儲和查詢系統,看起來非常像 JSON,因此對 JavaScript 開發人員來說很熟悉。 -
-

備註: 使用 Mongoose 時,您不需要事先了解 MongoDB,但是如果您已經熟悉 MongoDB,Mongoose documentation 文檔的一部分會更易於使用和理解。

-
+> **備註:** 使用 Mongoose 時,您不需要事先了解 MongoDB,但是如果您已經熟悉 MongoDB,[Mongoose documentation ](http://mongoosejs.com/docs/guide.html)文檔的一部分會更易於使用和理解。 -

本教程的其餘部分,將介紹如何為 本地圖書館網站示例,定義和訪問Mongoose 模式和模型。

+本教程的其餘部分,將介紹如何為 本地圖書館網站示例,定義和訪問 Mongoose 模式和模型。 -

設計本地圖書館的模型

+## 設計本地圖書館的模型 -

在您開始編寫模型之前,花幾分鐘的時間思考,我們需要儲存的數據以及不同對象之間的關係。

+在您開始編寫模型之前,花幾分鐘的時間思考,我們需要儲存的數據以及不同對象之間的關係。 -

我們知道,我們需要儲存有關書籍的訊息(標題,摘要,作者,種類,國際標準書號),以及我們可能有多個副本可用(具有全域唯一ID,可用狀態等)。我們可能需要存儲有關作者的更多訊息,而不僅僅是他們的名字,並且可能有多個作者,具有相同或相似的名稱。我們希望能夠根據書名,作者,種類和類別對訊息進行分類。

+我們知道,我們需要儲存有關書籍的訊息(標題,摘要,作者,種類,國際標準書號),以及我們可能有多個副本可用(具有全域唯一 ID,可用狀態等)。我們可能需要存儲有關作者的更多訊息,而不僅僅是他們的名字,並且可能有多個作者,具有相同或相似的名稱。我們希望能夠根據書名,作者,種類和類別對訊息進行分類。 -

在設計模型時,對於每個“對象”(相關訊息組)都有獨立的模型,是有意義的。在這種情況下,明顯的對像是書籍,書籍實例和作者。

+在設計模型時,對於每個“對象”(相關訊息組)都有獨立的模型,是有意義的。在這種情況下,明顯的對像是書籍,書籍實例和作者。 -

您可能還希望,使用模型來表示選擇列表選項(例如,選擇的下拉列表),而不是將選項硬編碼到網站本身— 在無法預先知道所有選項,或者可能更改時,更建議使用模型來表示。很明顯的,書本類型是這種模型的可能人選(例如科幻小說,法國詩歌等)。

+您可能還希望,使用模型來表示選擇列表選項(例如,選擇的下拉列表),而不是將選項硬編碼到網站本身— 在無法預先知道所有選項,或者可能更改時,更建議使用模型來表示。很明顯的,書本類型是這種模型的可能人選(例如科幻小說,法國詩歌等)。 -

一旦我們決定了我們的模型和字段,我們就需要考慮它們之間的關係。

+一旦我們決定了我們的模型和字段,我們就需要考慮它們之間的關係。 -

考慮到這一點,下面的UML關聯圖,顯示了我們在這種情況下定義的模型(一個框對應一個模型)。如上所述,我們創建了以下模型,圖書(本書的通用細節),書本實例(系統中可用圖書的特定實際副本的狀態)和作者。我們還決定建立一個種類模型,以便可以動態創建它的值,而不是將下拉選項硬編碼。我們已經決定不為書本實例:狀態BookInstance:status建立模型—我們將硬編碼可接受的值,因為我們不希望這些值發生變化。在下圖每個框中,您可以看到模型名稱,字段名稱和類型,以及方法及其返回類型。

+考慮到這一點,下面的 UML 關聯圖,顯示了我們在這種情況下定義的模型(一個框對應一個模型)。如上所述,我們創建了以下模型,圖書(本書的通用細節),書本實例(系統中可用圖書的特定實際副本的狀態)和作者。我們還決定建立一個種類模型,以便可以動態創建它的值,而不是將下拉選項硬編碼。我們已經決定不為書本實例:狀態`BookInstance:status`建立模型—我們將硬編碼可接受的值,因為我們不希望這些值發生變化。在下圖每個框中,您可以看到模型名稱,字段名稱和類型,以及方法及其返回類型。 -

下圖還顯示了模型之間的關係,包括它們的多重性。多重性是圖中顯示可能存在於關係中的每個模型的數量(最大值和最小值)的數字。例如,框之間的連接線,顯示書本Book和種類Genre是相關的。靠近書本Book模型的數字,表明一本書必須有零個或多個種類(您想要多少都可以),而種類Genre旁邊一行的數字,表明它可以有零個或多個相關書籍。

+下圖還顯示了模型之間的關係,包括它們的多重性。多重性是圖中顯示可能存在於關係中的每個模型的數量(最大值和最小值)的數字。例如,框之間的連接線,顯示書本`Book`和種類`Genre`是相關的。靠近書本`Book`模型的數字,表明一本書必須有零個或多個種類(您想要多少都可以),而種類`Genre`旁邊一行的數字,表明它可以有零個或多個相關書籍。 -
-

備註: 正如我們在下面的Mongoose入門中所討論的那樣,通常只需要在一個模型中定義文檔/模型之間關係的字段(通過在另一個模型中搜索相關的_id仍然可以找到反向關係)。下面我們選擇在書本綱要(Book schema)中定義Book/Genre和Book/Author之間的關係,以及書本實例綱要(BookInstance Schema)中Book/BookInstance之間的關係。這種選擇有點武斷—我們同樣可以在其他綱要中擁有該字段。

-
+> **備註:** 正如我們在下面的**Mongoose 入門**中所討論的那樣,通常只需要在一個模型中定義文檔/模型之間關係的字段(通過在另一個模型中搜索相關的`_id`仍然可以找到反向關係)。下面我們選擇在書本綱要(Book schema)中定義 Book/Genre 和 Book/Author 之間的關係,以及書本實例綱要(BookInstance Schema)中 Book/BookInstance 之間的關係。這種選擇有點武斷—我們同樣可以在其他綱要中擁有該字段。 -

Mongoose Library Model  with correct cardinality

+![Mongoose Library Model with correct cardinality](library_website_-_mongoose_express.png) -
-

備註: 下一節提供了一個基本的入門知識,解釋如何定義和使用模型。在您閱讀它時,請想想我們將如何構建上圖中的每個模型。

-
+> **備註:** 下一節提供了一個基本的入門知識,解釋如何定義和使用模型。在您閱讀它時,請想想我們將如何構建上圖中的每個模型。 -

Mongoose入門

+## Mongoose 入門 -

本節概述如何將Mongoose 連接到MongoDB 數據庫,如何定義模型綱要和模型,以及如何進行基本查詢。

+本節概述如何將 Mongoose 連接到 MongoDB 數據庫,如何定義模型綱要和模型,以及如何進行基本查詢。 -
-

備註: 本入門受到npm上的Mongoose快速入門Mongoose官方文檔的“深度影響”。

-
+> **備註:** 本入門受到 npm 上的[Mongoose 快速入門](https://www.npmjs.com/package/mongoose)和[Mongoose 官方文檔](http://mongoosejs.com/docs/guide.html)的“深度影響”。 -

安裝Mongoose和MongoDB

+### 安裝 Mongoose 和 MongoDB -

Mongoose像任何其他依賴項一樣,安裝在您的項目(package.json)中—使用NPM。要安裝它,請在項目文件夾中,使用以下命令:

+Mongoose 像任何其他依賴項一樣,安裝在您的項目(**package.json**)中—使用 NPM。要安裝它,請在項目文件夾中,使用以下命令: -
npm install mongoose
-
+```bash +npm install mongoose +``` -

安裝Mongoose會添加所有依賴項,包括MongoDB數據庫驅動程序,但它不會安裝MongoDB 。如果你想安裝一個MongoDB服務器,那麼你可以從這裡下載各種操作系統的安裝程序,並在本地安裝。您還可以使用基於雲端的MongoDB實例。

+安裝 Mongoose 會添加所有依賴項,包括 MongoDB 數據庫驅動程序,但它不會安裝 MongoDB 。如果你想安裝一個 MongoDB 服務器,那麼你可以[從這裡下載](https://www.mongodb.com/download-center)各種操作系統的安裝程序,並在本地安裝。您還可以使用基於雲端的 MongoDB 實例。 -
-

備註: 對於本教程,我們將使用基於mLab雲的數據庫,作為服務沙箱層來提供數據庫。這適用於開發,也對於本教程很有意義,因為它使“安裝”與操作系統無關(數據庫即服務,也是您可能會用於生產環境數據庫的一種方法)。

-
+> **備註:** 對於本教程,我們將使用基於 mLab 雲的數據庫,作為服務[沙箱層](https://mlab.com/plans/pricing/)來提供數據庫。這適用於開發,也對於本教程很有意義,因為它使“安裝”與操作系統無關(數據庫即服務,也是您可能會用於生產環境數據庫的一種方法)。 -

連接到MongoDB

+### 連接到 MongoDB -

Mongoose需要連接到MongoDB數據庫。您可以require()並使用mongoose.connect(),以連接到本地託管的數據庫,如下所示。

+Mongoose 需要連接到 MongoDB 數據庫。您可以`require()`並使用`mongoose.connect()`,以連接到本地託管的數據庫,如下所示。 -
//Import the mongoose module
+```js
+//Import the mongoose module
 var mongoose = require('mongoose');
 
 //Set up default mongoose connection
@@ -146,29 +132,27 @@ mongoose.Promise = global.Promise;
 var db = mongoose.connection;
 
 //Bind connection to error event (to get notification of connection errors)
-db.on('error', console.error.bind(console, 'MongoDB connection error:'));
+db.on('error', console.error.bind(console, 'MongoDB connection error:')); +``` -

您可以使用mongoose.connection獲取默認的Connection對象。一旦連接,在Connection實例上,將觸發打開事件。

+您可以使用`mongoose.connection`獲取默認的`Connection`對象。一旦連接,在`Connection`實例上,將觸發打開事件。 -
-

備註: 如果需要創建其他連接,可以使用mongoose.createConnection()。這與connect()採用相同形式的數據庫URI(包含主機,數據庫,端口,選項等),並返回Connection對象。

-
+> **備註:** 如果需要創建其他連接,可以使用`mongoose.createConnection()`。這與`connect()`採用相同形式的數據庫 URI(包含主機,數據庫,端口,選項等),並返回`Connection`對象。 -

定義並創建模型

+### 定義並創建模型 -

模型使用Schema接口進行定義。Schema允許您定義存儲在每個文檔中的字段,及其驗證要求和默認值。此外,您可以定義靜態和實例助手方法,以更輕鬆地處理數據類型,以及可以像其他任何字段一樣使用的虛擬屬性,但實際上並不存儲在數據庫中(我們稍後將討論)。

+模型使用`Schema`接口進行定義。Schema 允許您定義存儲在每個文檔中的字段,及其驗證要求和默認值。此外,您可以定義靜態和實例助手方法,以更輕鬆地處理數據類型,以及可以像其他任何字段一樣使用的虛擬屬性,但實際上並不存儲在數據庫中(我們稍後將討論)。 -

然後,綱要Schemas被mongoose.model()方法“編譯”為模型。擁有模型後,您可以使用它來查找,創建,更新和刪除給定類型的對象。

+然後,綱要 Schemas 被`mongoose.model()`方法“編譯”為模型。擁有模型後,您可以使用它來查找,創建,更新和刪除給定類型的對象。 -
-

備註: 每個模型都映射到MongoDB數據庫中的文檔集合。這些文檔將包含模型綱要Schema中定義的字段/綱要型態。

-
+> **備註:** 每個模型都映射到 MongoDB 數據庫中的文檔集合。這些文檔將包含模型綱要`Schema`中定義的字段/綱要型態。 -

定義綱要Schemas

+#### 定義綱要 Schemas -

下面的代碼片段,顯示了您可以如何定義一個簡單的綱要。首先require()mongoose,然後使用Schema構造函數,創建一個新的Schema實例,在構造函數的對象參數中,定義其中的各個字段。

+下面的代碼片段,顯示了您可以如何定義一個簡單的綱要。首先`require()`mongoose,然後使用 Schema 構造函數,創建一個新的 Schema 實例,在構造函數的對象參數中,定義其中的各個字段。 -
//Require Mongoose
+```js
+//Require Mongoose
 var mongoose = require('mongoose');
 
 //Define a schema
@@ -178,15 +162,16 @@ var SomeModelSchema = new Schema({
     a_string: String,
     a_date: Date
 });
-
+``` -

在上面的例子中,我們只有兩個字段,一個字符串和一個日期。在接下來的部分中,我們將展示一些其他的字段類型,驗證和其他方法。

+在上面的例子中,我們只有兩個字段,一個字符串和一個日期。在接下來的部分中,我們將展示一些其他的字段類型,驗證和其他方法。 -

創建模型

+#### 創建模型 -

使用mongoose.model()方法從綱要創建模型:

+使用`mongoose.model()`方法從綱要創建模型: -
// Define schema
+```js
+// Define schema
 var Schema = mongoose.Schema;
 
 var SomeModelSchema = new Schema({
@@ -194,78 +179,70 @@ var SomeModelSchema = new Schema({
     a_date: Date
 });
 
-// Compile model from schema
-var SomeModel = mongoose.model('SomeModel', SomeModelSchema );
+// Compile model from schema +var SomeModel = mongoose.model('SomeModel', SomeModelSchema ); +``` -

第一個參數,是將為模型創建的集合的單數名稱(Mongoose將為上面的SomeModel模型,創建數據庫集合),第二個參數,是您要在創建模型時使用的綱要Shema。

+第一個參數,是將為模型創建的集合的單數名稱(Mongoose 將為上面的 SomeModel 模型,創建數據庫集合),第二個參數,是您要在創建模型時使用的綱要 Shema。 -
-

備註: 定義模型類後,可以使用它們來創建,更新或刪除記錄,並運行查詢,以獲取記錄的所有記錄,或特定子集。我們將在以下“使用模型”部分,向您展示如何執行上述操作,以及當創建視圖時,如何執行此操作。

-
+> **備註:** 定義模型類後,可以使用它們來創建,更新或刪除記錄,並運行查詢,以獲取記錄的所有記錄,或特定子集。我們將在以下“使用模型”部分,向您展示如何執行上述操作,以及當創建視圖時,如何執行此操作。 -

綱要型態(字段)

+#### 綱要型態(字段) -

綱要schema可以有任意數量的字段 — 每個字段代表存儲在MongoDB 文檔中的字段。如下的示例綱要,顯示許多常見字段類型及其聲明方式。

+綱要 schema 可以有任意數量的字段 — 每個字段代表存儲在 MongoDB 文檔中的字段。如下的示例綱要,顯示許多常見字段類型及其聲明方式。 -
var schema = new Schema(
+```js
+var schema = new Schema(
 {
-  name: String,
-  binary: Buffer,
-  living: Boolean,
-  updated: { type: Date, default: Date.now },
-  age: { type: Number, min: 18, max: 65, required: true },
-  mixed: Schema.Types.Mixed,
-  _someId: Schema.Types.ObjectId,
-  array: [],
-  ofString: [String], // You can also have an array of each of the other types too.
-  nested: { stuff: { type: String, lowercase: true, trim: true } }
-})
- -

大多數綱要型態SchemaTypes(“type:”之後或字段名稱之後的描述符)都是自解釋的。例外情況是:

- -
    -
  • ObjectId:表示數據庫中模型的特定實例。例如,一本書可能會使用它來表示其作者對象。這實際上將包含指定對象的唯一ID ( _id) 。我們可以使用populate()方法,在需要時提取相關訊息。
  • -
  • Mixed :任意綱要型態。
  • -
  • [] :一個數組的項目。您可以在這些模型上執行JavaScript數組操作(push,pop,unshift等)。上面的例子,顯示了一個沒有指定類型的對像數組,和一個String對像數組,但是你可以有任何類型的對像數組。
  • -
- -

該代碼還顯示了聲明一個字段的兩種方式:

- -
    -
  • 字段名稱和類型作為鍵值對(即是,像上面的name, binary and living)。
  • -
  • 字段名稱後跟一個定義類型type的對象,以及該字段的任何其他選項。選項包括如下內容: -
      -
    • 默認值。
    • -
    • 內置驗證器(例如最大/最小值)和自定義驗證功能。
    • -
    • 該字段是否為必要
    • -
    • 是否應將字符串String字段自動設置為小寫,大寫或修剪(例如){ type:String, lowercase: true, trim: true }
    • -
    -
  • -
- -

有關選項的更多訊息,請參閱SchemaTypes(Mongoose docs)。

- -

驗證

- -

Mongoose 提供內置和自定義驗證器,以及同步和異步驗證器。它允許您在所有情況下,指定可接受的範圍或值,以及驗證失敗的錯誤消息。

- -

內置的驗證器包括:

- -
    -
  • 所有SchemaTypes都具有內置的必需驗證器。這用於指定,是否必須提供該字段才能保存文檔。
  • -
  • Numbers 數字有最小min和最大max驗證器。
  • -
  • Strings字符串有: -
      -
    • enum枚舉:指定該字段的允許值集合。
    • -
    • match :指定字符串必須匹配的正則表達式。
    • -
    • 字符串的最大長度maxlength和最小長度minlength
    • -
    -
  • -
- -

下面的示例(從Mongoose文檔稍微修改)顯示瞭如何指定一些驗證器類型和錯誤消息:

- -

+  name: String,
+  binary: Buffer,
+  living: Boolean,
+  updated: { type: Date, default: Date.now },
+  age: { type: Number, min: 18, max: 65, required: true },
+  mixed: Schema.Types.Mixed,
+  _someId: Schema.Types.ObjectId,
+  array: [],
+  ofString: [String], // You can also have an array of each of the other types too.
+  nested: { stuff: { type: String, lowercase: true, trim: true } }
+})
+```
+
+大多數綱要型態[SchemaTypes](http://mongoosejs.com/docs/schematypes.html)(“type:”之後或字段名稱之後的描述符)都是自解釋的。例外情況是:
+
+- `ObjectId`:表示數據庫中模型的特定實例。例如,一本書可能會使用它來表示其作者對象。這實際上將包含指定對象的唯一 ID ( `_id`) 。我們可以使用`populate()`方法,在需要時提取相關訊息。
+- [Mixed](http://mongoosejs.com/docs/schematypes.html#mixed) :任意綱要型態。
+- \[] :一個數組的項目。您可以在這些模型上執行 JavaScript 數組操作(push,pop,unshift 等)。上面的例子,顯示了一個沒有指定類型的對像數組,和一個 String 對像數組,但是你可以有任何類型的對像數組。
+
+該代碼還顯示了聲明一個字段的兩種方式:
+
+- 字段名稱和類型作為鍵值對(即是,像上面的`name`, `binary` and `living`)。
+- 字段名稱後跟一個定義類型`type`的對象,以及該字段的任何其他選項。選項包括如下內容:
+
+  - 默認值。
+  - 內置驗證器(例如最大/最小值)和自定義驗證功能。
+  - 該字段是否為必要
+  - 是否應將字符串`String`字段自動設置為小寫,大寫或修剪(例如)`{ type:String, lowercase: true, trim: true }`
+
+有關選項的更多訊息,請參閱[SchemaTypes](http://mongoosejs.com/docs/schematypes.html)(Mongoose docs)。
+
+#### 驗證
+
+Mongoose 提供內置和自定義驗證器,以及同步和異步驗證器。它允許您在所有情況下,指定可接受的範圍或值,以及驗證失敗的錯誤消息。
+
+內置的驗證器包括:
+
+- 所有[SchemaTypes](http://mongoosejs.com/docs/schematypes.html)都具有內置的[必需](http://mongoosejs.com/docs/api.html#schematype_SchemaType-required)驗證器。這用於指定,是否必須提供該字段才能保存文檔。
+- [Numbers ](http://mongoosejs.com/docs/api.html#schema-number-js)[數字](http://mongoosejs.com/docs/api.html#schema-number-js)有最小[min](http://mongoosejs.com/docs/api.html#schema_number_SchemaNumber-min)和最大[max](http://mongoosejs.com/docs/api.html#schema_number_SchemaNumber-max)驗證器。
+- [Strings](http://mongoosejs.com/docs/api.html#schema-string-js)字符串有:
+
+  - [enum](http://mongoosejs.com/docs/api.html#schema_string_SchemaString-enum)枚舉:指定該字段的允許值集合。
+  - [match](http://mongoosejs.com/docs/api.html#schema_string_SchemaString-match) :指定字符串必須匹配的正則表達式。
+  - 字符串的最大長度[maxlength](http://mongoosejs.com/docs/api.html#schema_string_SchemaString-maxlength)和最小長度[minlength](http://mongoosejs.com/docs/api.html#schema_string_SchemaString-minlength)
+
+下面的示例(從 Mongoose 文檔稍微修改)顯示瞭如何指定一些驗證器類型和錯誤消息:
+
+```js
+
     var breakfastSchema = new Schema({
       eggs: {
         type: Number,
@@ -278,90 +255,93 @@ var SomeModel = mongoose.model('SomeModel', SomeModelSchema );
enum: ['Coffee', 'Tea', 'Water',] } }); - +``` -

有關字段驗證的完整訊息,請參閱驗證(Mongoose docs)。

+有關字段驗證的完整訊息,請參閱[驗證](http://mongoosejs.com/docs/validation.html)(Mongoose docs)。 -

虛擬屬性

+#### 虛擬屬性 -

虛擬屬性是您可以獲取和設置的文檔屬性,但不會持久保存到MongoDB。getter 對格式化或組合字段非常有用,而setter 可用於將單個值分解為多個值,以進行存儲。

+虛擬屬性是您可以獲取和設置的文檔屬性,但不會持久保存到 MongoDB。getter 對格式化或組合字段非常有用,而 setter 可用於將單個值分解為多個值,以進行存儲。 -

文檔中的示例,從名字和姓氏字段構造(並解構)一個全名虛擬屬性,這比每次在模板中使用全名更簡單,更清晰。

+文檔中的示例,從名字和姓氏字段構造(並解構)一個全名虛擬屬性,這比每次在模板中使用全名更簡單,更清晰。 -
-

備註: 我們將使用庫中的虛擬屬性,來為每個使用路徑和記錄的_id值的模型記錄,定義唯一的URL。

-
+> **備註:** 我們將使用庫中的虛擬屬性,來為每個使用路徑和記錄的`_id`值的模型記錄,定義唯一的 URL。 -

欲了解更多訊息,請參閱虛擬(Mongoose文檔)。

+欲了解更多訊息,請參閱[虛擬](http://mongoosejs.com/docs/guide.html#virtuals)(Mongoose 文檔)。 -

方法和查詢幫助

+#### 方法和查詢幫助 -

綱要schema也可以有實例方法靜態方法查詢助手。實例和靜態方法很相似,但有明顯的區別,即實例方法與特定記錄相關聯,並且可以訪問當前對象。查詢助手允許您擴展mongoose的鍊式查詢構建器API(例如,除了find(), findOne()findById()方法外,還允許您添加一個“byName”查詢。

+綱要 schema 也可以有[實例方法](http://mongoosejs.com/docs/guide.html#methods),[靜態方法](http://mongoosejs.com/docs/guide.html#statics)和[查詢助手](http://mongoosejs.com/docs/guide.html#query-helpers)。實例和靜態方法很相似,但有明顯的區別,即實例方法與特定記錄相關聯,並且可以訪問當前對象。查詢助手允許您擴展 mongoose 的[鍊式查詢構建器 API](http://mongoosejs.com/docs/queries.html)(例如,除了`find()`, `findOne()`和`findById()`方法外,還允許您添加一個“byName”查詢。 -

使用模型

+### 使用模型 -

一旦創建了綱要,就可以使用它來創建模型。該模型代表數據庫中可以搜索的文檔集合,而模型的實例代表您可以保存和檢索的單個文檔。

+一旦創建了綱要,就可以使用它來創建模型。該模型代表數據庫中可以搜索的文檔集合,而模型的實例代表您可以保存和檢索的單個文檔。 -

我們在下面簡要介紹一下。有關更多訊息,請參閱:模型(Mongoose docs)。

+我們在下面簡要介紹一下。有關更多訊息,請參閱:[模型](http://mongoosejs.com/docs/models.html)(Mongoose docs)。 -

創建和修改文檔

+#### 創建和修改文檔 -

要創建記錄,您可以定義模型的實例,然後調用save()。下面的例子假設,SomeModel是我們從綱要創建的模型(帶有單一字段“name” )。

+要創建記錄,您可以定義模型的實例,然後調用`save()`。下面的例子假設,SomeModel 是我們從綱要創建的模型(帶有單一字段“name” )。 -
// Create an instance of model SomeModel
-var awesome_instance = new SomeModel({ name: 'awesome' });
+```js
+// Create an instance of model SomeModel
+var awesome_instance = new SomeModel({ name: 'awesome' });
 
 // Save the new model instance, passing a callback
 awesome_instance.save(function (err) {
   if (err) return handleError(err);
   // saved!
 });
-
+``` -

創建記錄(以及更新,刪除和查詢)是異步操作— 您提供在操作完成時調用的回調。API使用錯誤優先參數約定,因此回調的第一個參數將始終為錯誤值(或null)。如果API返回一些結果,則將作為第二個參數提供。

+創建記錄(以及更新,刪除和查詢)是異步操作— 您提供在操作完成時調用的回調。API 使用錯誤優先參數約定,因此回調的第一個參數將始終為錯誤值(或 null)。如果 API 返回一些結果,則將作為第二個參數提供。 -

您還可以使用create(),同時定義模型實例,並保存模型實例。回調將為第一個參數返回錯誤,為第二個參數返回新創建的模型實例。

+您還可以使用`create()`,同時定義模型實例,並保存模型實例。回調將為第一個參數返回錯誤,為第二個參數返回新創建的模型實例。 -
SomeModel.create({ name: 'also_awesome' }, function (err, awesome_instance) {
+```js
+SomeModel.create({ name: 'also_awesome' }, function (err, awesome_instance) {
   if (err) return handleError(err);
   // saved!
-});
+}); +``` -

每個模型都有一個關聯的連接(當您使用mongoose.model()時,這將成為默認連接)。您創建一個新連接並調用.model(),以在另一個數據庫上創建文檔。

+每個模型都有一個關聯的連接(當您使用`mongoose.model()`時,這將成為默認連接)。您創建一個新連接並調用`.model()`,以在另一個數據庫上創建文檔。 -

您可以使用點語法訪問此新記錄中的字段,並更改值。您必須調用save()update(),將修改的值存回數據庫。

+您可以使用點語法訪問此新記錄中的字段,並更改值。您必須調用`save()`或`update()`,將修改的值存回數據庫。 -
// Access model field values using dot notation
-console.log(awesome_instance.name); //should log 'also_awesome'
+```js
+// Access model field values using dot notation
+console.log(awesome_instance.name); //should log 'also_awesome'
 
 // Change record by modifying the fields, then calling save().
-awesome_instance.name="New cool name";
-awesome_instance.save(function (err) {
+awesome_instance.name="New cool name";
+awesome_instance.save(function (err) {
    if (err) return handleError(err); // saved!
-   });
-
+ }); +``` -

尋找紀錄

+#### 尋找紀錄 -

可以使用查詢方法搜索記錄,將查詢條件指定為JSON 文檔。下面的代碼片段,顯示瞭如何在數據庫中,找到所有參加網球運動的運動員,只返回運動員姓名和年齡的字段。這裡我們只指定一個匹配的字段(運動 sport),但您可以添加更多條件,指定正則表達式標準,或完全刪除條件以返回所有運動員。

+可以使用查詢方法搜索記錄,將查詢條件指定為 JSON 文檔。下面的代碼片段,顯示瞭如何在數據庫中,找到所有參加網球運動的運動員,只返回運動員姓名和年齡的字段。這裡我們只指定一個匹配的字段(運動 sport),但您可以添加更多條件,指定正則表達式標準,或完全刪除條件以返回所有運動員。 -
var Athlete = mongoose.model('Athlete', yourSchema);
+```js
+var Athlete = mongoose.model('Athlete', yourSchema);
 
 // find all athletes who play tennis, selecting the 'name' and 'age' fields
 Athlete.find({ 'sport': 'Tennis' }, 'name age', function (err, athletes) {
   if (err) return handleError(err);
   // 'athletes' contains the list of athletes that match the criteria.
-})
+}) +``` -

如果您指定回調,如上所示,查詢將立即執行。搜索完成後將調用回調。

+如果您指定回調,如上所示,查詢將立即執行。搜索完成後將調用回調。 -
-

備註: Mongoose中的所有回調,都使用此回調模式callback(error, result)。如果執行查詢時發生錯誤,錯誤參數error將包含錯誤文檔,並且結果result將為null。如果查詢成功,則error參數將為null,並且結果result 將被填充到查詢結果。

-
+> **備註:** Mongoose 中的所有回調,都使用此回調模式`callback(error, result)`。如果執行查詢時發生錯誤,錯誤參數`error`將包含錯誤文檔,並且結果`result`將為 null。如果查詢成功,則`error`參數將為 null,並且結果`result `將被填充到查詢結果。 -

如果您未指定回調,則API將返回Query類型的變量。您可以使用此查詢對象來構建查詢,然後稍後使用exec()方法執行(使用回調)。

+如果您未指定回調,則 API 將返回[Query](http://mongoosejs.com/docs/api.html#query-js)類型的變量。您可以使用此查詢對象來構建查詢,然後稍後使用`exec()`方法執行(使用回調)。 -
// find all athletes that play tennis
+```js
+// find all athletes that play tennis
 var query = Athlete.find({ 'sport': 'Tennis' });
 
 // selecting the 'name' and 'age' fields
@@ -377,60 +357,62 @@ query.sort({ age: -1 });
 query.exec(function (err, athletes) {
   if (err) return handleError(err);
   // athletes contains an ordered list of 5 athletes who play Tennis
-})
+}) +``` -

上面我們在find()方法中,定義了查詢條件。我們也可以使用where()函數來執行此操作,並且我們可以使用點運算符( . )將查詢的所有部分鏈接在一起,而不是分別添加它們。

+上面我們在`find()`方法中,定義了查詢條件。我們也可以使用`where()`函數來執行此操作,並且我們可以使用點運算符( . )將查詢的所有部分鏈接在一起,而不是分別添加它們。 -

下面的代碼片段,與我們上面的查詢相同,並有年齡的附加條件。

+下面的代碼片段,與我們上面的查詢相同,並有年齡的附加條件。 -
Athlete.
+```plain
+Athlete.
   find().
   where('sport').equals('Tennis').
   where('age').gt(17).lt(50).  //Additional where query
   limit(5).
   sort({ age: -1 }).
   select('name age').
-  exec(callback); // where callback is the name of our callback function.
+ exec(callback); // where callback is the name of our callback function. +``` -

find() 方法獲取所有匹配的記錄,但通常你只想獲得一個匹配。以下方法可以查詢單個記錄:

+[find()](http://mongoosejs.com/docs/api.html#query_Query-find) 方法獲取所有匹配的記錄,但通常你只想獲得一個匹配。以下方法可以查詢單個記錄: - +- [`findById()`](http://mongoosejs.com/docs/api.html#model_Model.findById):用指定的`id `查找文檔(每個文檔都有一個唯一的`id`)。 +- [`findOne()`](http://mongoosejs.com/docs/api.html#query_Query-findOne): 查找與指定條件匹配的單個文檔。 +- [`findByIdAndRemove()`](http://mongoosejs.com/docs/api.html#model_Model.findByIdAndRemove), [`findByIdAndUpdate()`](http://mongoosejs.com/docs/api.html#model_Model.findByIdAndUpdate), [`findOneAndRemove()`](http://mongoosejs.com/docs/api.html#query_Query-findOneAndRemove), [`findOneAndUpdate()`](http://mongoosejs.com/docs/api.html#query_Query-findOneAndUpdate):通過`id `或條件查找單個文檔,並更新或刪除它。這些是用於更新和刪除記錄的有用便利功能。 -
-

備註: 還有一個count()方法,您可以使用它來獲取與條件匹配的項目數。如果您想要在不實際提取記錄的情況下執行計數,這非常有用。

-
+> **備註:** 還有一個[`count()`](http://mongoosejs.com/docs/api.html#model_Model.count)方法,您可以使用它來獲取與條件匹配的項目數。如果您想要在不實際提取記錄的情況下執行計數,這非常有用。 -

查詢可以做更多的事情。有關更多訊息,請參閱:查詢(Mongoose文檔)。

+查詢可以做更多的事情。有關更多訊息,請參閱:[查詢](http://mongoosejs.com/docs/queries.html)(Mongoose 文檔)。 -

運用相關文檔— population方法

+#### 運用相關文檔— population 方法 -

您可以使用ObjectId綱要字段,從一個文檔/模型實例,創建一對一引用,或者使用ObjectIds數組,從一個文檔創建一對多的引用。該字段存儲相關模型的ID。如果需要關聯文檔的實際內容,可以在查詢中使用populate()方法,將id替換為實際數據。

+您可以使用`ObjectId`綱要字段,從一個文檔/模型實例,創建一對一引用,或者使用`ObjectIds`數組,從一個文檔創建一對多的引用。該字段存儲相關模型的 ID。如果需要關聯文檔的實際內容,可以在查詢中使用[`populate()`](http://mongoosejs.com/docs/api.html#query_Query-populate)方法,將 id 替換為實際數據。 -

例如,以下綱要定義作者和故事。每個作者可以有多個故事,我們將其表示為一個ObjectId數組。每個故事可以有一個作者。綱要從“ref”(以粗體突出顯示)得知,可以分配給該字段的模型。

+例如,以下綱要定義作者和故事。每個作者可以有多個故事,我們將其表示為一個`ObjectId`數組。每個故事可以有一個作者。綱要從“ref”(以粗體突出顯示)得知,可以分配給該字段的模型。 -
var mongoose = require('mongoose')
+```js
+var mongoose = require('mongoose')
   , Schema = mongoose.Schema
 
 var authorSchema = Schema({
   name    : String,
-  stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
+  stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
 });
 
 var storySchema = Schema({
-  author : { type: Schema.Types.ObjectId, ref: 'Author' },
+  author : { type: Schema.Types.ObjectId, ref: 'Author' },
   title    : String
 });
 
 var Story  = mongoose.model('Story', storySchema);
-var Author = mongoose.model('Author', authorSchema);
+var Author = mongoose.model('Author', authorSchema); +``` -

我們可以通過分配_id值,來保存對相關文檔的引用。下面我們創建一個作者,然後創建一個故事,並將作者ID分配給我們的故事作者字段。

+我們可以通過分配`_id`值,來保存對相關文檔的引用。下面我們創建一個作者,然後創建一個故事,並將作者 ID 分配給我們的故事作者字段。 -
var bob = new Author({ name: 'Bob Smith' });
+```js
+var bob = new Author({ name: 'Bob Smith' });
 
 bob.save(function (err) {
   if (err) return handleError(err);
@@ -445,40 +427,43 @@ bob.save(function (err) {
     if (err) return handleError(err);
     // Bob now has his story
   });
-});
+}); +``` -

我們的故事文檔,現在有作者文檔ID引用的作者。為了在我們的故事結果中,獲取作者訊息,我們使用populate(),如下所示。

+我們的故事文檔,現在有作者文檔 ID 引用的作者。為了在我們的故事結果中,獲取作者訊息,我們使用`populate()`,如下所示。 -
Story
+```js
+Story
 .findOne({ title: 'Bob goes sledding' })
 .populate('author') //This populates the author id with actual author information!
 .exec(function (err, story) {
   if (err) return handleError(err);
   console.log('The author is %s', story.author.name);
   // prints "The author is Bob Smith"
-});
- -
-

備註: 敏銳的讀者會注意到,我們在故事中添加了作者,但我們沒有做任何事情,來將我們的故事添加到作者的故事stories數組中。那麼我們怎樣才能得到特定作者的所有故事?

- -

一種方法,是將作者添加到故事數組中,但這會導致我們需要在兩個地方,維護與作者和故事有關的訊息。更好的方法是獲取作者的_id,然後使用find(),在所有故事的作者字段中搜索此內容。

+}); +``` -
Story
-.find({ author : bob._id })
-.exec(function (err, stories) {
-  if (err) return handleError(err);
-  // returns all stories that have Bob's id as their author.
-});
-
-
+> **備註:** 敏銳的讀者會注意到,我們在故事中添加了作者,但我們沒有做任何事情,來將我們的故事添加到作者的故事`stories`數組中。那麼我們怎樣才能得到特定作者的所有故事? +> +> 一種方法,是將作者添加到故事數組中,但這會導致我們需要在兩個地方,維護與作者和故事有關的訊息。更好的方法是獲取作者的`_id`,然後使用`find()`,在所有故事的作者字段中搜索此內容。 +> +> ```js +> Story +> .find({ author : bob._id }) +> .exec(function (err, stories) { +> if (err) return handleError(err); +> // returns all stories that have Bob's id as their author. +> }); +> ``` -

這幾乎是您在本教程中,使用相關項目時,需要了解的所有內容。有關更多詳細訊息,請參閱Population(Mongoose docs)。

+這幾乎是您在本教程中,使用相關項目時,需要了解的所有內容。有關更多詳細訊息,請參閱[Population](http://mongoosejs.com/docs/populate.html)(Mongoose docs)。 -

一個檔案對應一個綱要/模型

+### 一個檔案對應一個綱要/模型 -

雖然您可以使用任何喜歡的文件結構創建綱要和模型,但我們強烈建議在每個模型模塊(文件)中,定義每個模型綱要,導出方法以創建模型。如下所示:

+雖然您可以使用任何喜歡的文件結構創建綱要和模型,但我們強烈建議在每個模型模塊(文件)中,定義每個模型綱要,導出方法以創建模型。如下所示: -
// File: ./models/somemodel.js
+```js
+// File: ./models/somemodel.js
 
 //Require Mongoose
 var mongoose = require('mongoose');
@@ -491,121 +476,108 @@ var SomeModelSchema = new Schema({
     a_date            : Date,
 });
 
-//Export function to create "SomeModel" model class
-module.exports = mongoose.model('SomeModel', SomeModelSchema );
+//Export function to create "SomeModel" model class +module.exports = mongoose.model('SomeModel', SomeModelSchema ); +``` -

然後,您可以在其他文件中,立即要求並使用該模型。下面我們展示如何使用它,來獲取模型的所有實例。

+然後,您可以在其他文件中,立即要求並使用該模型。下面我們展示如何使用它,來獲取模型的所有實例。 -
//Create a SomeModel model just by requiring the module
+```js
+//Create a SomeModel model just by requiring the module
 var SomeModel = require('../models/somemodel')
 
 // Use the SomeModel object (model) to find all SomeModel records
-SomeModel.find(callback_function);
- -

架設MongoDB數據庫

- -

現在我們了解了Mongoose能做什麼,以及我們想如何設計我們的模型,現在該開始在LocalLibrary網站上工作了。我們想要做的第一件事,就是設置一個MongoDb數據庫,我們可以使用它來儲存我們的圖書館數據。

- -

本教程,我們將使用mLab免費的雲託管的“ 沙盒 ”數據庫。這個數據庫層不適合生產環境的網站,因為它沒有冗餘設計,但它對於開發和原型設計來說非常有用。我們在這裡使用它,是因為它免費且易於設置,並且因為作為數據庫服務供應商來說,mLab是流行的數據庫選擇之一,您可能會合理選擇您的生產環境數據庫(撰寫本文時,其他流行的選擇包括ComposeScaleGridMongoDB Atlas)。

- -
-

備註: 如果您願意,可以下載並安裝與系統相對應的二進製文件,在本地設置MongoDb數據庫。除了您在連接時指定的數據庫URL之外,本文中的其餘指令將很類似。

-
- -

您首先需要使用mLab創建一個賬戶(這是免費的,只需要輸入基本聯繫訊息,並確認其服務條款)。

- -

登錄後,您將進入mLab主畫面:

- -
    -
  1. 單擊MongoDB Deployments部分中的Create New。
  2. -
  3. 這將打開“雲提供商”Cloud Provider 選擇畫面。
    - MLab - screen for new deployment
    - -
      -
    • 從“計劃類型”Plan Type 部分中,選擇“SANDBOX(免費)”計劃。
    • -
    • 從“雲提供商” Cloud Provider部分,選擇任意提供商。不同的提供商,提供不同的地區(顯示在選定的計劃類型下面)。
    • -
    • 單擊“繼續” Continue按鈕。
    • -
    -
  4. -
  5. 這將打開“選擇區域” Select Region 畫面。 -

    Select new region screen

    - -
      -
    • -

      選擇離您最近的地區,然後選擇繼續Continue .

      -
    • -
    -
  6. -
  7. -

    這將打開 Final Details 畫面。
    - New deployment database name

    - -
      -
    • -

      輸入新數據庫的名稱local_library,然後選擇繼續Continue

      -
    • -
    -
  8. -
  9. -

    這將打開訂單確認畫面。
    - Order confirmation screen

    - -
      -
    • -

      單擊“提交訂單” Submit Order以創建數據庫。

      -
    • -
    -
  10. -
  11. -

    您將返回到主畫面。單擊剛剛創建的新數據庫,以打開其詳細訊息畫面。正如你所看到的,數據庫沒有集合(數據)。
    - mLab - Database details screen
    - 您需要用來訪問數據庫的URL,顯示在上面的表單中(如上圖所示)。為了使用它,您需要創建一個可以在URL中指定的數據庫用戶。

    -
  12. -
  13. 單擊用戶Users選項卡,並選擇添加數據庫用戶按鈕Add database user
  14. -
  15. 輸入用戶名和密碼(兩次),然後按創建Create。不要選擇只讀read-only
    -
  16. -
- -

您現在已經創建了數據庫,並且有一個可以用來訪問它的URL(帶有用戶名和密碼)。這看起來像是這樣的:mongodb://your_user_namer:your_password@ds119748.mlab.com:19748/local_library.

- -

安裝 Mongoose

- -

打開命令提示符,並到您創建本地圖書館骨架網站的目錄。輸入以下命令,安裝Mongoose(及其依賴項),並將其添加到您的package.json文件中,除非您在閱讀上述Mongoose入門時,已經這樣做了。

- -
npm install mongoose
-
- -

連接到 MongoDB

- -

打開/app.js(位於項目的根目錄),並在宣告Express應用程序對象的位置(在var app = express();之後)複製以下文本。將數據庫url字符串('insert_your_database_url_here')替換為表示您自己的數據庫的位置URL(即是使用來自上面mLab的訊息)。

- -
//Set up mongoose connection
+SomeModel.find(callback_function);
+```
+
+## 架設 MongoDB 數據庫
+
+現在我們了解了 Mongoose 能做什麼,以及我們想如何設計我們的模型,現在該開始在 LocalLibrary 網站上工作了。我們想要做的第一件事,就是設置一個 MongoDb 數據庫,我們可以使用它來儲存我們的圖書館數據。
+
+本教程,我們將使用[mLab](https://mlab.com/welcome/)免費的雲託管的“ [沙盒](https://mlab.com/plans/pricing/) ”數據庫。這個數據庫層不適合生產環境的網站,因為它沒有冗餘設計,但它對於開發和原型設計來說非常有用。我們在這裡使用它,是因為它免費且易於設置,並且因為作為數據庫服務供應商來說,mLab 是流行的數據庫選擇之一,您可能會合理選擇您的生產環境數據庫(撰寫本文時,其他流行的選擇包括[Compose](https://www.compose.com/)、[ScaleGrid](https://scalegrid.io/pricing.html)和[MongoDB Atlas](https://www.mongodb.com/cloud/atlas))。
+
+> **備註:** 如果您願意,可以下載並安裝[與系統相對應的二進製文件](https://www.mongodb.com/download-center),在本地設置 MongoDb 數據庫。除了您在連接時指定的數據庫 URL 之外,本文中的其餘指令將很類似。
+
+您首先需要[使用 mLab 創建一個賬戶](https://mlab.com/signup/)(這是免費的,只需要輸入基本聯繫訊息,並確認其服務條款)。
+
+登錄後,您將進入[mLab 主](https://mlab.com/home)畫面:
+
+1.  單擊*MongoDB Deployments*部分中的**Create New。**![](https://mdn.mozillademos.org/files/14446/mLabCreateNewDeployment.png)
+2.  這將打開“雲提供商”Cloud Provider 選擇畫面。
+    ![MLab - screen for new deployment](https://mdn.mozillademos.org/files/15661/mLab_new_deployment_form_v2.png)
+
+    - 從“計劃類型”Plan Type 部分中,選擇“SANDBOX(免費)”計劃。
+    - 從“雲提供商” *Cloud Provider*部分,選擇任意提供商。不同的提供商,提供不同的地區(顯示在選定的計劃類型下面)。
+    - 單擊“繼續” **Continue**按鈕。
+
+3.  這將打開“選擇區域” _Select Region_ 畫面。
+
+    ![Select new region screen](https://mdn.mozillademos.org/files/15662/mLab_new_deployment_select_region_v2.png)
+
+    - 選擇離您最近的地區,然後選擇繼續**Continue** .
+
+4.  這將打開 Final Details 畫面。
+    ![New deployment database name](https://mdn.mozillademos.org/files/15663/mLab_new_deployment_final_details.png)
+
+    - 輸入新數據庫的名稱`local_library`,然後選擇繼續**Continue**。
+
+5.  這將打開訂單確認畫面。
+    ![Order confirmation screen](https://mdn.mozillademos.org/files/15664/mLab_new_deployment_order_confirmation.png)
+
+    - 單擊“提交訂單” **Submit Order**以創建數據庫。
+
+6.  您將返回到主畫面。單擊剛剛創建的新數據庫,以打開其詳細訊息畫面。正如你所看到的,數據庫沒有集合(數據)。
+    ![mLab - Database details screen](https://mdn.mozillademos.org/files/15665/mLab_new_deployment_database_details.png)
+    您需要用來訪問數據庫的 URL,顯示在上面的表單中(如上圖所示)。為了使用它,您需要創建一個可以在 URL 中指定的數據庫用戶。
+7.  單擊用戶**Users**選項卡,並選擇添加數據庫用戶按鈕**Add database user**。
+8.  輸入用戶名和密碼(兩次),然後按創建**Create**。不要選擇只讀*read-only*。
+    ![](https://mdn.mozillademos.org/files/14454/mLab_database_users.png)
+
+您現在已經創建了數據庫,並且有一個可以用來訪問它的 URL(帶有用戶名和密碼)。這看起來像是這樣的:`mongodb://your_user_namer:your_password@ds119748.mlab.com:19748/local_library`.
+
+## 安裝 Mongoose
+
+打開命令提示符,並到您創建[本地圖書館骨架網站](/zh-CN/docs/Learn/Server-side/Express_Nodejs/skeleton_website)的目錄。輸入以下命令,安裝 Mongoose(及其依賴項),並將其添加到您的**package.json**文件中,除非您在閱讀上述**Mongoose 入門**時,已經這樣做了。
+
+```bash
+npm install mongoose
+```
+
+## 連接到 MongoDB
+
+打開**/app.js**(位於項目的根目錄),並在宣告 Express 應用程序對象的位置(在`var app = express();`之後)複製以下文本。將數據庫 url 字符串('insert_your_database_url_here')替換為表示您自己的數據庫的位置 URL(即是使用**來自上面 mLab**的訊息)。
+
+```js
+//Set up mongoose connection
 var mongoose = require('mongoose');
-var mongoDB = 'insert_your_database_url_here';
+var mongoDB = 'insert_your_database_url_here';
 mongoose.connect(mongoDB);
 mongoose.Promise = global.Promise;
 var db = mongoose.connection;
-db.on('error', console.error.bind(console, 'MongoDB connection error:'));
+db.on('error', console.error.bind(console, 'MongoDB connection error:')); +``` -

正如上面的Mongoose入門中所討論的,此代碼創建了與數據庫的默認連接,並綁定到錯誤事件(以便將錯誤打印到控制台)。

+正如上面的**Mongoose 入門**中所討論的,此代碼創建了與數據庫的默認連接,並綁定到錯誤事件(以便將錯誤打印到控制台)。 -

定義本地圖書館綱要

+## 定義本地圖書館綱要 -

如上所述,我們將為每個模型定義一個單獨的模塊。首先在項目根目錄(/models)中,為我們的模型創建一個文件夾,然後為每個模型創建單獨的文件:

+如上所述,我們將為每個模型定義一個單獨的模塊。首先在項目根目錄(**/models**)中,為我們的模型創建一個文件夾,然後為每個模型創建單獨的文件: -
/express-locallibrary-tutorial  //the project root
-  /models
-    author.js
-    book.js
-    bookinstance.js
-    genre.js
-
+```plain +/express-locallibrary-tutorial //the project root + /models + author.js + book.js + bookinstance.js + genre.js +``` -

作者模型

+### 作者模型 -

複製下面顯示的Author作者綱要代碼,並將其粘貼到./models/author.js文件中。該綱要定義了一個作者,具有StringSchemaTypes的第一個名稱和家族名稱,這是必需的,最多有100個字符,Date字段為出生和死亡日期。

+複製下面顯示的`Author`作者綱要代碼,並將其粘貼到**./models/author.js**文件中。該綱要定義了一個作者,具有`String`SchemaTypes 的第一個名稱和家族名稱,這是必需的,最多有 100 個字符,`Date`字段為出生和死亡日期。 -
var mongoose = require('mongoose');
+```js
+var mongoose = require('mongoose');
 
 var Schema = mongoose.Schema;
 
@@ -618,12 +590,12 @@ var AuthorSchema = new Schema(
   }
 );
 
-// Virtual for author's full name
+// Virtual for author's full name
 AuthorSchema
 .virtual('name')
 .get(function () {
   return this.family_name + ', ' + this.first_name;
-});
+});
 
 // Virtual for author's URL
 AuthorSchema
@@ -634,32 +606,30 @@ AuthorSchema
 
 //Export model
 module.exports = mongoose.model('Author', AuthorSchema);
+```
 
-
- -

我們還為AuthorSchema,聲明了一個名為“url”的虛擬屬性,它返回獲取模型的特定實例所需的絕對URL — 每當我們需要獲取指向特定作者的鏈接時,我們將在模板中使用該屬性。

+我們還為 AuthorSchema,聲明了一個名為“url”的虛擬屬性,它返回獲取模型的特定實例所需的絕對 URL — 每當我們需要獲取指向特定作者的鏈接時,我們將在模板中使用該屬性。 -
-

備註: 在綱要中聲明我們的URL是虛擬的,這是一個好主意,因為一個項目的URL只需要在一個地方更改。此時,使用此URL的鏈接將不起作用,因為我們還沒有任何路由,可以處理個別模型實例的代碼。我們將在後面的文章中介紹這些內容!

-
+> **備註:** 在綱要中聲明我們的 URL 是虛擬的,這是一個好主意,因為一個項目的 URL 只需要在一個地方更改。此時,使用此 URL 的鏈接將不起作用,因為我們還沒有任何路由,可以處理個別模型實例的代碼。我們將在後面的文章中介紹這些內容! -

在模塊的最後,我們導出了模型。

+在模塊的最後,我們導出了模型。 -

書本模型

+### 書本模型 -

複製下面顯示的Book綱要代碼,並將其粘貼到./models/book.js文件中。其中大部分與作者模型相似—我們已經聲明了一個具有多個字符串字段的綱要,以及一個虛擬屬性,用於獲取特定書籍記錄的URL,並且我們已經導出了模型。

+複製下面顯示的`Book`綱要代碼,並將其粘貼到**./models/book.js**文件中。其中大部分與作者模型相似—我們已經聲明了一個具有多個字符串字段的綱要,以及一個虛擬屬性,用於獲取特定書籍記錄的 URL,並且我們已經導出了模型。 -
var mongoose = require('mongoose');
+```js
+var mongoose = require('mongoose');
 
 var Schema = mongoose.Schema;
 
 var BookSchema = new Schema(
   {
     title: {type: String, required: true},
-    author: {type: Schema.Types.ObjectId, ref: 'Author', required: true},
+    author: {type: Schema.Types.ObjectId, ref: 'Author', required: true},
     summary: {type: String, required: true},
     isbn: {type: String, required: true},
-    genre: [{type: Schema.Types.ObjectId, ref: 'Genre'}]
+    genre: [{type: Schema.Types.ObjectId, ref: 'Genre'}]
   }
 );
 
@@ -672,20 +642,19 @@ BookSchema
 
 //Export model
 module.exports = mongoose.model('Book', BookSchema);
-
+``` -

這裡的主要區別,是我們已經創建了兩個對其他模型的引用:

+這裡的主要區別,是我們已經創建了兩個對其他模型的引用: -
    -
  • 作者是對單個Author作者模型對象的引用,並且是必要的。
  • -
  • 種類是對Genre種類模型對像數組的引用。我們還沒有宣告這個對象!
  • -
+- 作者是對單個`Author`作者模型對象的引用,並且是必要的。 +- 種類是對`Genre`種類模型對像數組的引用。我們還沒有宣告這個對象! -

書本實例模型

+### 書本實例模型 -

最後,複製下面顯示的BookInstance綱要代碼,並將其粘貼到./models/bookinstance.js文件中。BookInstance表示某人可能藉閱的書籍的特定副本,並包含有關該副本是否可用,或預期返回日期的訊息,“印記”或版本詳細訊息。

+最後,複製下面顯示的`BookInstance`綱要代碼,並將其粘貼到**./models/bookinstance.js**文件中。`BookInstance`表示某人可能藉閱的書籍的特定副本,並包含有關該副本是否可用,或預期返回日期的訊息,“印記”或版本詳細訊息。 -
var mongoose = require('mongoose');
+```js
+var mongoose = require('mongoose');
 
 var Schema = mongoose.Schema;
 
@@ -693,8 +662,8 @@ var BookInstanceSchema = new Schema(
   {
     book: { type: Schema.Types.ObjectId, ref: 'Book', required: true }, //reference to the associated book
     imprint: {type: String, required: true},
-    status: {type: String, required: true, enum: ['Available', 'Maintenance', 'Loaned', 'Reserved'], default: 'Maintenance'},
-    due_back: {type: Date, default: Date.now}
+    status: {type: String, required: true, enum: ['Available', 'Maintenance', 'Loaned', 'Reserved'], default: 'Maintenance'},
+    due_back: {type: Date, default: Date.now}
   }
 );
 
@@ -706,87 +675,80 @@ BookInstanceSchema
 });
 
 //Export model
-module.exports = mongoose.model('BookInstance', BookInstanceSchema);
+module.exports = mongoose.model('BookInstance', BookInstanceSchema); +``` + +我們在這裡展示的新東西,是字段選項: + +- 枚舉`enum`:這允許我們設置字符串的允許值。在這種情況下,我們用它來指定我們書籍的可用性狀態(使用枚舉,意味著我們可以防止錯誤拼寫和任意值,成為我們的狀態) +- 默認值`default`:我們使用默認值,將新創 ​​ 建的書本實例的默認狀態,設置為維護,並將默認的`due_back`日期,設置為現在`now`(請注意在設置日期時,如何調用 Date 函數!) + +其他所有內容,大夥應該在前面教程裡邊已經熟悉了。 + +### 種類模型-自我挑戰! + +打開你的**./models/genre.js**文件,並創建一個存儲類型的綱要(書本的類別,例如它是小說還是非小說,浪漫史或軍事歷史等)。 -

我們在這裡展示的新東西,是字段選項:

+該定義將與其他模型非常相似: -
    -
  • 枚舉enum:這允許我們設置字符串的允許值。在這種情況下,我們用它來指定我們書籍的可用性狀態(使用枚舉,意味著我們可以防止錯誤拼寫和任意值,成為我們的狀態)
  • -
  • 默認值default:我們使用默認值,將新創​​ 建的書本實例的默認狀態,設置為維護,並將默認的due_back日期,設置為現在now(請注意在設置日期時,如何調用Date函數!)
  • -
+- 該模型應該有一個名為`name`的`String`SchemaType ,來描述種類。 +- 這個`name`字段應該是必要的,並且有 3 到 ​​100 個字符。 +- 為類型的 URL 聲明虛擬,名為`url`。 +- 導出模型。 -

其他所有內容,大夥應該在前面教程裡邊已經熟悉了。

+## 測試—創建一些項目 -

種類模型-自我挑戰!

+就是這樣。我們現在已經為該網站建立了所有模型! -

打開你的./models/genre.js文件,並創建一個存儲類型的綱要(書本的類別,例如它是小說還是非小說,浪漫史或軍事歷史等)。

+為了測試這些模型(並創建一些示例書籍,和其他項目以便於我們在後面文章使用),現在我們將運行一個獨立的腳本來創建每種類型的項目: -

該定義將與其他模型非常相似:

+1. 在 express-locallibrary-tutorial 目錄下(與`package.json`處於同一級別),下載(或以其他方式創建)文件[populatedb.js](https://raw.githubusercontent.com/hamishwillee/express-locallibrary-tutorial/master/populatedb.js)。 -
    -
  • 該模型應該有一個名為nameStringSchemaType ,來描述種類。
  • -
  • 這個name字段應該是必要的,並且有3到​​100個字符。
  • -
  • 為類型的URL聲明虛擬,名為url
  • -
  • 導出模型。
  • -
+ > **備註:** 您不需要知道[populatedb.js](https://raw.githubusercontent.com/hamishwillee/express-locallibrary-tutorial/master/populatedb.js)的工作原理;它只是將示例數據添加到數據庫中。 -

測試—創建一些項目

+2. 在項目根目錄中,輸入以下命令,以安裝腳本所需的異步模塊(我們將在後面的教程中討論這一點) -

就是這樣。我們現在已經為該網站建立了所有模型!

+ ```bash + npm install async + ``` -

為了測試這些模型(並創建一些示例書籍,和其他項目以便於我們在後面文章使用),現在我們將運行一個獨立的腳本來創建每種類型的項目:

+3. 在命令提示符下,使用 node 運行此腳本,傳遞 MongoDB 數據庫的 URL(與之前在`app.js`中替換 insert_your_database_url_here 佔位符的那個相同): -
    -
  1. 在express-locallibrary-tutorial目錄下(與package.json處於同一級別),下載(或以其他方式創建)文件populatedb.js。 + ```bash + node populatedb ​​​​ + ``` -
    -

    備註: 您不需要知道populatedb.js的工作原理;它只是將示例數據添加到數據庫中。

    -
    -
  2. -
  3. 在項目根目錄中,輸入以下命令,以安裝腳本所需的異步模塊(我們將在後面的教程中討論這一點) -
    npm install async
    -
  4. -
  5. 在命令提示符下,使用node運行此腳本,傳遞MongoDB數據庫的URL(與之前在app.js中替換insert_your_database_url_here佔位符的那個相同): -
    node populatedb <your mongodb url>​​​​
    -
  6. -
  7. 該腳本應一路運行至完成,並在終端中創建它們時顯示各項目。
  8. -
+4. 該腳本應一路運行至完成,並在終端中創建它們時顯示各項目。 -
-

備註:mLab上的數據庫。您現在應該可以深入到書本籍,作者,種類和書本實例的各個集合中,並查看單個文檔。

-
+> **備註:** 至[mLab](https://mlab.com/home)上的數據庫。您現在應該可以深入到書本籍,作者,種類和書本實例的各個集合中,並查看單個文檔。 -

總結

+## 總結 -

本文中我們學到了一點數據庫和Node/Express的ORMs,更多的是關於如何定義Mongoose綱要與模型。然後我們使用這些知識,為本地圖書館網站設計並實作出書本Book,書本實例BookInstance,作者Author和種類Genre模型。

+本文中我們學到了一點數據庫和 Node/Express 的 ORMs,更多的是關於如何定義 Mongoose 綱要與模型。然後我們使用這些知識,為本地圖書館網站設計並實作出書本`Book`,書本實例`BookInstance`,作者`Author`和種類`Genre`模型。 -

最後,我們創建一些實例,以測試模型(使用獨立運作的命令稿)。下一篇文章,我們將關注於如何創建一些網頁,以呈現這些物件。

+最後,我們創建一些實例,以測試模型(使用獨立運作的命令稿)。下一篇文章,我們將關注於如何創建一些網頁,以呈現這些物件。 -

參閱

+## 參閱 - +- [Database integration](https://expressjs.com/en/guide/database-integration.html) (Express docs) +- [Mongoose website](http://mongoosejs.com/) (Mongoose docs) +- [Mongoose Guide](http://mongoosejs.com/docs/guide.html) (Mongoose docs) +- [Validation](http://mongoosejs.com/docs/validation.html) (Mongoose docs) +- [Schema Types](http://mongoosejs.com/docs/schematypes.html) (Mongoose docs) +- [Models](http://mongoosejs.com/docs/models.html) (Mongoose docs) +- [Queries](http://mongoosejs.com/docs/queries.html) (Mongoose docs) +- [Population](http://mongoosejs.com/docs/populate.html) (Mongoose docs) -

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs")}}

+{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs")}} -

本教程連結

+## 本教程連結 - +- [Express/Node introduction](/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction) +- [Setting up a Node (Express) development environment](/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment) +- [Express Tutorial: The Local Library website](/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website) +- [Express Tutorial Part 2: Creating a skeleton website](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website) +- [Express Tutorial Part 3: Using a Database (with Mongoose)](/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose) +- [Express Tutorial Part 4: Routes and controllers](/en-US/docs/Learn/Server-side/Express_Nodejs/routes) +- [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms) +- [Express Tutorial Part 7: Deploying to production](/en-US/docs/Learn/Server-side/Express_Nodejs/deployment) diff --git a/files/zh-tw/learn/server-side/express_nodejs/routes/index.md b/files/zh-tw/learn/server-side/express_nodejs/routes/index.md index 3c6bb624ef5512..8e0c51b904d2f2 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/routes/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/routes/index.md @@ -3,64 +3,67 @@ title: 'Express 教學 4: 路由與控制器' slug: Learn/Server-side/Express_Nodejs/routes translation_of: Learn/Server-side/Express_Nodejs/routes --- -
{{LearnSidebar}}
+{{LearnSidebar}}{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs")}} -
{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs")}}
- -

在本教程中,我們將為最終在 本地圖書館 網站中需要的所有資源端點,搭配 "空殼" 處理函式來配置路由 (URL handling code) 。完成後,我們的路由處理源碼將會有模組化結構,在接下來的文章中,我們可以用真實的處理函式加以擴充。我們也會對如何使用Express 創建模組化路由,有更好的理解。

+在本教程中,我們將為最終在 本地圖書館 網站中需要的所有資源端點,搭配 "空殼" 處理函式來配置路由 (URL handling code) 。完成後,我們的路由處理源碼將會有模組化結構,在接下來的文章中,我們可以用真實的處理函式加以擴充。我們也會對如何使用 Express 創建模組化路由,有更好的理解。 - - - - - - - - - - + + + + + + + + + +
先備知識:閱讀 Express/Node 介紹。 完成先前教學主題 (包含 Express 教學 3: 使用資料庫 (Mongoose)).
目標:理解如何創建簡易路由配置。我們所有的URL端點。
先備知識: + 閱讀 + Express/Node 介紹。 完成先前教學主題 (包含 + Express 教學 3: 使用資料庫 (Mongoose)). +
目標:理解如何創建簡易路由配置。我們所有的URL端點。
-

概覽

+## 概覽 -

上一篇教程文章中,我們定義了Mongoose模型,以與數據庫互動,並使用(獨立)腳本創建一些初始庫記錄。現在我們可以編寫代碼,向用戶展示這些信息。我們需要做的第一件事,是確定我們希望能夠在頁面中顯示哪些信息,然後定義適當的URL,以返回這些資源。然後我們將需要創建路由(URL處理程序)和視圖(模板)來顯示這些頁面。

+在[上一篇教程文章](/zh-CN/docs/Learn/Server-side/Express_Nodejs/mongoose)中,我們定義了 Mongoose 模型,以與數據庫互動,並使用(獨立)腳本創建一些初始庫記錄。現在我們可以編寫代碼,向用戶展示這些信息。我們需要做的第一件事,是確定我們希望能夠在頁面中顯示哪些信息,然後定義適當的 URL,以返回這些資源。然後我們將需要創建路由(URL 處理程序)和視圖(模板)來顯示這些頁面。 -

下圖是作為處理HTTP請求/響應時,需要實現的主要數據流和事項的提醒。除了視圖和路線之外,圖表還顯示“控制器” — 實際處理請求的函數,那些與路由請求分開的代碼。

+下圖是作為處理 HTTP 請求/響應時,需要實現的主要數據流和事項的提醒。除了視圖和路線之外,圖表還顯示“控制器” — 實際處理請求的函數,那些與路由請求分開的代碼。 -

由於我們已經創建了模型,我們需要創建的主要內容是:

+由於我們已經創建了模型,我們需要創建的主要內容是: -
    -
  • “路由”將支持的請求(以及請求URL中編碼的任何信息)轉發到適當的控制器功能。
  • -
  • 控制器用於從模型中獲取請求的數據,創建一個顯示數據的HTML頁面,並將其返回給用戶,以在瀏覽器中查看。
  • -
  • 視圖(模板)則由控制器用來呈現數據。
  • -
+- “路由”將支持的請求(以及請求 URL 中編碼的任何信息)轉發到適當的控制器功能。 +- 控制器用於從模型中獲取請求的數據,創建一個顯示數據的 HTML 頁面,並將其返回給用戶,以在瀏覽器中查看。 +- 視圖(模板)則由控制器用來呈現數據。 -

+![](mvc_express.png) -

最終,我們可能會有頁面顯示書籍,流派,作者和書籍的列表和詳細信息,以及用於創建,更新和刪除記錄的頁面。對一篇文章來說,這是很多的內容。因此,本文的大部分內容,都將集中在設置我們的路由和控制器,以返回“虛擬”內容。我們將在後續文章中,擴展控制器方法,以使用模型數據。

+最終,我們可能會有頁面顯示書籍,流派,作者和書籍的列表和詳細信息,以及用於創建,更新和刪除記錄的頁面。對一篇文章來說,這是很多的內容。因此,本文的大部分內容,都將集中在設置我們的路由和控制器,以返回“虛擬”內容。我們將在後續文章中,擴展控制器方法,以使用模型數據。 -

下面的第一部分,提供了關於如何使用Express Router中間件的簡要“入門”。當我們設置LocalLibrary路由時,我們將在後面的章節中使用這些知識。

+下面的第一部分,提供了關於如何使用[Express Router](http://expressjs.com/en/4x/api.html#router)中間件的簡要“入門”。當我們設置 LocalLibrary 路由時,我們將在後面的章節中使用這些知識。 -

路由入門

+## 路由入門 -

路由是Express代碼的一部分,它將HTTP動詞(GET, POST, PUT, DELETE等),URL路徑/模式和被調用來處理該模式的函數,相關聯起來。

+路由是 Express 代碼的一部分,它將 HTTP 動詞(`GET`, `POST`, `PUT`, `DELETE`等),URL 路徑/模式和被調用來處理該模式的函數,相關聯起來。 -

有幾種方法可以創建路線。本教程將使用express.Router中間件,因為它允許我們將站點的特定部分的路由處理程序組合在一起,並使用通用的路由前綴訪問它們。我們會將所有與圖書館有關的路由,保存在“目錄”模塊中,如果我們添加路由來處理用戶帳戶或其他功能,我們可以將它們分開保存。

+有幾種方法可以創建路線。本教程將使用[`express.Router`](http://expressjs.com/en/guide/routing.html#express-router)中間件,因為它允許我們將站點的特定部分的路由處理程序組合在一起,並使用通用的路由前綴訪問它們。我們會將所有與圖書館有關的路由,保存在“目錄”模塊中,如果我們添加路由來處理用戶帳戶或其他功能,我們可以將它們分開保存。 -
-

備註: 我們在Express簡介>創建路由處理程序中,簡要討論了Express應用程序路由。除了為模塊化提供更好的支持之外(如下面第一小節所述),使用Router非常類似於直接在Express應用程序對像上定義路由。

-
+> **備註:** 我們在[Express 簡介>創建路由處理程序](/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction#Creating_route_handlers)中,簡要討論了 Express 應用程序路由。除了為模塊化提供更好的支持之外(如下面第一小節所述),使用 Router 非常類似於直接在 Express 應用程序對像上定義路由。 -

本節的其餘部分,概述瞭如何使用路由器Router來定義路由。

+本節的其餘部分,概述瞭如何使用路由器`Router`來定義路由。 -

定義和使用單獨的路由模塊

+### 定義和使用單獨的路由模塊 -

下面的代碼提供了一個具體示例,說明我們如何創建路由模塊,然後在Express應用程序中使用它。首先,我們在一個名為wiki.js的模塊中創建一個wiki的路由。代碼首先導入Express應用程序對象,使用它獲取一個

+下面的代碼提供了一個具體示例,說明我們如何創建路由模塊,然後在 Express 應用程序中使用它。首先,我們在一個名為**wiki.js**的模塊中創建一個 wiki 的路由。代碼首先導入 Express 應用程序對象,使用它獲取一個 -

Router對象,然後使用get()方法向其添加一對路由。所有模塊的最後一個導出路由器Router對象。

+`Router`對象,然後使用`get()`方法向其添加一對路由。所有模塊的最後一個導出路由器`Router`對象。 -
// wiki.js - Wiki route module.
+```js
+// wiki.js - Wiki route module.
 
 var express = require('express');
 var router = express.Router();
@@ -75,136 +78,132 @@ router.get('/about', function (req, res) {
   res.send('About this wiki');
 })
 
-module.exports = router;
-
-
+module.exports = router; +``` -
-

備註: 上面我們直接在路由器函數中定義路由處理程序回調。在LocalLibrary中,我們將在一個單獨的控制器模塊中,定義這些回調。

-
+> **備註:** 上面我們直接在路由器函數中定義路由處理程序回調。在 LocalLibrary 中,我們將在一個單獨的控制器模塊中,定義這些回調。 -

要在主應用程序文件中使用路由器模塊,我們首先require()路由模塊(wiki.js)。然後,我們在Express應用程序上調用use(),將路由器添加到中間件處理路徑,並指定一個'wiki'的URL路徑。

+要在主應用程序文件中使用路由器模塊,我們首先`require()`路由模塊(**wiki.js**)。然後,我們在 Express 應用程序上調用`use()`,將路由器添加到中間件處理路徑,並指定一個'wiki'的 URL 路徑。 -
var wiki = require('./wiki.js');
+```js
+var wiki = require('./wiki.js');
 // ...
-app.use('/wiki', wiki);
+app.use('/wiki', wiki); +``` -

然後可以從/wiki//wiki/about/,訪問我們的wiki路由模塊中定義的兩個路由。

+然後可以從`/wiki/`和`/wiki/about/`,訪問我們的 wiki 路由模塊中定義的兩個路由。 -

路由函數

+### 路由函數 -

我們上面的模塊,定義了幾個典型的路由功能。使用Router.get()方法定義“about”路由(在下面),該方法僅響應HTTP GET請求。此方法的第一個參數是URL路徑,而第二個參數是一個回調函數,如果收到帶有路徑的HTTP GET請求,將會調用該函數。

+我們上面的模塊,定義了幾個典型的路由功能。使用`Router.get()`方法定義“about”路由(在下面),該方法僅響應 HTTP GET 請求。此方法的第一個參數是 URL 路徑,而第二個參數是一個回調函數,如果收到帶有路徑的 HTTP GET 請求,將會調用該函數。 -
router.get('/about', function (req, res) {
+```js
+router.get('/about', function (req, res) {
   res.send('About this wiki');
-})
-
- -

回調函數接受三個參數(通常如下所示命名:req, res, next),它將包含HTTP請求對象,HTTP響應,以及中間件鏈中的下一個函數。

+}) +``` -
-

備註: 路由器功能是Express中間件,這意味著它們必須完成(響應)請求或調用鏈中的下一個功能next。在上面的例子中,我們使用send()完成了請求,所以下一個參數next沒有被使用(我們選擇不指定它)。

+回調函數接受三個參數(通常如下所示命名:`req`, `res`, `next`),它將包含 HTTP 請求對象,HTTP 響應,以及中間件鏈中的下一個函數。 -

上面的路由器函數只需要一次回調,但您可以根據需要指定任意數量的回調參數,或一組回調函數。每個函數都是中間件鏈的一部分,並且將按照添加到鏈中的順序調用(除非前面的函數完成請求)。

-
+> **備註:** 路由器功能是[Express 中間件](/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction#Using_middleware),這意味著它們必須完成(響應)請求或調用鏈中的下一個功能`next`。在上面的例子中,我們使用`send()`完成了請求,所以下一個參數`next`沒有被使用(我們選擇不指定它)。 +> +> 上面的路由器函數只需要一次回調,但您可以根據需要指定任意數量的回調參數,或一組回調函數。每個函數都是中間件鏈的一部分,並且將按照添加到鏈中的順序調用(除非前面的函數完成請求)。 -

這裡的回調函數,在響應中調用send(),當我們收到帶有路徑(' /about')的GET請求時,返回字符串“About this wiki”。有許多其他響應方法,可以結束請求/響應週期。例如,您可以調用res.json(),來發送JSON響應,或調用res.sendFile()來發送文件。構建庫時,我們最常使用的響應方法是render(),它使用模板和數據創建並返回HTML文件—我們將在後面的文章中,進一步討論這個問題!

+這裡的回調函數,在響應中調用[`send()`](https://expressjs.com/en/4x/api.html#res.send),當我們收到帶有路徑(' `/about'`)的 GET 請求時,返回字符串“About this wiki”。有[許多其他響應方法](https://expressjs.com/en/guide/routing.html#response-methods),可以結束請求/響應週期。例如,您可以調用[`res.json()`](https://expressjs.com/en/4x/api.html#res.json),來發送 JSON 響應,或調用[`res.sendFile()`](https://expressjs.com/en/4x/api.html#res.sendFile)來發送文件。構建庫時,我們最常使用的響應方法是[render()](https://expressjs.com/en/4x/api.html#res.render),它使用模板和數據創建並返回 HTML 文件—我們將在後面的文章中,進一步討論這個問題! -

HTTP 動詞

+### HTTP 動詞 -

上面的示例路由使用Router.get()方法,響應具有特定路徑的HTTP GET請求。路由器Router還為所有其他HTTP動詞提供路由方法,這些方法多數以完全相同的方式使用:post(), put(), delete(), options(), trace(), copy(), lock(), mkcol(), move(), purge(), propfind(), proppatch(), unlock(), report(), mkactivity()​​​​​​, checkout(), merge(), m-search(), notify(), subscribe(), unsubscribe(), patch(), search(),和connect()

+上面的示例路由使用`Router.get()`方法,響應具有特定路徑的 HTTP GET 請求。路由器`Router`還為所有其他 HTTP 動詞提供路由方法,這些方法多數以完全相同的方式使用:`post()`, `put()`, `delete()`, `options()`, `trace()`, `copy()`, `lock()`, `mkcol()`, `move()`, `purge()`, `propfind()`, `proppatch()`, `unlock()`, `report()`, `mkactivity()`​​​​​​, `checkout()`, `merge()`, ` m-``search() `, `notify()`, `subscribe()`, `unsubscribe()`, `patch()`, `search()`,和`connect()`。 -

例如,下面的代碼就像上一個/about路由一樣,但只響應HTTP POST請求。

+例如,下面的代碼就像上一個`/about`路由一樣,但只響應 HTTP POST 請求。 -
router.post('/about', function (req, res) {
+```js
+router.post('/about', function (req, res) {
   res.send('About this wiki');
-})
+}) +``` -

路由路徑

+### 路由路徑 -

路由路徑定義可以進行請求的端點。我們到目前為止看到的例子,都是字符串,並且完全按照字符串的寫法使用:'/','/ about','/ book','/any-random.path'。

+路由路徑定義可以進行請求的端點。我們到目前為止看到的例子,都是字符串,並且完全按照字符串的寫法使用:'/','/ about','/ book','/any-random.path'。 -

路由路徑也可以是字符串模式。字符串模式使用正則表達式語法的子集,來定義將匹配的端點模式。下面列出了子集(請注意,連字符(-)和點(.)由字符串路徑字面解釋):

+路由路徑也可以是字符串模式。字符串模式使用正則表達式語法的子集,來定義將匹配的端點模式。下面列出了子集(請注意,連字符(`-`)和點(`.`)由字符串路徑字面解釋): -
    -
  • ? :端點在?號前面的那個字符,必須為0個或1個。例如。'/ab?cd'的路徑路徑將匹配端點acd abcd
  • -
  • + :端點在+號前面的那個字符,必須為1個或多個。例如,'/ab+cd'的路徑路徑將與端點abcdabbcdabbbcd等匹配。
  • -
  • * :端點在放置*字符的地方,可以代換為任意字符串。例如。'ab\*cd'的路由路徑,將匹配端點abcd, abXcd, abSOMErandomTEXTcd等。
  • -
  • () :將一組字符進行匹配,以執行上面三個操作。例如。'/ab(cd)?e',表示以?號對(cd)進行匹配-它會匹配abeabcde。(譯註:即(cd)必須為0個或1個。若為0,匹配abe。若為1,匹配abcde
  • -
+- ? :端點在?號前面的那個字符,必須為 0 個或 1 個。例如。`'/ab?cd'`的路徑路徑將匹配端點`acd `或`abcd`。 +- \+ :端點在+號前面的那個字符,必須為 1 個或多個。例如,`'/ab+cd'`的路徑路徑將與端點`abcd`,`abbcd`,`abbbcd`等匹配。 +- \* :端點在放置\*字符的地方,可以代換為任意字符串。例如。`'ab\*cd'`的路由路徑,將匹配端點`abcd`, `abXcd`, `abSOMErandomTEXTcd`等。 +- () :將一組字符進行匹配,以執行上面三個操作。例如。`'/ab(cd)?e'`,表示以?號對(cd)進行匹配-它會匹配`abe`和`abcde`。(譯註:即(cd)必須為 0 個或 1 個。若為 0,匹配`abe`。若為 1,匹配`abcde`) -

路由路徑也可以是JavaScript正則表達式。例如,下面的路由路徑將與鯰魚catfish 和角鯊魚dogfish相匹配,但不包括鯰魚catflap、鯰魚頭catfishhead等。請注意,正則表達式的路徑使用正則表達式語法(它不像以前那樣,是帶引號的字符串)。

+路由路徑也可以是[JavaScript 正則表達式](/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions)。例如,下面的路由路徑將與鯰魚`catfish `和角鯊魚`dogfish`相匹配,但不包括鯰魚`catflap`、鯰魚頭`catfishhead`等。請注意,正則表達式的路徑使用正則表達式語法(它不像以前那樣,是帶引號的字符串)。 -
app.get(/.*fish$/, function (req, res) {
+```js
+app.get(/.*fish$/, function (req, res) {
   ...
-})
+}) +``` -
-

備註: LocalLibrary的大部分路由,都只使用字符串,而不是字符串模式和正則表達式。我們還將使用下一節中討論的路由參數。

-
+> **備註:** LocalLibrary 的大部分路由,都只使用字符串,而不是字符串模式和正則表達式。我們還將使用下一節中討論的路由參數。 -

路由參數

+### 路由參數 -

路徑參數是命名的URL段,用於捕獲在URL中的位置指定的值。命名段以冒號為前綴,然後是名稱(例如。捕獲的值,使用參數名稱作為鍵,存在對像中(例如)。/:your_parameter_name/req.paramsreq.params.your_parameter_name

+路徑參數是命名的 URL 段,用於捕獲在 URL 中的位置指定的值。命名段以冒號為前綴,然後是名稱(例如。捕獲的值,使用參數名稱作為鍵,存在對像中(例如)。` /:your_parameter_name/``req.params``req.params.your_parameter_name ` -

例如,考慮一個編碼的URL,其中包含有關用戶和書本的信息:http://localhost:3000/users/34/books/8989。我們可以使用userIdbookId路徑參數,提取如下所示的信息:

+例如,考慮一個編碼的 URL,其中包含有關用戶和書本的信息:`http://localhost:3000/users/34/books/8989`。我們可以使用`userId`和`bookId`路徑參數,提取如下所示的信息: -
app.get('/users/:userId/books/:bookId', function (req, res) {
+```plain
+app.get('/users/:userId/books/:bookId', function (req, res) {
   // Access userId via: req.params.userId
   // Access bookId via: req.params.bookId
   res.send(req.params);
 })
-
+``` -

路由參數的名稱,必須由“單詞字符”(AZ,az,0-9和_)組成。

+路由參數的名稱,必須由“單詞字符”(AZ,az,0-9 和\_)組成。 -
-

備註: URL /book/create將與/book/:bookId 之類的路由匹配(它將提取要創建' create'的“bookId”值)。將使用與傳入URL匹配的第一個路由,因此,如果要單獨處理/book/createURL,則必須在/book/:bookId路由之前,先定義其路由處理程序。

-
+> **備註:** URL */book/create*將與`/book/:bookId` 之類的路由匹配(它將提取要創建' `create`'的“bookId”值)。將使用與傳入 URL 匹配的第一個路由,因此,如果要單獨處理`/book/create`URL,則必須在`/book/:bookId`路由之前,先定義其路由處理程序。 -

這就是您開始使用路由所需的全部內容-如果需要,您可以在Express文檔中找到更多信息:基本路由路由指南。以下部分顯示了我們如何為LocalLibrary設置路由和控制器。

+這就是您開始使用路由所需的全部內容-如果需要,您可以在 Express 文檔中找到更多信息:[基本路由](http://expressjs.com/en/starter/basic-routing.html)和[路由指南](http://expressjs.com/en/guide/routing.html)。以下部分顯示了我們如何為 LocalLibrary 設置路由和控制器。 -

本地圖書館需要的路由

+## 本地圖書館需要的路由 -

下面列出了我們最終需要用於頁面的URL,其中object被替換為每個模型的名稱(book,bookinstance,genre,author),objects是對象的複數,id是默認情況下,為每個Mongoose模型實例指定的唯一實例字段(_id)。

+下面列出了我們最終需要用於頁面的 URL,其中 object 被替換為每個模型的名稱(book,bookinstance,genre,author),objects 是對象的複數,id 是默認情況下,為每個 Mongoose 模型實例指定的唯一實例字段(`_id`)。 -
    -
  • catalog/ — 主頁/索引頁面。
  • -
  • catalog/<objects>/—所有書本,書本實例,種類或作者的列表(例如/ catalog/books/, / catalog/genres/等)
  • -
  • catalog/<object>/<id>—具有給定_id字段值的特定書本,書本實例,種類或作者的詳細信息頁面(例如/catalog/book/584493c1f4887f06c0e67d37)。
  • -
  • catalog/<object>/create—用於創建新的書本,書本實例,種類或作者的表單(例如/catalog/book/create)。
  • -
  • catalog/<object>/<id>/update—使用給定的_id字段值更新特定書本,書本實例,種類或作者的表單(例如/catalog/book/584493c1f4887f06c0e67d37/update)。
  • -
  • catalog/<object>/<id>/delete—刪除具有給定_id字段值的特定書本,書本實例,種類或作者的表單(例如/catalog/book/584493c1f4887f06c0e67d37/delete)。
  • -
+- `catalog/` — 主頁/索引頁面。 +- `catalog//`—所有書本,書本實例,種類或作者的列表(例如/ `catalog/books/`, / `catalog/genres/`等) +- `catalog//`—具有給定`_id`字段值的特定書本,書本實例,種類或作者的詳細信息頁面(例如`/catalog/book/584493c1f4887f06c0e67d37`)。 +- `catalog//create`—用於創建新的書本,書本實例,種類或作者的表單(例如`/catalog/book/create`)。 +- `catalog///update`—使用給定的`_id`字段值更新特定書本,書本實例,種類或作者的表單(例如`/catalog/book/584493c1f4887f06c0e67d37/update`)。 +- `catalog///delete`—刪除具有給定`_id`字段值的特定書本,書本實例,種類或作者的表單(例如`/catalog/book/584493c1f4887f06c0e67d37/delete`)。 -

第一個主頁和列表頁面,不編碼任何其他信息。雖然返回的結果,將取決於模型類型和數據庫中的內容,但為了獲取信息所運行的查詢,將始終相同(類似地,用於創建對象的代碼將始終類似)。相反的,其他URL用於處理特定文檔/模型實例—這些將項目的標識編碼在URL中(如上面的<id>)。

+第一個主頁和列表頁面,不編碼任何其他信息。雖然返回的結果,將取決於模型類型和數據庫中的內容,但為了獲取信息所運行的查詢,將始終相同(類似地,用於創建對象的代碼將始終類似)。相反的,其他 URL 用於處理特定文檔/模型實例—這些將項目的標識編碼在 URL 中(如上面的``)。 -

我們將使用路徑參數,來提取編碼信息,並將其傳遞給路由處理程序(在稍後的文章中,我們將使用它來動態確定從數據庫獲取的信息)。通過對我們的URL中的信息進行編碼,我們只需要一個路由,用於特定類型的每個資源(例如,一個路由來處理每個書本項目的顯示)。

+我們將使用路徑參數,來提取編碼信息,並將其傳遞給路由處理程序(在稍後的文章中,我們將使用它來動態確定從數據庫獲取的信息)。通過對我們的 URL 中的信息進行編碼,我們只需要一個路由,用於特定類型的每個資源(例如,一個路由來處理每個書本項目的顯示)。 -
-

備註: Express允許您以任何方式構建URL -您可以在URL正文中編碼信息,就像上面一樣,或使用URL GET參數(例如/book/?id=6)。無論您使用哪種方法,URL都應保持乾淨,合理且可讀(請在此處查看W3C建議)。

-
+> **備註:** Express 允許您以任何方式構建 URL -您可以在 URL 正文中編碼信息,就像上面一樣,或使用 URL `GET`參數(例如`/book/?id=6`)。無論您使用哪種方法,URL 都應保持乾淨,合理且可讀(請在此處查看[W3C 建議](https://www.w3.org/Provider/Style/URI))。 -

接下來,我們為所有上述URL,創建路由處理程序回調函數和路由代碼。

+接下來,我們為所有上述 URL,創建路由處理程序回調函數和路由代碼。 -

創建路由-handler回調函式

+## 創建路由-handler 回調函式 -

在我們定義路由之前,我們將首先創建它們將調用的所有虛擬/骨架回調函數。回調將存在Books,BookInstances,Genres 和Authors 的單獨“控制器” 模塊中(您可以使用任何文件/模塊結構,但這似乎是該項目的適當粒度)。

+在我們定義路由之前,我們將首先創建它們將調用的所有虛擬/骨架回調函數。回調將存在 Books,BookInstances,Genres 和 Authors 的單獨“控制器” 模塊中(您可以使用任何文件/模塊結構,但這似乎是該項目的適當粒度)。 -

首先在項目根目錄(/controllers)中,為我們的控制器創建一個文件夾,然後創建單獨的控制器文件/模塊,來處理每個模型:

+首先在項目根目錄(**/controllers**)中,為我們的控制器創建一個文件夾,然後創建單獨的控制器文件/模塊,來處理每個模型: -
/express-locallibrary-tutorial  //the project root
-  /controllers
-    authorController.js
-    bookController.js
-    bookinstanceController.js
-    genreController.js
+```plain +/express-locallibrary-tutorial //the project root + /controllers + authorController.js + bookController.js + bookinstanceController.js + genreController.js +``` -

作者控制器

+### 作者控制器 -

打開/controllers/authorController.js文件,並複制以下代碼:

+打開**/controllers/authorController.js**文件,並複制以下代碼: -
var Author = require('../models/author');
+```js
+var Author = require('../models/author');
 
 // Display list of all Authors.
 exports.author_list = function(req, res) {
@@ -245,17 +244,18 @@ exports.author_update_get = function(req, res) {
 exports.author_update_post = function(req, res) {
     res.send('NOT IMPLEMENTED: Author update POST');
 };
-
+``` -

該模塊首先導入我們稍後將使用的模型,來訪問和更新我們的數據。然後它為我們希望處理的每個URL,導出函數(創建,更新和刪除操作使用表單,因此還有其他方法,來處理表單發布請求- 我們將在稍後的“表單文章” 中討論這些方法) 。

+該模塊首先導入我們稍後將使用的模型,來訪問和更新我們的數據。然後它為我們希望處理的每個 URL,導出函數(創建,更新和刪除操作使用表單,因此還有其他方法,來處理表單發布請求- 我們將在稍後的“表單文章” 中討論這些方法) 。 -

所有函數都具有Express中間件函數的標準形式,如果方法沒有完成請求週期,則會調用請求,響應和next下一個函數的參數(在所有這些情況下,它都會執行!)。這些方法只返回一個字符串,表明尚未創建關聯的頁面。如果期望控制器函數接收路徑參數,則在消息字符串中,輸出這些參數(參見上面的req.params.id)。

+所有函數都具有 Express 中間件函數的標準形式,如果方法沒有完成請求週期,則會調用請求,響應和`next`下一個函數的參數(在所有這些情況下,它都會執行!)。這些方法只返回一個字符串,表明尚未創建關聯的頁面。如果期望控制器函數接收路徑參數,則在消息字符串中,輸出這些參數(參見上面的`req.params.id`)。 -

書本實例控制器

+#### 書本實例控制器 -

打開/controllers/bookinstanceController.js文件,並將其複製到以下代碼中(它遵循與Author控制器模塊相同的模式):

+打開**/controllers/bookinstanceController.js**文件,並將其複製到以下代碼中(它遵循與`Author`控制器模塊相同的模式): -
var BookInstance = require('../models/bookinstance');
+```js
+var BookInstance = require('../models/bookinstance');
 
 // Display list of all BookInstances.
 exports.bookinstance_list = function(req, res) {
@@ -296,13 +296,14 @@ exports.bookinstance_update_get = function(req, res) {
 exports.bookinstance_update_post = function(req, res) {
     res.send('NOT IMPLEMENTED: BookInstance update POST');
 };
-
+``` -

種類控制器

+#### 種類控制器 -

打開/controllers/genreController.js文件,並複制以下文本(這與AuthorBookInstance文件的模式相同):

+打開**/controllers/genreController.js**文件,並複制以下文本(這與`Author`和`BookInstance`文件的模式相同): -
var Genre = require('../models/genre');
+```js
+var Genre = require('../models/genre');
 
 // Display list of all Genre.
 exports.genre_list = function(req, res) {
@@ -343,17 +344,18 @@ exports.genre_update_get = function(req, res) {
 exports.genre_update_post = function(req, res) {
     res.send('NOT IMPLEMENTED: Genre update POST');
 };
-
+``` -

書本控制器

+#### 書本控制器 -

打開/controllers/bookController.js文件,並複制以下代碼。它遵循與其他控制器模塊相同的模式,但另外還有一個index()函數,用於顯示站點歡迎頁面:

+打開**/controllers/bookController.js**文件,並複制以下代碼。它遵循與其他控制器模塊相同的模式,但另外還有一個`index()`函數,用於顯示站點歡迎頁面: -
var Book = require('../models/book');
+```js
+var Book = require('../models/book');
 
-exports.index = function(req, res) {
+exports.index = function(req, res) {
     res.send('NOT IMPLEMENTED: Site Home Page');
-};
+};
 
 // Display list of all books.
 exports.book_list = function(req, res) {
@@ -394,25 +396,28 @@ exports.book_update_get = function(req, res) {
 exports.book_update_post = function(req, res) {
     res.send('NOT IMPLEMENTED: Book update POST');
 };
-
+``` -

創建目錄路由模組

+## 創建目錄路由模組 -

接下來,我們為LocalLibrary 網站,創建所需全部URL 的路由,這將調用我們在上一節中定義的控制器功能。

+接下來,我們為 LocalLibrary 網站,創建所需全部 URL 的路由,這將調用我們在上一節中定義的控制器功能。 -

骨架網站已經有一個./routes文件夾,其中包含索引和用戶的路由。在此文件夾中,創建另一個路徑文件— catalog.js —如下圖所示。

+骨架網站已經有一個**./routes**文件夾,其中包含索引和用戶的路由。在此文件夾中,創建另一個路徑文件— **catalog.js** —如下圖所示。 -
/express-locallibrary-tutorial //the project root
+```plain
+/express-locallibrary-tutorial //the project root
   /routes
     index.js
     users.js
-    catalog.js
+ catalog.js +``` -

打開/routes/ catalog.js,複製下面的代碼:

+打開**/routes/** **catalog.js**,複製下面的代碼: -
var express = require('express');
+```js
+var express = require('express');
 var router = express.Router();
-
+
 // Require controller modules.
 var book_controller = require('../controllers/bookController');
 var author_controller = require('../controllers/authorController');
@@ -526,117 +531,110 @@ router.get('/bookinstance/:id', book_instance_controller.bookinstance_detail);
 // GET request for list of all BookInstance.
 router.get('/bookinstances', book_instance_controller.bookinstance_list);
 
-module.exports = router;
-
+module.exports = router; +``` -

該模塊導入Express,然後使用它來創建一個Router對象。路由都在路由器上設置完成,然後導出。

+該模塊導入 Express,然後使用它來創建一個`Router`對象。路由都在路由器上設置完成,然後導出。 -

路由是使用路由器對像上的.get().post()方法定義的。所有路徑都是使用字符串定義的(我們不使用字符串模式或正則表達式)。作用於某些特定資源(如書籍)的路由,則使用路徑參數從URL中獲取對象標識id。

+路由是使用路由器對像上的`.get()`或`.post()`方法定義的。所有路徑都是使用字符串定義的(我們不使用字符串模式或正則表達式)。作用於某些特定資源(如書籍)的路由,則使用路徑參數從 URL 中獲取對象標識 id。 -

處理程序函數,都是從我們在上一節中,創建的控制器模塊導入的。

+處理程序函數,都是從我們在上一節中,創建的控制器模塊導入的。 -

更新 index 路由模組

+### 更新 index 路由模組 -

我們已經設置了所有新路由,但我們仍然有一個到原始頁面的路由。讓我們將其重定向,到我們在路徑'/ catalog' 創建的新索引頁面。

+我們已經設置了所有新路由,但我們仍然有一個到原始頁面的路由。讓我們將其重定向,到我們在路徑'/ catalog' 創建的新索引頁面。 -

打開/routes/index.js並使用下面的函數,替換現有路由。

+打開**/routes/index.js**並使用下面的函數,替換現有路由。 -
// GET home page.
+```js
+// GET home page.
 router.get('/', function(req, res) {
   res.redirect('/catalog');
-});
+}); +``` -
-

備註: 這是我們第一次使用redirect()響應方法。這會重定向到指定的頁面,默認情況下會發送HTTP狀態代碼“302 Found”。您可以根據需要,更改返回的狀態代碼,並提供絕對路徑或相對路徑。

-
+> **備註:** 這是我們第一次使用[redirect()](https://expressjs.com/en/4x/api.html#res.redirect)響應方法。這會重定向到指定的頁面,默認情況下會發送 HTTP 狀態代碼“302 Found”。您可以根據需要,更改返回的狀態代碼,並提供絕對路徑或相對路徑。 -

更新app.js

+### 更新 app.js -

最後一步,是將路由,添加到中間件鏈。我們在app.js這樣做。

+最後一步,是將路由,添加到中間件鏈。我們在`app.js`這樣做。 -

打開app.js,並要求其他路由下方的目錄路由(添加下面顯示的第三行,在其他兩個路由下面):

+打開**app.js**,並要求其他路由下方的目錄路由(添加下面顯示的第三行,在其他兩個路由下面): -
var indexRouter = require('./routes/index');
+```js
+var indexRouter = require('./routes/index');
 var usersRouter = require('./routes/users');
-var catalogRouter = require('./routes/catalog');  //Import routes for "catalog" area of site
+var catalogRouter = require('./routes/catalog'); //Import routes for "catalog" area of site +``` -

接下來,將目錄路由,添加到其他路由下面的中間件堆棧(添加下面顯示的第三行,在其他兩行下面):

+接下來,將目錄路由,添加到其他路由下面的中間件堆棧(添加下面顯示的第三行,在其他兩行下面): -
app.use('/', indexRouter);
+```js
+app.use('/', indexRouter);
 app.use('/users', usersRouter);
-app.use('/catalog', catalogRouter);  // Add catalog routes to middleware chain.
+app.use('/catalog', catalogRouter); // Add catalog routes to middleware chain. +``` + +> **備註:** 我們已在路徑`'/catalog'`中添加了目錄模塊。它預先添加到目錄模塊中定義的所有路徑。例如,要訪問書本列表,URL 將為:`/catalog/books/`。 -
-

備註: 我們已在路徑'/catalog'中添加了目錄模塊。它預先添加到目錄模塊中定義的所有路徑。例如,要訪問書本列表,URL將為:/catalog/books/

-
+就是這樣。現在應該為我們最終在 LocalLibrary 網站上支持的所有 URL,啟用路由和框架功能。 -

就是這樣。現在應該為我們最終在LocalLibrary 網站上支持的所有URL,啟用路由和框架功能。

+### 測試路由 -

測試路由

+要測試路由,首先使用您通常的方法啟動網站 -

要測試路由,首先使用您通常的方法啟動網站

+- 預設方法 -
    -
  • 預設方法 -
    // Windows
    -SET DEBUG=express-locallibrary-tutorial:* & npm start
    +  ```bash
    +  // Windows
    +  SET DEBUG=express-locallibrary-tutorial:* & npm start
     
    -// macOS or Linux
    -DEBUG=express-locallibrary-tutorial:* npm start
    -
    -
  • -
  • 如果您先前設置了nodemon ,則可以使用: -
    // Windows
    -SET DEBUG=express-locallibrary-tutorial:* & npm run devstart
    +  // macOS or Linux
    +  DEBUG=express-locallibrary-tutorial:* npm start
    +  ```
     
    -// macOS or Linux
    -DEBUG=express-locallibrary-tutorial:* npm run devstart
    -
    -
  • -
+- 如果您先前設置了[nodemon](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website) ,則可以使用: -

然後瀏覽一些上面的LocalLibrary URL,並驗證您沒有收到錯誤頁面(HTTP 404)。為方便起見,下面列出了一小組網址:

+ ```plain + // Windows + SET DEBUG=express-locallibrary-tutorial:* & npm run devstart - + // macOS or Linux + DEBUG=express-locallibrary-tutorial:* npm run devstart + ``` -

總結

+然後瀏覽一些上面的 LocalLibrary URL,並驗證您沒有收到錯誤頁面(HTTP 404)。為方便起見,下面列出了一小組網址: -

我們現在為網站創建了所有的路由,在稍後的教程,我們可以將實作完成的代碼,填入到空殼控制器函式。以這樣的方式,我們學到了許多關於Express 路由的基本信息,以及一些組織路由和控制器的方式。

+- +- +- +- +- +- +- [http://localhost:3000/catalog/book/5846437593935e2f8c2aa226](http://localhost:3000/catalog/book/5846437593935e2f8c2aa226/) +- -

下一篇文章,我們將使用視圖(模板) 和存在模型裡的信息,為網站創建一個合適的歡迎頁面。

+## 總結 -

參閱

+我們現在為網站創建了所有的路由,在稍後的教程,我們可以將實作完成的代碼,填入到空殼控制器函式。以這樣的方式,我們學到了許多關於 Express 路由的基本信息,以及一些組織路由和控制器的方式。 - +下一篇文章,我們將使用視圖(模板) 和存在模型裡的信息,為網站創建一個合適的歡迎頁面。 -

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs")}}

+## 參閱 -

+- [Basic routing](http://expressjs.com/en/starter/basic-routing.html) (Express docs) +- [Routing guide](http://expressjs.com/en/guide/routing.html) (Express docs) -

本教程連結

+{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs")}} - +## 本教程連結 -

+- [Express/Node introduction](/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction) +- [Setting up a Node (Express) development environment](/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment) +- [Express Tutorial: The Local Library website](/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website) +- [Express Tutorial Part 2: Creating a skeleton website](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website) +- [Express Tutorial Part 3: Using a Database (with Mongoose)](/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose) +- [Express Tutorial Part 4: Routes and controllers](/en-US/docs/Learn/Server-side/Express_Nodejs/routes) +- [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms) +- [Express Tutorial Part 7: Deploying to production](/en-US/docs/Learn/Server-side/Express_Nodejs/deployment) diff --git a/files/zh-tw/learn/server-side/express_nodejs/skeleton_website/index.md b/files/zh-tw/learn/server-side/express_nodejs/skeleton_website/index.md index 7e00bd84cbe4a3..3ae25095dcf7b9 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/skeleton_website/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/skeleton_website/index.md @@ -3,44 +3,50 @@ title: 'Express 教學 2: 創建一個骨架網站' slug: Learn/Server-side/Express_Nodejs/skeleton_website translation_of: Learn/Server-side/Express_Nodejs/skeleton_website --- -
{{LearnSidebar}}
+{{LearnSidebar}} -

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs")}}

+{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs")}} -

Express 教程的第二篇文章,演示如何創建一個 "骨架" 網站項目,你可以接著在裡面加入網站特定的路由、模板/視圖、和數据庫調用。

+在 [Express 教程](/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website)的第二篇文章,演示如何創建一個 "骨架" 網站項目,你可以接著在裡面加入網站特定的路由、模板/視圖、和數据庫調用。 - - - - - - - - - - + + + + + + + + + +
前置條件:架設一個Node 開發環境。回顧Express 教程。
目標:能夠使用Express 應用產生器,創建自己新的網頁項目。
前置條件: + 架設一個Node 開發環境。回顧Express 教程。 +
目標:能夠使用Express 應用產生器,創建自己新的網頁項目。
-

概覽

+## 概覽 -

本文演示如何使用 Express 應用產生器 工具,創建一個 "骨架" 網站,然後您可以使用特定於站點的路由,視圖/模板和數據庫調用來填充它們。在這個教程,我們將使用該工具,為我們的本地圖書館網站創建框架,我們稍後將添加該網站所需的所有其他代碼。該過程非常簡單,只需要在命令行上,用新項目名稱調用生成器,還可以指定站點的模板引擎和 CSS 生成器。

+本文演示如何使用 [Express 應用產生器](https://expressjs.com/en/starter/generator.html) 工具,創建一個 "骨架" 網站,然後您可以使用特定於站點的路由,視圖/模板和數據庫調用來填充它們。在這個教程,我們將使用該工具,為我們的本地圖書館網站創建框架,我們稍後將添加該網站所需的所有其他代碼。該過程非常簡單,只需要在命令行上,用新項目名稱調用生成器,還可以指定站點的模板引擎和 CSS 生成器。 -

以下部分向您展示如何調用應用程序生成器,並提供關於視圖或CSS的不同選項的一些解釋。我們還將解釋骨架網站的結構。最後,我們將展示如何運行網站,來驗證它是否有效。

+以下部分向您展示如何調用應用程序生成器,並提供關於視圖或 CSS 的不同選項的一些解釋。我們還將解釋骨架網站的結構。最後,我們將展示如何運行網站,來驗證它是否有效。 -
-

備註: Express Application Generator並非 Express 應用程序的唯一生成器,生成的項目不是構建文件和目錄的唯一可行方式。然而,生成的網站具有易於擴展和理解的模塊化結構。有關最小 Express 應用程序的信息,請參閱 Hello world 示例(Express docs)。

-
+> **備註:** Express Application Generator 並非 Express 應用程序的唯一生成器,生成的項目不是構建文件和目錄的唯一可行方式。然而,生成的網站具有易於擴展和理解的模塊化結構。有關最小 Express 應用程序的信息,請參閱 [Hello world ](https://expressjs.com/en/starter/hello-world.html)示例(Express docs)。 -

使用應用產生器

+## 使用應用產生器 -

您應該已經安裝了生成器,作為設置 Node 開發環境的一部分。作為快速提醒,您可以使用 NPM 軟件包管理器,在整個站點安裝生成器工具,如下所示:

+您應該已經安裝了生成器,作為設置 Node 開發環境的一部分。作為快速提醒,您可以使用 NPM 軟件包管理器,在整個站點安裝生成器工具,如下所示: -
npm install express-generator -g
+```bash +npm install express-generator -g +``` -

生成器有許多選項,您可以使用--help(或-h)命令,在命令行上查看它們:

+生成器有許多選項,您可以使用`--help`(或`-h`)命令,在命令行上查看它們: -
> express --help
+```bash
+> express --help
 
   Usage: express [options] [dir]
 
@@ -52,84 +58,76 @@ translation_of: Learn/Server-side/Express_Nodejs/skeleton_website
         --pug            add pug engine support
         --hbs            add handlebars engine support
     -H, --hogan          add hogan.js engine support
-    -v, --view <engine>  add view <engine> support (ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)
-    -c, --css <engine>   add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
+    -v, --view   add view  support (ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade)
+    -c, --css    add stylesheet  support (less|stylus|compass|sass) (defaults to plain css)
         --git            add .gitignore
     -f, --force          force on non-empty directory
-
+``` -

您可以使用 Jade 視圖引擎和純 CSS 來指定 express,以在當前目錄中創建項目(如果指定目錄名,則項目將創建在具有該名稱的子文件夾中)。

+您可以使用 Jade 視圖引擎和純 CSS 來指定 `express`,以在當前目錄中創建項目(如果指定目錄名,則項目將創建在具有該名稱的子文件夾中)。 -
express
+```bash +express +``` -

您還可以使用--view選擇視圖(模板)引擎,並且/或者使用--css選擇 CSS 生成引擎。

+您還可以使用`--view`選擇視圖(模板)引擎,並且/或者使用`--css`選擇 CSS 生成引擎。 -
-

備註: 選擇模板引擎的其他選項(例如 --hogan, --ejs, --hbs等)已被棄用。請用 --view (或 -v)!

-
+> **備註:** 選擇模板引擎的其他選項(例如 `--hogan`, `--ejs`, `--hbs`等)已被棄用。請用 `--view` (或 `-v`)! -

我應該用哪個視圖引擎?

+### 我應該用哪個視圖引擎? -

Express Application Generator 允許您配置許多流行的視圖/模板引擎,包括 EJS, Hbs, Pug (Jade), Twig, 和 Vash,但如果您沒有指定視圖選項,它會默認選擇Jade。 Express 本身也可以支持大量其他模板語言,是「開箱即可使用」的。

+Express Application Generator 允許您配置許多流行的視圖/模板引擎,包括 [EJS](https://www.npmjs.com/package/ejs), [Hbs](http://github.com/donpark/hbs), [Pug](https://pugjs.org/api/getting-started.html) (Jade), [Twig](https://www.npmjs.com/package/twig), 和 [Vash](https://www.npmjs.com/package/vash),但如果您沒有指定視圖選項,它會默認選擇 Jade。 Express 本身也可以支持大量其他模板語言,是「[開箱即可使用](https://github.com/expressjs/express/wiki#template-engines)」的。 -
-

備註: 如果要使用生成器不支持的模板引擎,請參閱在Express中使用模板引擎(Express文檔),並參閱目標視圖引擎的文檔。

-
+> **備註:** 如果要使用生成器不支持的模板引擎,請參閱[在 Express 中使用模板引擎(Express 文檔)](https://expressjs.com/en/guide/using-template-engines.html),並參閱目標視圖引擎的文檔。 -

一般來說,您應該選擇一種「可以提供您所需的所有功能」的模板引擎,
- 並使您能夠儘早提高生產力 - 換句話說,就像您選擇其他組件一樣!比較模板引擎時需要考慮的一些事項如下:

+一般來說,您應該選擇一種「可以提供您所需的所有功能」的模板引擎, +並使您能夠儘早提高生產力 - 換句話說,就像您選擇其他組件一樣!比較模板引擎時需要考慮的一些事項如下: -
    -
  • 具備生產力之前所需要花費的時間 — 如果您的團隊已經有模板語言的經驗,那麼使用該語言可能會更快地提高生產力。如果不是,那麼你應該考慮候選模板引擎的相對學習曲線(即以投入時間與得到的生產力為XY座標所繪製的曲線)。
  • -
  • 人氣和活躍 — 回顧引擎的普及程度以及它是否擁有活躍的社區。當您在網站的生命週期中遇到問題時,能夠獲得對引擎的支持非常重要。
  • -
  • 風格 — 某些模板引擎使用特定的標記,來指示在 “普通” HTML內插入的內容,而其他模板引擎使用不同的語法(例如,使用縮進和區塊名稱)構造HTML。
  • -
  • 性能/渲染時間。
  • -
  • 特點 — 你應該考慮你看中的引擎是否具有以下功能: -
      -
    • 佈局繼承:允許您定義基本模板,然後 “繼承” 它的一部分,使特定頁面可以有不同的呈現。比起通過包含大量必需組件,或每次從頭開始構建模板,這通常是更好的方式。
    • -
    • "Include" 支持:允許您通過包含其他模板,來構建模板。
    • -
    • 簡潔的變量和循環控制語法。
    • -
    • 能夠在模板級別過濾變量值(例如,將變量設置為大寫,或格式化日期值)。
    • -
    • 能夠生成HTML以外的輸出格式(例如 JSON 或 XML)。
    • -
    • 支持異步操作和流媒體。
    • -
    • 可以在客戶端和服務器上使用。如果可以在客戶端上使用模板引擎,則允許提供數據,並有可能在客戶端完成所有渲染,或大部分渲染。
    • -
    -
  • -
+- 具備生產力之前所需要花費的時間 — 如果您的團隊已經有模板語言的經驗,那麼使用該語言可能會更快地提高生產力。如果不是,那麼你應該考慮候選模板引擎的相對學習曲線(即以投入時間與得到的生產力為 XY 座標所繪製的曲線)。 +- 人氣和活躍 — 回顧引擎的普及程度以及它是否擁有活躍的社區。當您在網站的生命週期中遇到問題時,能夠獲得對引擎的支持非常重要。 +- 風格 — 某些模板引擎使用特定的標記,來指示在 “普通” HTML 內插入的內容,而其他模板引擎使用不同的語法(例如,使用縮進和區塊名稱)構造 HTML。 +- 性能/渲染時間。 +- 特點 — 你應該考慮你看中的引擎是否具有以下功能: -
-

備註: 互聯網上有許多資源,可幫助您比較不同的視圖/模板引擎選擇!

-
+ - 佈局繼承:允許您定義基本模板,然後 “繼承” 它的一部分,使特定頁面可以有不同的呈現。比起通過包含大量必需組件,或每次從頭開始構建模板,這通常是更好的方式。 + - "Include" 支持:允許您通過包含其他模板,來構建模板。 + - 簡潔的變量和循環控制語法。 + - 能夠在模板級別過濾變量值(例如,將變量設置為大寫,或格式化日期值)。 + - 能夠生成 HTML 以外的輸出格式(例如 JSON 或 XML)。 + - 支持異步操作和流媒體。 + - 可以在客戶端和服務器上使用。如果可以在客戶端上使用模板引擎,則允許提供數據,並有可能在客戶端完成所有渲染,或大部分渲染。 -

對於這個項目,我們將使用 Pug 模板引擎(這是最近更名的 Jade 引擎),因為這是最流行的 Express / JavaScript 模板語言之一,並且應用發生器支持開箱即用。

+> **備註:** 互聯網上有許多資源,可幫助您比較不同的視圖/模板引擎選擇! -

我應該用哪個CSS樣式引擎?

+對於這個項目,我們將使用 [Pug](https://pugjs.org/api/getting-started.html) 模板引擎(這是最近更名的 Jade 引擎),因為這是最流行的 Express / JavaScript 模板語言之一,並且應用發生器支持開箱即用。 -

Express 應用生成器允許您創建一個項目,並配置最常見的 CSS 樣式表引擎:LESS, SASS, Compass, Stylus

+### 我應該用哪個 CSS 樣式引擎? -
-

備註: CSS有一些限制,使某些任務變得困難。 CSS 樣式表引擎允許您使用更強大的語法來定義您的 CSS,然後將定義編譯為純粹的舊式 CSS,以供瀏覽器使用。

-
+Express 應用生成器允許您創建一個項目,並配置最常見的 CSS 樣式表引擎:[LESS](http://lesscss.org/), [SASS](http://sass-lang.com/), [Compass](http://compass-style.org/), [Stylus](http://stylus-lang.com/)。 -

與模板引擎一樣,您應該使用樣式表引擎,這樣可以讓您的團隊獲得最高生產力。對於這個項目,我們將使用普通的 CSS(默認值),因為我們的 CSS 要求不夠複雜,沒有必要使用其他任何東西。

+> **備註:** CSS 有一些限制,使某些任務變得困難。 CSS 樣式表引擎允許您使用更強大的語法來定義您的 CSS,然後將定義編譯為純粹的舊式 CSS,以供瀏覽器使用。 -

我應該用哪個數據庫?

+與模板引擎一樣,您應該使用樣式表引擎,這樣可以讓您的團隊獲得最高生產力。對於這個項目,我們將使用普通的 CSS(默認值),因為我們的 CSS 要求不夠複雜,沒有必要使用其他任何東西。 -

生成的代碼不使用/包含任何數據庫。 Express 應用程序可以使用 Node支持的任何數據庫機制(Express 本身並未針對數據庫管理,定義任何特定的附加行為/要求)。
- 我們將在後面的文章中,討論如何與數據庫集成。

+### 我應該用哪個數據庫? -

創建項目

+生成的代碼不使用/包含任何數據庫。 Express 應用程序可以使用 Node 支持的任何[數據庫機制](https://expressjs.com/en/guide/database-integration.html)(Express 本身並未針對數據庫管理,定義任何特定的附加行為/要求)。 +我們將在後面的文章中,討論如何與數據庫集成。 -

對於我們要構建的示例 Local Library 應用程序,我們將使用 Pug 模板庫,創建一個名為 express-locallibrary-tutorial 的項目,並且不使用 CSS樣式表引擎。

+## 創建項目 -

首先到要創建項目的位置,然後在命令提示符下,運行 Express 應用生成器,如下所示:

+對於我們要構建的示例 Local Library 應用程序,我們將使用 Pug 模板庫,創建一個名為 express-locallibrary-tutorial 的項目,並且不使用 CSS 樣式表引擎。 -
express express-locallibrary-tutorial --view=pug
-
+首先到要創建項目的位置,然後在命令提示符下,運行 Express 應用生成器,如下所示: -

成器將創建(並列出)項目的文件。

+```bash +express express-locallibrary-tutorial --view=pug +``` -
   create : express-locallibrary-tutorial
+成器將創建(並列出)項目的文件。
+
+```bash
+   create : express-locallibrary-tutorial
    create : express-locallibrary-tutorial/package.json
    create : express-locallibrary-tutorial/app.js
    create : express-locallibrary-tutorial/public/images
@@ -148,137 +146,144 @@ translation_of: Learn/Server-side/Express_Nodejs/skeleton_website
    create : express-locallibrary-tutorial/bin/www
 
    install dependencies:
-     > cd express-locallibrary-tutorial && npm install
+     > cd express-locallibrary-tutorial && npm install
 
    run the app:
-     > SET DEBUG=express-locallibrary-tutorial:* & npm start
+ > SET DEBUG=express-locallibrary-tutorial:* & npm start +``` + +在輸出結束時,生成器提供關於「如何安裝依賴關係」的指示信息(如 **package.json** 文件中所列),以及如何運行應用程序(上述說明適用於 Windows;在 Linux / macOS 上,它們會略有不同)。 + +## 運行骨架網站 + +在這一時間點上,我們有一個完整的骨架項目。該網站實際上並沒有做太多工作,但運行它,能夠展示它是如何工作的。 -

在輸出結束時,生成器提供關於「如何安裝依賴關係」的指示信息(如 package.json 文件中所列),以及如何運行應用程序(上述說明適用於 Windows;在 Linux / macOS上,它們會略有不同)。

+1. 首先安裝依賴項(`install`安裝命令,將獲取項目的 **package.json** 文件中列出的所有依賴項包)。 -

運行骨架網站

+ ```bash + cd express-locallibrary-tutorial + npm install + ``` -

在這一時間點上,我們有一個完整的骨架項目。該網站實際上並沒有做太多工作,但運行它,能夠展示它是如何工作的。

+2. 然後運行該應用程序。 -
    -
  1. 首先安裝依賴項(install安裝命令,將獲取項目的 package.json 文件中列出的所有依賴項包)。 + - 在 Windows 上,使用此命令: -
    cd express-locallibrary-tutorial
    -npm install
    -
  2. -
  3. 然後運行該應用程序。 -
      -
    • 在Windows上,使用此命令: -
      SET DEBUG=express-locallibrary-tutorial:* & npm start
      -
    • -
    • 在macOS 或 Linux,使用此命令: -
      DEBUG=express-locallibrary-tutorial:* npm start
      -
      -
    • -
    -
  4. -
  5. 然後在瀏覽器中加載 http://localhost:3000/ ,以訪問該應用程序。
  6. -
+ ```bash + SET DEBUG=express-locallibrary-tutorial:* & npm start + ``` -

你應該會看到一個瀏覽器頁面,就像這樣:

+ - 在 macOS 或 Linux,使用此命令: -

Browser for default Express app generator website

+ ```bash + DEBUG=express-locallibrary-tutorial:* npm start + ``` -

你有一個能工作的 Express 應用了,讓它在 http://localhost:3000/ 服務。

+3. 然後在瀏覽器中加載 ,以訪問該應用程序。 -
-

備註: 您也可以使用 npm start命令啟動應用程序。如下圖所示,指定 DEBUG 變量可啟用控制台日誌記錄/調試。例如,當你訪問上面的頁面時,你會看到像這樣的調試輸出:

+你應該會看到一個瀏覽器頁面,就像這樣: -
>SET DEBUG=express-locallibrary-tutorial:* & npm start
+![Browser for default Express app generator website](expressgeneratorskeletonwebsite.png)
 
-> express-locallibrary-tutorial@0.0.0 start D:\express-locallibrary-tutorial
-> node ./bin/www
+你有一個能工作的 Express 應用了,讓它在 [http://localhost:3000/ ](http://localhost:3000/)服務。
 
-  express-locallibrary-tutorial:server Listening on port 3000 +0ms
-GET / 200 288.474 ms - 170
-GET /stylesheets/style.css 200 5.799 ms - 111
-GET /favicon.ico 404 34.134 ms - 1335
-
+> **備註:** 您也可以使用 `npm start`命令啟動應用程序。如下圖所示,指定 DEBUG 變量可啟用控制台日誌記錄/調試。例如,當你訪問上面的頁面時,你會看到像這樣的調試輸出: +> +> ```bash +> >SET DEBUG=express-locallibrary-tutorial:* & npm start +> +> > express-locallibrary-tutorial@0.0.0 start D:\express-locallibrary-tutorial +> > node ./bin/www +> +> express-locallibrary-tutorial:server Listening on port 3000 +0ms +> GET / 200 288.474 ms - 170 +> GET /stylesheets/style.css 200 5.799 ms - 111 +> GET /favicon.ico 404 34.134 ms - 1335 +> ``` -

讓伺服器在檔案更改時重新啟動

+## 讓伺服器在檔案更改時重新啟動 -

在您重新啟動服務器之前,您對 Express 網站所做的任何更改,目前都不可見。每次進行更改時,必須停止並重新啟動服務器,很快變得非常煩人,因此值得花時間使服務器在需要時,自動重新啟動。

+在您重新啟動服務器之前,您對 Express 網站所做的任何更改,目前都不可見。每次進行更改時,必須停止並重新啟動服務器,很快變得非常煩人,因此值得花時間使服務器在需要時,自動重新啟動。 -

這種工具中,最簡單的之一就是 nodemon。這通常是全局安裝的(因為它是一個“工具”),但在這裡,我們將在本地安裝和使用它,作為開發人員依賴項,以便任何使用該項目的開發人員,在安裝應用程序時自動獲取它。在骨架項目的根目錄中,使用以下命令:

+這種工具中,最簡單的之一就是 [nodemon](https://github.com/remy/nodemon)。這通常是全局安裝的(因為它是一個“工具”),但在這裡,我們將在本地安裝和使用它,作為開發人員依賴項,以便任何使用該項目的開發人員,在安裝應用程序時自動獲取它。在骨架項目的根目錄中,使用以下命令: -
npm install --save-dev nodemon
+```bash +npm install --save-dev nodemon +``` -

如果您打開項目的 package.json 文件,您現在將看到一個具有此依賴關係的新區段:

+如果您打開項目的 **package.json** 文件,您現在將看到一個具有此依賴關係的新區段: -
  "devDependencies": {
+```json
+  "devDependencies": {
     "nodemon": "^1.14.11"
   }
-
+``` -

由於該工具沒有全局安裝,我們無法從命令行啟動它(除非我們將其添加到路徑中),但是我們可以從 NPM 腳本中調用它,因為 NPM 知道所有關於安裝的軟件包的信息。找到你的 package.json 的腳本 scripts 區塊。我們更新 scripts 區塊,最初的一行,以"start"開頭,在該行的末尾添加逗號,並添加 "devstart" 開頭的一行,如下所示:

+由於該工具沒有全局安裝,我們無法從命令行啟動它(除非我們將其添加到路徑中),但是我們可以從 NPM 腳本中調用它,因為 NPM 知道所有關於安裝的軟件包的信息。找到你的 package.json 的腳本 scripts 區塊。我們更新 `scripts `區塊,最初的一行,以"`start`"開頭,在該行的末尾添加逗號,並添加 "`devstart`" 開頭的一行,如下所示: -
  "scripts": {
-    "start": "node ./bin/www",
-    "devstart": "nodemon ./bin/www"
+```json
+  "scripts": {
+    "start": "node ./bin/www",
+    "devstart": "nodemon ./bin/www"
   },
-
+``` +現在我們可以用與前面幾乎完全相同的方式,啟動服務器,但使用指定的 devstart 命令: +- 在 Windows,使用此命令: -

現在我們可以用與前面幾乎完全相同的方式,啟動服務器,但使用指定的 devstart 命令:

+ ```bash + SET DEBUG=express-locallibrary-tutorial:* & npm run devstart + ``` -
    -
  • 在 Windows,使用此命令: -
    SET DEBUG=express-locallibrary-tutorial:* & npm run devstart
    -
  • -
  • 在 macOS or Linux,使用此命令: -
    DEBUG=express-locallibrary-tutorial:* npm run devstart
    -
    -
  • -
+- 在 macOS or Linux,使用此命令: -
-

備註: 現在,如果您編輯項目中的任何文件,服務器將重新啟動(或者您可以隨時在命令提示符下,鍵入rs來重新啟動它)。您仍需要重新加載瀏覽器,以刷新頁面。

+ ```bash + DEBUG=express-locallibrary-tutorial:* npm run devstart + ``` -

我們現在必須調用“npm run <scriptname>”而不是 npm start,因為“start”實際上是映射到指定腳本的 NPM 命令。我們可以在啟動腳本中替換該命令,但我們只想在開發期間使用 nodemon,因此創建新的腳本命令是有意義的。

-
+> **備註:** 現在,如果您編輯項目中的任何文件,服務器將重新啟動(或者您可以隨時在命令提示符下,鍵入`rs`來重新啟動它)。您仍需要重新加載瀏覽器,以刷新頁面。 +> +> 我們現在必須調用“`npm run `”而不是 `npm start`,因為“start”實際上是映射到指定腳本的 NPM 命令。我們可以在啟動腳本中替換該命令,但我們只想在開發期間使用 nodemon,因此創建新的腳本命令是有意義的。 -

從產生器得到的項目

+## 從產生器得到的項目 -

現在我們來看看我們剛剛創建的項目。

+現在我們來看看我們剛剛創建的項目。 -

目錄結構

+### 目錄結構 -

從產生器得到的生成項目,現在已經安裝了依賴項,具有以下文件結構 (不帶前綴 “/” 的項目,表示文件)。 package.json 文件定義了應用程序依賴項,和其他信息。它還定義了一個啟動腳本,它將調用應用程序入口點 JavaScript 文件 /bin/www。這設置了一些應用程序的錯誤處理,然後加載 app.js ,來完成剩下的工作。應用程序路徑,存儲在 /routes 目錄下的單獨模塊中。模板存儲在 /views 目錄下。

+從產生器得到的生成項目,現在已經安裝了依賴項,具有以下文件結構 (**不帶**前綴 “/” 的項目,表示文件)。 **package.json** 文件定義了應用程序依賴項,和其他信息。它還定義了一個啟動腳本,它將調用應用程序入口點 JavaScript 文件 **/bin/www**。這設置了一些應用程序的錯誤處理,然後加載 **app.js** ,來完成剩下的工作。應用程序路徑,存儲在 **/routes** 目錄下的單獨模塊中。模板存儲在 **/views** 目錄下。 -
/express-locallibrary-tutorial
-    app.js
+```plain
+/express-locallibrary-tutorial
+    app.js
     /bin
-        www
-    package.json
+        www
+    package.json
     /node_modules
         [about 4,500 subdirectories and files]
     /public
         /images
         /javascripts
         /stylesheets
-            style.css
+            style.css
     /routes
-        index.js
-        users.js
+        index.js
+        users.js
     /views
-        error.pug
-        index.pug
-        layout.pug
+        error.pug
+        index.pug
+        layout.pug
+```
 
-
+以下各節將詳細介紹這些文件。 -

以下各節將詳細介紹這些文件。

+### package.json -

package.json

+**package.json** 文件定義了應用程序依賴關係,和其他訊息: -

package.json 文件定義了應用程序依賴關係,和其他訊息:

- -
{
+```json
+{
   "name": "express-locallibrary-tutorial",
   "version": "0.0.0",
   "private": true,
@@ -299,110 +304,111 @@ GET /favicon.ico 404 34.134 ms - 1335
"nodemon": "^1.14.11" } } - +``` -

依賴關係包括 express 套件,和我們所選視圖引擎(pug)的套件。另外,我們還有以下的套件,在許多 Web 應用程序中很有用:

+依賴關係包括 express 套件,和我們所選視圖引擎(pug)的套件。另外,我們還有以下的套件,在許多 Web 應用程序中很有用: -
    -
  • body-parser: 這解析傳入HTTP請求的正文 body 部分,並更容易提取包含信息的不同部分。例如,您可以使用它來讀取 POST 參數。
  • -
  • cookie-parser: 用於解析 cookie header 並填充 req.cookies(本質上提供了訪問 cookie 信息的便捷方法)。
  • -
  • debug: 一個小型 node 調試程序,仿照 node 核心的調試技術建立的。
  • -
  • morgan: 搭配 node 使用的 HTTP 請求記錄器中間層軟件。
  • -
  • serve-favicon: 用於提供收藏圖標 favicon 的 node 中間層軟件(這是用於表示瀏覽器選項卡、書籤等網站內的圖標)。
  • -
+- [body-parser](https://www.npmjs.com/package/body-parser): 這解析傳入 HTTP 請求的正文 body 部分,並更容易提取包含信息的不同部分。例如,您可以使用它來讀取 `POST` 參數。 +- [cookie-parser](https://www.npmjs.com/package/cookie-parser): 用於解析 cookie header 並填充 `req.cookies`(本質上提供了訪問 cookie 信息的便捷方法)。 +- [debug](https://www.npmjs.com/package/debug): 一個小型 node 調試程序,仿照 node 核心的調試技術建立的。 +- [morgan](https://www.npmjs.com/package/morgan): 搭配 node 使用的 HTTP 請求記錄器中間層軟件。 +- [serve-favicon](https://www.npmjs.com/package/serve-favicon): 用於提供收藏圖標 [favicon](https://en.wikipedia.org/wiki/Favicon) 的 node 中間層軟件(這是用於表示瀏覽器選項卡、書籤等網站內的圖標)。 -

腳本部分,定義了一個“開始” "start" 腳本,當我們調用 npm start 來啟動服務器時,這就是我們所調用的腳本。從腳本定義中,您可以看到這實際上用 node 啟動了 JavaScript 文件 ./bin/www。它還定義了一個“devstart” 腳本,我們在調用 npm run devstart 時調用它。這將啟動相同的 ./bin/www 文件,但使用 nodemon 調用而不是 node 。

+腳本部分,定義了一個“開始” "start" 腳本,當我們調用 `npm start` 來啟動服務器時,這就是我們所調用的腳本。從腳本定義中,您可以看到這實際上用 node 啟動了 JavaScript 文件 **./bin/www**。它還定義了一個“devstart” 腳本,我們在調用 `npm run devstart `時調用它。這將啟動相同的 **./bin/www** 文件,但使用 nodemon 調用而不是 node 。 -
  "scripts": {
+```json
+  "scripts": {
     "start": "node ./bin/www",
     "devstart": "nodemon ./bin/www"
   },
-
+``` -

www 文件

+### www 文件 -

文件 /bin/www 是應用程序入口點!它做的第一件事是 require() “真正的” 應用程序入口點(即項目根目錄中的 app.js ),app.js 會設置並返回 express()應用程序的對象。

+文件 **/bin/www** 是應用程序入口點!它做的第一件事是 `require()` “真正的” 應用程序入口點(即項目根目錄中的 app.js ),**app.js** 會設置並返回 [`express()`](http://expressjs.com/en/api.html)應用程序的對象。 -
#!/usr/bin/env node
+```js
+#!/usr/bin/env node
 
 /**
  * Module dependencies.
  */
 
-var app = require('../app');
-
+var app = require('../app'); +``` -
-

備註: require() 是一個全局 node 函數,用於將模塊導入當前文件。這裡我們使用相對路徑指定 app.js 模塊,並省略可選的(.js)文件擴展名。

-
+> **備註:** `require()` 是一個全局 node 函數,用於將模塊導入當前文件。這裡我們使用相對路徑指定 **app.js** 模塊,並省略可選的(**.js**)文件擴展名。 -

此文件中的其餘代碼,將設置一個node 運行的HTTP 服務器,並將應用app 設置為特定的端口(在環境變量中定義,如果變量未定義,則定義為3000),並開始監聽和報告服務器錯誤和連接。現在你並不需要知道代碼的其他內容(這個文件中的所有內容都是 “樣板文件” ),但如果你感興趣,可以隨時查看它。

+此文件中的其餘代碼,將設置一個 node 運行的 HTTP 服務器,並將應用 app 設置為特定的端口(在環境變量中定義,如果變量未定義,則定義為 3000),並開始監聽和報告服務器錯誤和連接。現在你並不需要知道代碼的其他內容(這個文件中的所有內容都是 “樣板文件” ),但如果你感興趣,可以隨時查看它。 -

app.js

+### app.js -

此文件創建一個 express 應用程序對象(按傳統命名為 app),使用各種設置和中間件,以設置應用程序,然後從模塊導出應用程序。下面的代碼只顯示了文件的一部分,創建和導出應用程序對象的部分:

+此文件創建一個 `express` 應用程序對象(按傳統命名為 `app`),使用各種設置和中間件,以設置應用程序,然後從模塊導出應用程序。下面的代碼只顯示了文件的一部分,創建和導出應用程序對象的部分: -
var express = require('express');
+```js
+var express = require('express');
 var app = express();
 ...
-module.exports = app;
-
+module.exports = app; +``` -

回到上面的 www 入口點文件,它是在導入該文件時,提供給調用者的這個 module.exports 對象。

+回到上面的 **www** 入口點文件,它是在導入該文件時,提供給調用者的這個 `module.exports` 對象。 -

讓我們詳細了解 app.js 文件。首先,我們使用 require()將一些有用的 node 庫導入到文件中,其中包括我們先前使用 NPM 為應用程序下載的 express,serve-favicon,morgan,cookie-parser 和body-parser;和path 庫,它是解析文件和目錄路徑的核心 node 庫。

+讓我們詳細了解 **app.js** 文件。首先,我們使用 `require()`將一些有用的 node 庫導入到文件中,其中包括我們先前使用 NPM 為應用程序下載的 express,serve-favicon,morgan,cookie-parser 和 body-parser;和 path 庫,它是解析文件和目錄路徑的核心 node 庫。 -
var express = require('express');
+```js
+var express = require('express');
 var path = require('path');
 var favicon = require('serve-favicon');
 var logger = require('morgan');
 var cookieParser = require('cookie-parser');
 var bodyParser = require('body-parser');
-
+``` -

然後我們用 require()導入來自我們的路由目錄的模塊。這些模塊/文件包含用於處理特定的相關“路由”集合(URL路徑)的代碼。當我們擴展骨架應用程序,例如列出圖書館中的所有書籍時,我們將添加一個新文件,來處理與書籍相關的路由。

+然後我們用 `require()`導入來自我們的路由目錄的模塊。這些模塊/文件包含用於處理特定的相關“路由”集合(URL 路徑)的代碼。當我們擴展骨架應用程序,例如列出圖書館中的所有書籍時,我們將添加一個新文件,來處理與書籍相關的路由。 -
var indexRouter = require('./routes/index');
+```js
+var indexRouter = require('./routes/index');
 var usersRouter = require('./routes/users');
-
+``` -
-

備註: 此時我們剛剛導入了模塊;我們還沒有真正使用過它的路由(在文件的更下方一點將使用到路由)。

-
+> **備註:** 此時我們剛剛導入了模塊;我們還沒有真正使用過它的路由(在文件的更下方一點將使用到路由)。 -

接下來,我們使用導入的 express 模塊​​,創建應用程序 app 對象,然後使用它來設置視圖(模板)引擎。引擎的設置有兩個部分。首先我們設置 'views' 值,來指定模板將被存儲的文件夾(在這種情況下是子文件夾 /views)。然後我們設置 'view engine' 的值,來指定模板庫(在本例中為 “pug” )。

+接下來,我們使用導入的 express 模塊 ​​,創建應用程序 `app` 對象,然後使用它來設置視圖(模板)引擎。引擎的設置有兩個部分。首先我們設置 '`views`' 值,來指定模板將被存儲的文件夾(在這種情況下是子文件夾 **/views**)。然後我們設置 '`view engine`' 的值,來指定模板庫(在本例中為 “pug” )。 -
var app = express();
+```js
+var app = express();
 
 // view engine setup
 app.set('views', path.join(__dirname, 'views'));
 app.set('view engine', 'pug');
-
+``` -

下一組函數調用 app.use(),將中間件的庫,添加到請求處理鏈中。除了我們之前導入的第三方庫之外,我們還使用 express.static 中間件,來使 Express 提供在項目根目錄下,/public 目錄中的所有靜態文件。

+下一組函數調用 `app.use()`,將中間件的庫,添加到請求處理鏈中。除了我們之前導入的第三方庫之外,我們還使用 `express.static` 中間件,來使 Express 提供在項目根目錄下,**/public** 目錄中的所有靜態文件。 -
// uncomment after placing your favicon in /public
+```js
+// uncomment after placing your favicon in /public
 //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
 app.use(logger('dev'));
 app.use(bodyParser.json());
 app.use(bodyParser.urlencoded({ extended: false }));
 app.use(cookieParser());
-app.use(express.static(path.join(__dirname, 'public')));
-
+app.use(express.static(path.join(__dirname, 'public'))); +``` -

現在所有其他中間件都已設置完畢,我們將(先前導入的)路由處理代碼,添加到請求處理鏈中。導入的代碼,將為網站的不同部分定義特定路由:

+現在所有其他中間件都已設置完畢,我們將(先前導入的)路由處理代碼,添加到請求處理鏈中。導入的代碼,將為網站的不同部分定義特定路由: -
app.use('/', indexRouter);
+```js
+app.use('/', indexRouter);
 app.use('/users', usersRouter);
-
+``` -
-

備註: 上面指定的路徑 ('/' and '/users'),被視為定義在導入文件中的路由前綴。因此,例如,如果導入的用戶模塊 users/profile定義了路由,則可以在 /users/profile中訪問該路由。我們將在後面的文章中,詳細討論路由。

-
+> **備註:** 上面指定的路徑 ('/' and '`/users`'),被視為定義在導入文件中的路由前綴。因此,例如,如果導入的用戶模塊 **users**為`/profile`定義了路由,則可以在 `/users/profile`中訪問該路由。我們將在後面的文章中,詳細討論路由。 -

文件中的最後一個中間件,為錯誤和 HTTP 404 響應添加了處理程序方法。

+文件中的最後一個中間件,為錯誤和 HTTP 404 響應添加了處理程序方法。 -
// catch 404 and forward to error handler
+```js
+// catch 404 and forward to error handler
 app.use(function(req, res, next) {
   var err = new Error('Not Found');
   err.status = 404;
@@ -419,88 +425,82 @@ app.use(function(err, req, res, next) {
   res.status(err.status || 500);
   res.render('error');
 });
-
+``` -

Express 應用程序對象(app)現已完全完成配置。最後一步,是將其添加到模塊導出(這允許它通過 /bin/www 導入)。

+Express 應用程序對象(app)現已完全完成配置。最後一步,是將其添加到模塊導出(這允許它通過 **/bin/www** 導入)。 -
module.exports = app;
+```js +module.exports = app; +``` -

路由

+### 路由 -

路由文檔 /routes/users.js 如下所示(路由文件共享一個類似的結構,所以我們不需要也顯示 index.js)。首先加載 express 模塊​​,並使用它獲取 express.Router對象。然後它在該對像上指定一個路由,最後從模塊中導出路由器(這就是允許將文件導入到 app.js 中的路由)。

+路由文檔 **/routes/users.js** 如下所示(路由文件共享一個類似的結構,所以我們不需要也顯示 **index.js**)。首先加載 express 模塊 ​​,並使用它獲取` express.Router`對象。然後它在該對像上指定一個路由,最後從模塊中導出路由器(這就是允許將文件導入到 **app.js** 中的路由)。 -
var express = require('express');
+```js
+var express = require('express');
 var router = express.Router();
 
 /* GET users listing. */
-router.get('/', function(req, res, next) {
+router.get('/', function(req, res, next) {
   res.send('respond with a resource');
-});
+});
 
 module.exports = router;
-
+``` -

該路由定義了一個回調,只要檢測到具有正確模式的HTTP GET 請求,就會調用該回調。匹配模式是模塊導入時指定的路由('/users'),加上('/')文件中定義的任何內容。換句話說,當收到/users/的 URL 時,將使用此路由。

+該路由定義了一個回調,只要檢測到具有正確模式的 HTTP `GET` 請求,就會調用該回調。匹配模式是模塊導入時指定的路由('`/users`'),加上('`/`')文件中定義的任何內容。換句話說,當收到`/users/`的 URL 時,將使用此路由。 -
-

備註: 嘗試運行帶有 node 的服務器,並在瀏覽器中訪問以下 URL: http://localhost:3000/users/。您應該看到一條消息:'respond with a resource'。

-
+> **備註:** 嘗試運行帶有 node 的服務器,並在瀏覽器中訪問以下 URL: 。您應該看到一條消息:'respond with a resource'。 -

上面有趣的事情是,回調函數有第三個參數 'next',因此是一個中間件函數,而不是簡單的路由回調。雖然代碼當前不使用 next 參數,但如果要在'/'根路由路徑中,添加多個路由處理程序,將來可能會有用。

+上面有趣的事情是,回調函數有第三個參數 '`next`',因此是一個中間件函數,而不是簡單的路由回調。雖然代碼當前不使用 `next` 參數,但如果要在'`/`'根路由路徑中,添加多個路由處理程序,將來可能會有用。 -

視圖(模板)

+### 視圖(模板) -

視圖(模板)存儲在 /views 目錄中(如 app.js 中指定的)並且被賦予文件擴展名.pug。方法 Response.render()用於呈現指定的模板,以及在對像中傳遞的命名變量的值,然後將結果作為響應發送。在來自 /routes/index.js 的以下代碼中,您可以看到,該路由如何使用模板 "index" 傳遞模板變量 "title" ,以呈現響應。

+視圖(模板)存儲在 **/views** 目錄中(如 **app.js** 中指定的)並且被賦予文件擴展名**.pug**。方法 [`Response.render()`](http://expressjs.com/en/4x/api.html#res.render)用於呈現指定的模板,以及在對像中傳遞的命名變量的值,然後將結果作為響應發送。在來自 **/routes/index.js** 的以下代碼中,您可以看到,該路由如何使用模板 "index" 傳遞模板變量 "title" ,以呈現響應。 -
/* GET home page. */
+```js
+/* GET home page. */
 router.get('/', function(req, res) {
   res.render('index', { title: 'Express' });
 });
-
+``` -

上面路由的相應模板在下面給出(index.pug)。我們稍後會詳細討論這個語法。您現在需要知道的是,標題變量 title(值為 'Express')將插入模板中指定的位置。

+上面路由的相應模板在下面給出(**index.pug**)。我們稍後會詳細討論這個語法。您現在需要知道的是,標題變量 `title`(值為 '`Express`')將插入模板中指定的位置。 -
extends layout
+```plain
+extends layout
 
 block content
   h1= title
   p Welcome to #{title}
-
- -

挑戰自己

- -

/routes/users.js 中創建一個新路由,它將在 /users/cool/上顯示文本 “You're so cool”。通過運行服務器,並在瀏覽器中訪問 http://localhost:3000/users/cool/ 來測試它。

- -
    -
+``` -

總結

+## 挑戰自己 -

你現在為 本地圖書館 創建了一個骨架網站項目,並且用 node 驗證了它能夠運行。最重要的,你也理解了項目的結構,因此你也明白了我們需要為本地圖書館加上路由和視圖。

+在 **/routes/users.js** 中創建一個新路由,它將在 `/users/cool/`上顯示文本 “You're so cool”。通過運行服務器,並在瀏覽器中訪問 來測試它。 -

接下來我們將開始修改骨架,讓它能像一個圖書館網站一樣運作。

+## 總結 -

參閱

+你現在為 本地圖書館 創建了一個骨架網站項目,並且用 node 驗證了它能夠運行。最重要的,你也理解了項目的結構,因此你也明白了我們需要為本地圖書館加上路由和視圖。 - +接下來我們將開始修改骨架,讓它能像一個圖書館網站一樣運作。 -

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs")}}

+## 參閱 +- [Express application generator](https://expressjs.com/en/starter/generator.html) (Express docs) +- [Using template engines with Express](https://expressjs.com/en/guide/using-template-engines.html) (Express docs) +{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs")}} -

本教程連結

+## 本教程連結 - +- [Express/Node introduction](/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction) +- [Setting up a Node (Express) development environment](/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment) +- [Express Tutorial: The Local Library website](/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website) +- [Express Tutorial Part 2: Creating a skeleton website](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website) +- [Express Tutorial Part 3: Using a Database (with Mongoose)](/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose) +- [Express Tutorial Part 4: Routes and controllers](/en-US/docs/Learn/Server-side/Express_Nodejs/routes) +- [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms) +- [Express Tutorial Part 7: Deploying to production](/en-US/docs/Learn/Server-side/Express_Nodejs/deployment) diff --git a/files/zh-tw/learn/server-side/express_nodejs/tutorial_local_library_website/index.md b/files/zh-tw/learn/server-side/express_nodejs/tutorial_local_library_website/index.md index 3b7ce349f491b9..bfe5b25eeb4581 100644 --- a/files/zh-tw/learn/server-side/express_nodejs/tutorial_local_library_website/index.md +++ b/files/zh-tw/learn/server-side/express_nodejs/tutorial_local_library_website/index.md @@ -3,81 +3,81 @@ title: 'Express 教學 1: 本地圖書館網站' slug: Learn/Server-side/Express_Nodejs/Tutorial_local_library_website translation_of: Learn/Server-side/Express_Nodejs/Tutorial_local_library_website --- -
{{LearnSidebar}}
+{{LearnSidebar}}{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs")}} -
{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs")}}
- -

我們實作教程系列的第一篇文章,會說明將學到什麼東西,並提供「本地圖書館」範例網站的概述 。我們將在接下來的文章中一步一步完成這個網站。

+我們實作教程系列的第一篇文章,會說明將學到什麼東西,並提供「本地圖書館」範例網站的概述 。我們將在接下來的文章中一步一步完成這個網站。 - - - - - - - - - - + + + + + + + + + +
前置條件:閱讀 Express 介紹。 在底下的教程,你將需要 架設一個 Node 開發環境。
目標:介紹本教程的範例應用,讓讀者理解包含哪些主題。
前置條件: + 閱讀 + Express 介紹。 + 在底下的教程,你將需要 + 架設一個 Node 開發環境。 +
目標:介紹本教程的範例應用,讓讀者理解包含哪些主題。
-

概覽

+## 概覽 -

歡迎來到 MDN "本地圖書館" Express (Node) 教程,我們將開發一個網站,用於管理本地圖書館的目錄。

+歡迎來到 MDN "本地圖書館" Express (Node) 教程,我們將開發一個網站,用於管理本地圖書館的目錄。 -

本系列教程文章中,你將會:

+本系列教程文章中,你將會: -
    -
  • 使用 Express 應用產生器工具,創建一個骨架網站與應用
  • -
  • 起動和停止Node 網頁伺服器
  • -
  • 使用數据庫存放應用的數据
  • -
  • 創建路由用以要求不同的信息,創建模板 ("視圖") 以HTML的形式在瀏覽器中呈現數据
  • -
  • 使用表單
  • -
  • 部署應用到生產環境
  • -
+- 使用 _Express 應用產生器工具,創建一個骨架網站與應用_ +- 起動和停止 Node 網頁伺服器 +- 使用數据庫存放應用的數据 +- 創建路由用以要求不同的信息,創建模板 ("視圖") 以 HTML 的形式在瀏覽器中呈現數据 +- 使用表單 +- 部署應用到生產環境 -

這些主題中,有一部分你可能已經學過了,或者曾經簡短的接觸過。在本列系教程的最後,你應該知道的夠多,能夠自己開發簡單的 Express 應用。

+這些主題中,有一部分你可能已經學過了,或者曾經簡短的接觸過。在本列系教程的最後,你應該知道的夠多,能夠自己開發簡單的 Express 應用。 -

本地圖書館網站

+## 本地圖書館網站 -

我們接下來將創建,並隨著本系列教程發展的網站,名字是本地圖書館。如同你的預測,此網站的目的,是為一間小型本地圖書館,提供一個線上目錄,使用者能夠瀏覽可取得的書本,並管理他們的帳号。

+_我們接下來將創建,並隨著本系列教程發展的網站,名字是本地圖書館。如同你的預測,此網站的目的,是為一間小型本地圖書館,提供一個線上目錄,使用者能夠瀏覽可取得的書本_,並管理他們的帳号。 -

本範例經過細心地考慮,因為它的規模可以放大或縮小,以配合我們的需要,演示盡可能多或少的細節。並且可以用來演示幾乎所有的 Express 特性。更重要的,它允許我們提供一條引導路徑,演示你在任何網站都會需要的功能:

+本範例經過細心地考慮,因為它的規模可以放大或縮小,以配合我們的需要,演示盡可能多或少的細節。並且可以用來演示幾乎所有的 Express 特性。更重要的,它允許我們提供一條引導路徑,演示你在任何網站都會需要的功能: -
    -
  • 在教程一開始,我們將定義一個簡單的、只能瀏覽的圖書館,圖書館成員能夠用來找到可以借的書。這允許我們找出,幾乎每個網站都會使用的共同操作: 從數据庫讀取並呈現內容。
  • -
  • 跟隨教程的進展,圖書館的例子會一步一步擴充,以演示更高級的網站特征。比如我們會擴充圖書館,允許新書能夠被創建,並用這個來演示如何使用表單,並支持使用者授權。
  • -
+- 在教程一開始,我們將定義一個簡單的、只能瀏覽的圖書館,圖書館成員能夠用來找到可以借的書。這允許我們找出,幾乎每個網站都會使用的共同操作: 從數据庫讀取並呈現內容。 +- 跟隨教程的進展,圖書館的例子會一步一步擴充,以演示更高級的網站特征。比如我們會擴充圖書館,允許新書能夠被創建,並用這個來演示如何使用表單,並支持使用者授權。 -

即使這是一個具備相當擴充性的範例,它被叫做本地圖書館是有原因的 — 我們希望呈現給你最少的信息,能夠盡快幫助你上手並運行Express。因此,我們將會存放書本、複本、作者、和其它關鍵信息。然而,我們不會存放其它圖書館可能用到的有關信息,或者提供支持多個圖書館網站的架構,又或者其它 "大型圖書館" 的特性。

+即使這是一個具備相當擴充性的範例,它被叫做**本地**圖書館是有原因的 — 我們希望呈現給你最少的信息,能夠盡快幫助你上手並運行 Express。因此,我們將會存放書本、複本、作者、和其它關鍵信息。然而,我們不會存放其它圖書館可能用到的有關信息,或者提供支持多個圖書館網站的架構,又或者其它 "大型圖書館" 的特性。 -

我被卡住了,哪裡可以得到原始碼?

+## 我被卡住了,哪裡可以得到原始碼? -

當你使用本教程,我們將在每個知識點,提供適當的代碼片段,讓你複制貼上,同時有些代碼,我們希望你能自己擴充 (會有一些指引)。

+當你使用本教程,我們將在每個知識點,提供適當的代碼片段,讓你複制貼上,同時有些代碼,我們希望你能自己擴充 (會有一些指引)。 -

如果你被卡住了,你可以在 Github 的這裡,找到本地圖書館網站已經開發完成的版本。

+如果你被卡住了,你可以在 [Github 的這裡](https://github.com/mdn/express-locallibrary-tutorial),找到本地圖書館網站已經開發完成的版本。 -
-

備註: 在本教程中,指定版本的 node、Express、還有其它模組,都經過測試,並列出在專案項目的 package.json 檔案中。

-
+> **備註:** 在本教程中,指定版本的 node、Express、還有其它模組,都經過測試,並列出在專案項目的 [package.json](https://github.com/mdn/express-locallibrary-tutorial/blob/master/package.json) 檔案中。 -

總結

+## 總結 -

現在,你對本地圖書館網站以及將要學習的東西,有更多一點的認識,是時候開始創建一個 骨架項目,以存放我們的範例。

+現在,你對本地圖書館網站以及將要學習的東西,有更多一點的認識,是時候開始創建一個 [骨架項目](/zh-TW/docs/Learn/Server-side/Express_Nodejs/skeleton_website),以存放我們的範例。 -

{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs")}}

+{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs")}} -

本系列教學

+## 本系列教學 - +- [Express/Node introduction](/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction) +- [Setting up a Node (Express) development environment](/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment) +- [Express Tutorial: The Local Library website](/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website) +- [Express Tutorial Part 2: Creating a skeleton website](/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website) +- [Express Tutorial Part 3: Using a Database (with Mongoose)](/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose) +- [Express Tutorial Part 4: Routes and controllers](/en-US/docs/Learn/Server-side/Express_Nodejs/routes) +- [Express Tutorial Part 5: Displaying library data](/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data) +- [Express Tutorial Part 6: Working with forms](/en-US/docs/Learn/Server-side/Express_Nodejs/forms) +- [Express Tutorial Part 7: Deploying to production](/en-US/docs/Learn/Server-side/Express_Nodejs/deployment)