今天我們來介紹form表單相關內容,前2天講到的資料傳遞都是藉由Controller從資料模型中抓取Data,再將Data丟到View呈現的方式。那如果是想把在View頁面輸入的資訊傳回給Controller,再做後續資料傳遞與處理的話該怎麼做呢?
例如我們想建立一個登入畫面,使用者可以輸入帳號密碼後按登入並顯示登入後畫面,這時候就會需要用到關鍵的HTML表單 (form)了!
因為之前的DemoController
已經寫了太多方法,避免混亂我們另外新建一個Controller叫做LogInController
,忘記怎麼做的話可以回去看Day3的內容~
建立後Code如下:
public class LogInController : Controller
{
// GET: LogIn
public ActionResult Index()
{
return View();
}
}
接著新增View,View的內容會使用HTML的form
標籤,在View修改為如下列的Code:
@{
ViewBag.Title = "Index";
}
<h2>LogIn Page</h2>
<div class="container">
<form method="post" action="~/login/verify">
<label>Account</label>
<br />
<input type="text" name="account" />
<br /><br />
<label>Password</label>
<br />
<input type="text" name="password" />
<br /><br />
<input type="submit" name="submit" value="Login" class="btn btn-primary" />
</form>
</div>
完成後執行畫面如下:
簡單說明一下上面範例form標籤的語法,幾個重點如下:
<form>
標籤裡面才能傳遞。<form>
標籤的method
屬性決定用何種HTTP請求方法傳送,主要為post
但也有get
選項。<form>
標籤的action
屬性決定送出表單後,表單內容該送去哪個URL。submit
按鈕,一個表單內只能有一個submit
按鈕。name
屬性繫結,才能讓後端對應到元素欄位。另外提一下標籤裡面會看到class
屬性,這是用來美化標籤樣式的功能,預設是套用Bootstrap套件的樣式檔,後面會專門開一篇來介紹。
因為我們已經在form裡面給定了method
使用post
方法,以及action
要傳送到~/login/verify
這個URL,代表我們在LoginController
內就要有個對應的動作方法叫做Verify
,且這個方法是接受HTTP POST的請求。因此我們新增如下Code:
[HttpPost]
public ActionResult Verify()
{
return View();
}
注意動作方法的上方一定要標註[HttpPost]
,否則預設都會視為[HttpGet]
。
接著說明form的內容如何繫結到動作方法,第一種是使用FormCollection
來抓取表單內資料元素,因為資料量不大這邊直接使用ViewBag
攜帶,我們將Verify()
方法修改Code如下:
[HttpPost]
public ActionResult Verify(FormCollection obj)
{
ViewBag.Account = obj["account"];
ViewBag.Password = obj["password"];
return View();
}
要注意的重點在於FormCollection
使用form裡面元素的name
屬性來繫結(不區分大小寫),對照下圖:
最後來新增Verify()
方法對應的View,Code如下:
@{
ViewBag.Title = "Verify";
}
<h2>Verify</h2>
<div>
<ol>
<li>User: @ViewBag.Account</li>
<li>Pwd: @ViewBag.Password</li>
</ol>
</div>
執行的時候注意不是直接連到~/login/verify
這個URL,因為它是藉由POST方法請求,而直接輸入網址是一種GET方法,我們並沒有建立對應GET的動作方法。我們應該要從原本的~/login/Index
執行,輸入帳號與密碼欄位內容,並按下Submit按鈕提交表單,依照表單的method
與action
屬性才會轉到~/login/verify
並顯示對應的View。
按下Login後畫面如下:
另外一種方式是直接將參數名稱對應form裡面元素name
屬性,並代入動作方法,修改Verify()
方法Code如下:
[HttpPost]
public ActionResult Verify(string account, string password)
{
ViewBag.Account = account;
ViewBag.Password = password;
return View();
}
View的部分不用修改,顯示結果會與上面相同。
最後一種方式就是自行建立類別與屬性,來對應form裡面元素name
屬性,首先新增一個LoginViewModel
類別於Models資料夾底下,並加入對照form的屬性Account
與Password
,Code如下:
public class LoginViewModel
{
public string Account { get; set; }
public string Password { get; set; }
}
接著修改Verify()
方法Code如下:
[HttpPost]
public ActionResult Verify(LoginViewModel vm) //using Models
{
ViewBag.Account = vm.Account;
ViewBag.Password = vm.Password;
return View();
}
View的部分一樣不用修改,顯示結果會與上面相同。
一開始我們有提到form表單其實可以使用get方法來提交資料,那呈現方式會是如何呢?
首先當然我們要先把View的<form>
裡面的method
屬性改為get
,Code如下:
@{
ViewBag.Title = "Index";
}
<h2>LogIn Page</h2>
<div class="container">
<form method="get" action="~/login/verify">
<label>Account</label>
<br />
<input type="text" name="account" />
<br /><br />
<label>Password</label>
<br />
<input type="text" name="password" />
<br /><br />
<input type="submit" name="submit" value="Login" class="btn btn-primary" />
</form>
</div>
接著在Controller將Verify()
動作方法上面的[HttpPost]
拿掉或改成[HttpGet]
,Code如下:
[HttpGet]
public ActionResult Verify(LoginViewModel vm)
{
ViewBag.Account = vm.Account;
ViewBag.Password = vm.Password;
return View();
}
重新執行Index畫面並登入,畫面如下:
眼睛比較利的人可以發現,當使用GET請求方式提交表單時,表單輸入的資訊會被放到URL的尾端,這與我們在Day3時講到的重點相同。這樣是否會有點問題呢?因為帳號密碼資訊不應該在網址上可以被看到才對呀!?所以透過這個例子可以瞭解,為何form通常會使用POST而不用GET的理由之一了,但原因不僅僅是這樣而已,下面會做個詳細比較整理。
※Day 3內容節錄:
在form表單使用POST的理由可能如下:
1. 安全性:
如上面範例所述,使用GET請求時把資料放在URL上傳遞是一目瞭然的,我們並不希望隱密的內容可以如此被直接看到。而使用POST請求時,傳遞內容會放在請求的message body裡面,這相對安全多了。(但實際上仍可透過查看HTTP封包內容得知)
2. 參數長度限制:
例如我們知道GET請求是將參數放在URL尾端傳送,但根據不同瀏覽器URL限制的長度會不同(ex: IE為2083 bytes、Chrome為8192 bytes),需要傳遞的參數過多可能會造成影響。而POST是將傳遞參數放在請求的message body 中,就沒有長度限制問題。
3. 為了新增或修改資源:
根本上來說,HTTP/1.1協定中定義的POST請求就是向指定資源提交資料,請求伺服器進行處理(例如提交表單或者上傳檔案)。這個請求可能會建立新的資源或修改現有資源,或二者皆有。
例如提交表單的目的是要新增會員資料、修改現有密碼、或者將商品加入購物車清單等等,這些動作會新增或變更資料庫內容,此時就應該使用POST方法;而如果發送表單只為了幫助查尋資源內容,那使用GET就無妨。
※這邊可以延伸出來講個名詞叫做冪等性(idempotency),冪等指的是,假如在不考慮諸如錯誤或者過期等問題的情況下,若干次請求的副作用與單次請求相同或者根本沒有副作用,那麼這些請求方法就能夠被視作「冪等(idempotence)」的。
例如GET請求是「冪等」的,因為它只讀取顯示內容而不會新增或修改到資源,同樣的請求即使不小心送了兩次,也不用擔心對伺服器端造成不同的影響。而POST請求則是「非冪等」的,因為每次提交表單都會對資源造成影響,如果User不小心送了兩次請求,就有可能造成重複提交表單,這也是網頁開發者需要去防範的問題。
GET 與 POST的詳細比較表格如下:
請求方式 | GET | POST |
---|---|---|
資料傳遞方式 | 將參數放在URL尾端,傳遞至Server | 傳遞參數會放在請求的 message body 中 |
參數長度限制 | URL長度限制根據瀏覽器、伺服器會有所不同。 | 長度無限制 |
安全性 | 傳遞的參數會在URL上顯示,較POST不安全 | 較GET安全,但實際上內容可透過HTTP封包看到 |
資料種類 | 只允許 ASCII,非ASCII會進行轉碼 | 無限制 |
重新載入 | 不影響顯示結果 | 會詢問重新提交資料 |
瀏覽器書籤 | 可以加入 | 無法加入 |
冪等性 | 冪等 | 非冪等 |
今天主要講了form表單傳送資料的幾種方式,以及GET與POST的比較,明天來講和View畫面呈現息息相關的BootStrap套件功能,那就明天見囉~
※小弟不才,在軟體的世界還只是個小菜雞,如果內容有任何謬誤或問題,還請各位大神前輩們多多批評指教~歡迎下方留言討論^^