上一篇已經把這條線接起來:
網址
↓
Route
↓
Controller
↓
Action
例如:
/Home/Index
常見情況下會進到:
HomeController.Index()
但接著看到:
public IActionResult Index()
{
return View();
}
我又卡住了。
View() 裡面明明什麼都沒寫,
MVC 怎麼知道要找 Index.cshtml?
後來才知道,MVC 很常靠的是:
命名慣例
也就是 Controller 名稱、Action 名稱和 View 資料夾之間,通常有一套固定對應方式。
假設 Controller 是:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
MVC 看到目前執行的是:
HomeController
↓
Index()
而 return View() 沒有另外指定名稱。
這時就會按照慣例去找:
Views/Home/Index.cshtml
可以拆成:
HomeController
去掉 Controller
↓
Home
Index()
↓
Index.cshtml
最後組成:
Views/Home/Index.cshtml

Views
/
Controller 名稱
/
Action 名稱.cshtml
例如:
HomeController
↓
Views/Home
Index()
↓
Index.cshtml
所以:
HomeController.Index()
↓
Views/Home/Index.cshtml
再換一個例子。
public class UserController : Controller
{
public IActionResult Edit()
{
return View();
}
}
就會優先找:
Views/User/Edit.cshtml
也就是:
UserController
↓
User
Edit()
↓
Edit.cshtml
return View() 的意思不是「隨便找一支 View」不是。
當沒有指定 View 名稱時,它會根據目前 Action 的資訊去找對應畫面。
例如:
public IActionResult Detail()
{
return View();
}
如果這個方法在:
ProductController
裡面。
MVC 會優先找:
Views/Product/Detail.cshtml
所以:
return View();
背後其實隱含了:
目前是哪個 Controller?
目前是哪個 Action?
可以自己指定。
例如:
public IActionResult Index()
{
return View("HomePage");
}
這時候就不是找:
Index.cshtml
而是找:
HomePage.cshtml
在 HomeController 裡可能會找:
Views/Home/HomePage.cshtml
所以:
return View();
代表:
依照目前 Action 名稱找 View
而:
return View("HomePage");
代表:
我要指定 HomePage 這支 View
return View("History") 會執行 History() 嗎?不會。
這個地方我一開始很容易混亂。
假設:
public IActionResult Index()
{
return View("History");
}
這代表:
現在仍然執行的是 Index()
只是最後不要用 Index.cshtml,改用:
History.cshtml
也就是:
Index()
↓
return View("History")
↓
History.cshtml
它並不會再跑去執行:
public IActionResult History()
{
...
}
這點很重要。
這時就不是:
return View("History");
而可能會看到:
return RedirectToAction("History");
兩個意思完全不同。
return View("History")目前還是在這個 Action
↓
直接使用 History.cshtml 產生畫面
RedirectToAction("History")告訴瀏覽器重新發出請求
↓
再進到 History Action
例如:
public IActionResult Index()
{
return RedirectToAction("History");
}
public IActionResult History()
{
return View();
}
流程會是:
Index()
↓
RedirectToAction("History")
↓
重新請求
↓
History()
↓
return View()
↓
History.cshtml
所以看到:
View
和:
RedirectToAction
不要把它們當成同一件事。
通常還會看到另一個重要位置:
Views/Shared
例如:
Views
├── Home
│ └── Index.cshtml
│
└── Shared
├── _Layout.cshtml
└── Error.cshtml
假設 HomeController 裡:
public IActionResult Error()
{
return View();
}
MVC 可能先找:
Views/Home/Error.cshtml
如果沒有,再找:
Views/Shared/Error.cshtml
所以可以先把常見查找概念記成:
先找目前 Controller 對應資料夾
找不到
↓
再找 Views/Shared

不過實際企業專案也可能有:
Areas
自訂 ViewLocation
其他 Razor 設定
所以這篇先掌握最常見的慣例就好。
假設 Controller 是:
public class UserController : Controller
{
public IActionResult Edit()
{
return View();
}
}
照慣例應該有:
Views/User/Edit.cshtml
但如果我不小心放成:
Views/Home/Edit.cshtml
那 MVC 不會因為專案裡「剛好有一支 Edit.cshtml」就自動找到。
因為它還會看:
目前是哪個 Controller
所以這時可能出現找不到 View 的錯誤。
如果 MVC 找不到 View,通常錯誤訊息會告訴你:
它找過哪些路徑
例如可能看到類似:
The view 'Edit' was not found.
The following locations were searched:
/Views/User/Edit.cshtml
/Views/Shared/Edit.cshtml
這種錯誤其實很有用。
因為它直接告訴我:
MVC 現在認為 Controller 是 User
它正在找 Edit.cshtml
所以如果實際檔案放在:
Views/Home/Edit.cshtml
我就知道問題在哪了。

現在如果我看到:
public IActionResult Edit()
{
return View();
}
我會先做這幾件事。
例如:
UserController
先得到:
User
例如:
Edit
Views/User/Edit.cshtml
View() 有沒有另外指定名稱如果是:
return View();
就用 Action 名稱找。
如果是:
return View("Detail");
就改找:
Views/User/Detail.cshtml
Views/Shared
我的追法就會變成:
Controller 名稱
↓
Action 名稱
↓
return View() 有沒有指定 View
↓
Views/ControllerName
↓
Views/Shared
比直接在 Views 裡面亂翻快很多。
return View() 可以傳資料嗎?可以。
例如:
public IActionResult Index()
{
var userName = "Abbie";
return View(userName);
}
這時:
return View(userName);
不是說 View 名稱叫做 userName。
而是:
仍然按照目前 Action 找 View
同時把 userName 當資料傳進去
所以:
Index()
↓
return View(userName)
↓
Views/Home/Index.cshtml
+
userName 資料
也可以同時指定 View 名稱和資料:
return View("Detail", model);
這代表:
使用 Detail.cshtml
+
把 model 傳進去
下一篇就會正式整理:
Controller 到底怎麼把資料交給 View?
View() 常見寫法整理先整理四個很常看到的寫法。
View()return View();
代表:
使用目前 Action 名稱找 View
View("Detail")return View("Detail");
代表:
使用指定名稱的 View
View(model)return View(model);
代表:
使用目前 Action 對應的 View
+
傳入 model
View("Detail", model)return View("Detail", model);
代表:
使用 Detail View
+
傳入 model
整理成表格:
| 寫法 | View 怎麼找 | 有沒有傳資料 |
|---|---|---|
View() |
依目前 Action 名稱 | 沒有 |
View("Detail") |
指定 Detail |
沒有 |
View(model) |
依目前 Action 名稱 | 有 |
View("Detail", model) |
指定 Detail |
有 |
現在就可以把 Day 17 和 Day 18 接起來。
假設網址:
/Home/Index
第一段:
/Home/Index
↓
Route
↓
HomeController
↓
Index()
接著 Action:
public IActionResult Index()
{
return View();
}
第二段:
HomeController
+
Index()
↓
return View()
↓
Views/Home/Index.cshtml
所以完整流程變成:
瀏覽器
↓
Route
↓
Controller
↓
Action
↓
View
這才是目前真正接起來的 MVC 流程。
_Layout.cshtml 又在哪裡進來?前面已經學過:
Index.cshtml
可能不是完整頁面。
當 MVC 找到:
Views/Home/Index.cshtml
之後,Razor 還可能繼續套用:
_ViewStart.cshtml
↓
_Layout.cshtml
最後才產生完整 HTML。
所以整條線可以再拉長:
網址
↓
Route
↓
Controller
↓
Action
↓
return View()
↓
找到 Index.cshtml
↓
_ViewStart.cshtml
↓
_Layout.cshtml
↓
@RenderBody()
↓
產生完整 HTML
↓
瀏覽器
前面那些 View 的知識,現在終於跟 Controller 接起來了。
如果我只找到 Controller:
public IActionResult Detail(int id)
{
return View();
}
但不知道畫面在哪裡。
我現在會先:
1. 確認 Controller 名稱
↓
2. 確認 Action 名稱
↓
3. 看 return View() 有沒有指定名稱
↓
4. 去 Views/ControllerName 找
↓
5. 找不到再看 Shared
↓
6. 打開 View 後再確認 Layout 和 Partial
例如:
ProductController
↓
Detail()
↓
return View()
我就先找:
Views/Product/Detail.cshtml
如果是:
return View("Info");
就改找:
Views/Product/Info.cshtml
這樣就不用看到:
return View();
還在想:
到底是哪一支啦 XD
最重要的是:
return View();
並不是真的「什麼都沒指定」。
MVC 會利用目前的:
Controller 名稱
Action 名稱
按照命名慣例尋找 View。
最基本的規則可以先記成:
Controller:
HomeController
Action:
Index()
↓
View:
Views/Home/Index.cshtml
如果自己指定:
return View("Detail");
則會改找:
Detail.cshtml
另外:
return View("History");
只是:
使用 History View
不代表執行:
History Action
要重新進入另一個 Action,通常會看到:
RedirectToAction(...)
目前整條 MVC 流程已經可以先串成:
網址
↓
Route
↓
Controller
↓
Action
↓
return View()
↓
找到 .cshtml
↓
Razor 組成 HTML
↓
瀏覽器
下一篇就接著看:
Controller 已經知道要使用哪個 View 了,那查到的資料到底怎麼交給
.cshtml顯示?