diff options
Diffstat (limited to 'files/zh-tw/learn/server-side/express_nodejs/forms/index.html')
-rw-r--r-- | files/zh-tw/learn/server-side/express_nodejs/forms/index.html | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/files/zh-tw/learn/server-side/express_nodejs/forms/index.html b/files/zh-tw/learn/server-side/express_nodejs/forms/index.html new file mode 100644 index 0000000000..008d7ae4e8 --- /dev/null +++ b/files/zh-tw/learn/server-side/express_nodejs/forms/index.html @@ -0,0 +1,274 @@ +--- +title: 'Express 教學 6: 使用表單' +slug: Learn/Server-side/Express_Nodejs/forms +translation_of: Learn/Server-side/Express_Nodejs/forms +--- +<div>{{LearnSidebar}}</div> + +<div>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs/deployment", "Learn/Server-side/Express_Nodejs")}}</div> + +<p class="summary">在此教程中,我們會教你如何使用 Express ,並且結合 Pug 來實現 HTML 表單,並且如何從數據庫中創建、更新、和刪除文檔。</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">前提條件:</th> + <td>完成前面所有的教程,包括 Express 教程第5章: 展示圖書館數據。</td> + </tr> + <tr> + <th scope="row">目標:</th> + <td>了解如何編寫表單獲取用戶信息,並且將這些數據更新到數據庫中。</td> + </tr> + </tbody> +</table> + +<h2 id="概覽">概覽</h2> + +<p><a href="/zh-TW/docs/Web/Guide/HTML/Forms">HTML 表單</a>是網頁中由一個、或多個字段/小工具形成的一個組合,它被用來收集用戶的信息,並將信息上傳到服務器。表單作為一種用來收集用戶的機制,非常的靈活,因為有各種合適的輸入框,來接受各種類型的數據——文本框,複選框,單選按鈕,時間選擇器等。表單和服務器交互數據也相對安全,因為它使用<code>POST</code>請求發送數據,保護不受跨站點請求偽造攻擊(cross-site request forgery)的威脅。</p> + +<p>但是表單同樣也很複雜!開發者需要編寫給表單編寫 HTML,在服務器上驗證,並且正確去除有害的數據(瀏覽器上也可能需要),對於任何不合法的字段,需要傳給用戶相應的錯誤信息,當數據成功提交後,處理數據,並設法通知用戶提交成功。</p> + +<p>此教程將展示上述的操作,如何在 Express 中實現。在此過程中,我們將擴展 LocalLibrary 網站,以允許用戶創建、編輯、和刪除圖書館中的項目。</p> + +<div class="note"> +<p><strong>注意:</strong> 我們還沒有考慮如何將特定路由,限制為經過身份驗證或授權的用戶,因此在這個時間點,任何用戶都可以對數據庫進行更改。</p> +</div> + +<h3 id="HTML_表單">HTML 表單</h3> + +<p>首先簡要概述 <a href="/zh-TW/docs/Web/Guide/HTML/Forms">HTML 表單</a>。考慮一個簡單的 HTML 表單,其中包含一個文本字段,用於輸入某些 “團隊” 的名稱,及其相關標籤:</p> + +<p><img alt="Simple name field example in HTML form" src="https://mdn.mozillademos.org/files/14117/form_example_name_field.png" style="border-style: solid; border-width: 1px; display: block; height: 44px; margin: 0px auto; width: 399px;"></p> + +<p>表單在HTML中,定義為 <code><form>...</form></code>標記內的元素集合,包含至少一個<code>type="submit"</code>的 <code>input</code>輸入元素。</p> + +<pre class="brush: html"><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></pre> + +<p>雖然這裡,我們只包含一個(文本)字段,用於輸入團隊名稱,但表單可能包含任意數量的其他輸入元素,及其相關標籤。字段的 <code>type</code> 屬性,定義將顯示哪種窗口小部件。該字段的名稱<code>name</code>和<code>id</code> ,用於標識JavaScript/CSS/HTML 中的字段,而 <code>value</code>定義字段首次顯示時的初始值。匹配團隊標籤使用 <code style="font-style: normal; font-weight: normal;">label </code>標籤,指定(請參閱上面的“輸入名稱” "Enter name"),其中 <code style="font-style: normal; font-weight: normal;">for</code> 字段,包含 <code style="font-style: normal; font-weight: normal;">input </code>相關輸入的 <code style="font-style: normal; font-weight: normal;">id</code>值。</p> + +<p>提交輸入(<code>submit</code>)將顯示為按鈕(默認情況下) - 用戶可以按此按鈕,將其他輸入元素包含的數據,上傳到服務器(在本例中,只有 <code>team_name</code>)。表單屬性,定義用於發送數據的HTTP <code>method</code>方法,和服務器上數據的目標(<code>action</code>):</p> + +<ul> + <li><code>action</code>: 提交表單時,要發送數據以進行處理的資源/ URL。如果未設置(或設置為空字符串),則表單將提交回當前頁面 URL。</li> + <li><code>method</code>: 用於發送數據的 HTTP 方法:<code>POST</code> 或 <code>GET</code>。 + <ul> + <li><code>POST</code> 方法。如果數據將導致服務器數據庫的更改,則始終應該使用 <code>POST</code>方法,因為這更加可以抵抗跨站點偽造請求攻擊。</li> + <li><code>GET</code> 方法只應用於不更改用戶數據的表單(例如,搜索表單)。當您希望能夠為URL添加書籤或共享時,建議使用此選項。</li> + </ul> + </li> +</ul> + +<h3 id="表單處理流程">表單處理流程</h3> + +<p>表單處理使用的技術,與我們學習過、用來顯示有關模型的信息的所有技術,是相同的:路由將我們的請求發送到控制器函數,該函數執行所需的任何數據庫操作,包括從模型中讀取數據,然後生成並返回 HTML 頁面。使事情變得更複雜的是,服務器還需要能夠處理用戶提供的數據,並在出現任何問題時,重新顯示帶有錯誤信息的表單。</p> + +<p>下面顯示了處理表單請求的流程圖,從包含表單的頁面請求開始(以綠色顯示):</p> + +<p><img alt="" src="https://mdn.mozillademos.org/files/14478/Web%20server%20form%20handling.png" style="height: 649px; width: 800px;"></p> + +<p>如上圖所示,構成處理代碼所需要做的主要是: </p> + +<ol> + <li>在用戶第一次請求時顯示默認表單。 + <ul> + <li>表單可能包含空白字段(例如,如果您正在創建新記錄),或者可能預先填充了初始值(例如,如果您要更改記錄,或者俱有有用的默認初始值)。</li> + </ul> + </li> + <li>接收用戶提交的數據,通常是在HTTP <code>POST</code>請求中。</li> + <li>驗證並清理數據。</li> + <li>如果任何數據無效,請重新顯示表單 - 這次使用用戶填寫的任何值,和問題字段的錯誤消息。</li> + <li>如果所有數據都有效,請執行所需的操作(例如,將數據保存在數據庫中,發送通知電子郵件,返回搜索結果,上傳文件等)</li> + <li>完成所有操作之後,將用戶重定向到另一個頁面。</li> +</ol> + +<p>表格處理代碼,通常使用 <code>GET</code>路由,以實現表單的初始顯示,以及 <code>POST</code>路由到同一路徑,以處理表單數據的驗證和處理。這是將在本教程中使用的方法!</p> + +<p>Express 本身不提供表單處理操作的任何特定支持,但它可以使用中間件,以處理表單中的 <code>POST</code>和 <code>GET</code>參數,並驗證/清理它們的值。</p> + +<h3 id="驗證和清理">驗證和清理</h3> + +<p>在儲存表單中的數據之前,必須對其進行驗證和清理:</p> + +<ul> + <li>驗證檢查輸入的值,適用於每個字段(範圍,格式等),並且已為所有必填字段提供了值。</li> + <li>數據中的字符,可能用於將惡意內容送到服務器,為其進行清理刪除/替換。</li> +</ul> + +<p>在本教程中,我們將使用流行的 <a href="https://www.npmjs.com/package/express-validator">express-validator </a>模塊,來執行表單數據的驗證和清理。</p> + +<h4 id="安裝">安裝</h4> + +<p>通過在項目的根目錄中,運行以下命令,來安裝模塊。</p> + +<pre class="brush: bash">npm install express-validator +</pre> + +<h4 id="使用_express-validator">使用 express-validator</h4> + +<div class="note"> +<p><strong>注意:</strong> Github上的 <a href="https://github.com/ctavan/express-validator#express-validator">express-validator</a> 指南,提供了API的良好概述。我們建議您閱讀該內容,以了解其所有功能(包括創建自定義驗證程序)。下面我們只介紹一個對 LocalLibrary 有用的子集。</p> + +<p> </p> + +<p> </p> +</div> + +<p>要在我們的控制器中使用驗證器,我們必須從 'e<strong>xpress-validator/check</strong>'和'<strong>express-validator/filter</strong>'模塊中,導入我們想要使用的函數,如下所示:</p> + +<pre class="brush: js">const { body,validationResult } = require('express-validator/check'); +const { sanitizeBody } = require('express-validator/filter'); +</pre> + +<p>有許多可用的功能,允許您一次檢查和清理請求參數,正文,標頭,cookie 等數據,或所有數據。對於本教程,我們主要使用 <code>body</code>, <code>sanitizeBody</code>,和 <code>validationResult</code>(如上面 required 導入的 )。</p> + +<p>功能定義如下:</p> + +<ul> + <li><code><a href="https://github.com/ctavan/express-validator#bodyfields-message">body(fields[, message])</a></code>: 指定請求本文中的一組字段(<code>POST</code>參數)以及可選的錯誤消息,如果測試失敗,則可以顯示該字段。驗證標準以菊花鏈形式連接到 <code>body()</code>方法。例如,下面的第一個檢查測試,“name”字段不為空,如果不是,則設置錯誤消息“Empty name”。第二個測試,檢查age字段是否為有效日期,並使用<code>optional()</code>指定null和空字符串不會驗證失敗。 + + <pre class="brush: js">body('name', 'Empty name').isLength({ min: 1 }), +body('age', 'Invalid age').optional({ checkFalsy: true }).isISO8601(), +</pre> + 您還可以用菊花鍊式連接不同的驗證器,並添加前面驗證器為真時顯示的消息。 + + <pre class="brush: js">body('name').isLength({ min: 1 }).trim().withMessage('Name empty.') + .isAlpha().withMessage('Name must be alphabet letters.'), +</pre> + + <div class="note"> + <p><strong>注意:</strong> 您還可以添加內聯清理器,如<code>trim()</code>,如上所示。但是,此處應用清理器,僅適用於驗證步驟。如果要對最終輸出進行消毒,則需要使用單獨的清理器方法,如下所示。</p> + </div> + </li> + <li><code><a href="https://github.com/ctavan/express-validator#sanitizebodyfields">sanitizeBody(fields)</a></code>: 指定一個正文要清理的字段。然後將清理操作,以菊花鏈形式連接到此方法。例如,下面的 <code>escape()</code>清理操作,會從名稱變量中,刪除可能在 JavaScript 跨站點腳本攻擊中使用的 HTML 字符。 + <pre class="brush: js">sanitizeBody('name').trim().escape(), +sanitizeBody('date').toDate(),</pre> + </li> + <li><code><a href="https://github.com/ctavan/express-validator#validationresultreq">validationResult(req)</a></code>: 運行驗證,以 <code>validation </code>驗證結果對象的形式,提供錯誤。這是在單獨的回調中調用的,如下所示: + <pre class="brush: 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. + } +}</pre> + 我們使用驗證結果的<code>isEmpty()</code>方法,來檢查是否存在錯誤,並使用其 <code>array()</code> 方法,來獲取錯誤消息集合。有關更多信息,請參閱驗證結果的 <a href="https://github.com/ctavan/express-validator#validation-result-api">Validation Result API</a>。</li> +</ul> + +<p>驗證和清理鏈,是應該傳遞給 Express 路由處理程序的中間件(我們通過控制器,間接地執行此操作)。中間件運行時,每個驗證器/清理程序都按指定的順序運行。</p> + +<p>當我們實現下面的LocalLibrary表單時,我們將介紹一些真實的例子。</p> + +<h3 id="表單設計">表單設計</h3> + +<p>圖書館中的許多模型都是相關/依賴的 - 例如,一本書需要一個作者,也可能有一個或多個種類。這提出了一個問題,即我們應該如何處理用戶希望的情況:</p> + +<ul> + <li>在其相關對象尚不存在時,創建對象(例如,尚未定義作者對象的書)。<br> + </li> + <li>刪除另一個對象仍在使用的對象(例如,刪除仍有書本 <code>Book </code>使用的種類 <code>Genre</code>)。</li> +</ul> + +<p>在這個項目,我們為了簡化實作,將聲明表單只能:</p> + +<ul> + <li>使用已存在的對象創建對象(因此用戶在嘗試創建任何 <code>Book</code>對象之前,必須創建任何所需的 <code>Author</code>和 <code>Genre</code>實例)。</li> + <li>如果對象未被其他對象引用,則刪除該對象(例如,在刪除所有關聯的 <code>BookInstance</code>對象之前,您將無法刪除該書)。</li> +</ul> + +<div class="note"> +<p><strong>注意:</strong> 更“牢固”的實現,可能允許您在創建新對象時,創建依賴對象,並隨時刪除任何對象(例如,通過刪除依賴對象,或從數據庫中,刪除對已刪除對象的引用) 。</p> +</div> + +<h3 id="路由">路由</h3> + +<p>為了實現我們的表單處理代碼,我們需要兩個具有相同 URL 模式的路由。</p> + +<p>第一個(<code>GET</code>)路由,用於顯示用於創建對象的新空表單。第二個路由(<code>POST</code>),用於驗證用戶輸入的數據,然後保存信息,並重定向到詳細信息頁面(如果數據有效),或重新顯示有錯誤的表單(如果數據無效)。</p> + +<p>我們已經在 <strong>/routes/catalog.js</strong>(在之前的教程中)為我們所有模型的創建頁面,創建了路徑。例如,種類路由如下所示:</p> + +<pre class="brush: 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); +</pre> + +<h2 id="Express_表單子文件">Express 表單子文件</h2> + +<p>以下子文件,將帶我們完成向示例應用程序添加所需表單的過程。在進入下一個文件之前,您需要依次閱讀並解決每個問題。</p> + +<ol> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_genre_form">創建種類表單</a> — 定義我們的頁面以創建種類對象 <code>Genre </code>。</li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_author_form">創建作者表單</a> — 定義用於創建作者對象 <code>Author </code>的頁面。</li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_book_form">創建書本表單</a> — 定義頁面/表單以創建書本對象 <code>Book</code> 。</li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Create_BookInstance_form">創建書本實例表單</a> — 定義頁面/表單以創建書本實例對象 <code>BookInstance</code> 。</li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Delete_author_form">刪除作者表單 </a>— 定義要刪除作者對象 <code>Author </code>的頁面。</li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms/Update_Book_form">更新書本表單</a> — 定義頁面以更新書本對象 <code>Book</code> 。</li> +</ol> + +<h2 id="挑戰自我">挑戰自我</h2> + +<p>實現 <code>Book</code>, <code>BookInstance</code>, 和 <code>Genre</code>模型的刪除頁面,用跟我們的作者刪除頁面相同的方式,將它們與關聯的詳細信息頁面,鏈接起來。頁面應遵循相同的設計方法:</p> + +<ul> + <li>如果有來自其他對象的、對於對象的引用,則應顯示註釋,列出這些對象,並說明在刪除列出的對象之前,無法刪除此記錄。</li> + <li>如果沒有對該對象的其他引用,則視圖應提示刪除它。如果用戶按下“刪除”按鈕 <strong>Delete</strong>,則應刪除該記錄。</li> +</ul> + +<p>一些提示:</p> + +<ul> + <li>刪除種類 <code>Genre</code>就像刪除作者<code>Author</code>一樣,因為兩個對像都是 <code>Book</code>的依賴項(因此在這兩種情況下,只有在刪除相關書本時,才能刪除對象)。</li> + <li>刪除書本 <code>Book</code>也很相似,但您需要檢查是否沒有關聯的書本實例 <code>BookInstances</code>。</li> + <li>刪除書本實例 <code>BookInstances</code>是最簡單的,因為沒有依賴對象。在這種情況下,您只需找到相關記錄並將其刪除即可。</li> +</ul> + +<p>實現 <code>BookInstance</code>, <code>Author</code>, 和 <code>Genre</code>模型的更新頁面,以與我們的書本更新頁面相同的方式,將它們與關聯的詳細信息頁面,鏈接起來。</p> + +<p>一些提示:</p> + +<ul> + <li>我們剛剛實施的圖書更新頁面是最難的!相同的模式可用於其他對象的更新頁面。</li> + <li>作者<code>Author</code>的死亡日期和出生日期字段,以及書本實例<code>BookInstance</code> 的 due_date 字段,是輸入到表單上日期輸入字段的錯誤格式(它需要 “YYYY-MM-DD” 形式的數據)。解決此問題的最簡單方法,是為適當格式化的日期,定義新的虛擬屬性,然後在關聯的視圖模板中,使用此字段。</li> + <li>如果您遇到困難,此處示例中的更新頁面有一些示例<a href="https://github.com/mdn/express-locallibrary-tutorial">的連結。</a></li> +</ul> + +<h2 id="總結">總結</h2> + +<p>Express, node, 與NPM上面的第三方套件,提供你需要的每樣東西 ,可用於新增表單到你的網站上。在本文中,您學習如何使用 Pug 創建表單,使用 express-validator 驗證和清理輸入,以及添加,刪除和修改數據庫中的記錄。</p> + +<p>你現在應該了解如何新增基本表單,以及表單處理代碼到你的 node 網站!</p> + +<h2 id="請參閱">請參閱</h2> + +<ul> + <li><a href="https://www.npmjs.com/package/express-validator">express-validator</a> (npm docs).</li> +</ul> + +<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs/deployment", "Learn/Server-side/Express_Nodejs")}}</p> + +<h2 id="本教程連結">本教程連結</h2> + +<ul> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node (Express) development environment</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li> +</ul> + +<p> </p> |