這邊使用 .Net Core 5 版本,Net Core 6 會不太一樣,大致原理都是一致的。這邊我觀看學習教學影片邊紀錄,我覺得動手做比較容易了解問題與忽略問題地方,我那個影片有操作一些錯誤地方,不是他忘記步驟,而是當我們忘記那些步驟可以快速從那個地方找出問題解決。
專案選擇
這邊要選擇 Web API 選項。
Startup.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication6", Version = "v1" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication6 v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
|
程式上面重點:
- Configure 註冊容器服務,會在這邊配置訊息。
- ConfigureServices 依賴注入服務都會在這邊設定。
Tip
這邊可以看到 Startup() 注入了 IConfiguration,這邊我後續讀完覺得很奇怪,ConfigureServices() 這邊不是才註冊容器嗎?為什麼建構子就能注入,剛好查看官方文件原來能注入東西很少。
使用泛型主機 () IHostBuilder 時,只能將下列服務類型插入 Startup 建構函式:
IWebHostEnvironment
IHostEnvironment
IConfiguration
相關連結: 點我
.NET Core 加入 Net Core MVC 中間件
這邊提一下中間件英文是MIddleware。
Tip
這邊我在學習 Middleware 我發現跟 Laravel Middleware 很像,回想我當初學的 Spring Boot 沒有這一層東西,但一樣還是可以做到這一層事情。沒錯,是 AOP。所以Spring Boot 不需要有 Middleware 這一個東西。
相關連結: Spring Boot AOP 小記
MVC 加入到 asp.net core 依賴注入容器中
1
2
3
4
|
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
|
但是新版,上面可能要做變更,參考從 ASP.NET Core 2.2 移轉至 3.0 | Microsoft Docs文章。
這邊我用 .Net Core 5
1
2
3
4
|
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => options.EnableEndpointRouting = false);
}
|
設定中間件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello world");
});
|
從 ASP.NET Core 2.2 移轉至 3.0 | Microsoft Docs
addMvc 和 addMvcCore
addMvcCore: 只包含MVC功能。所以Controller 用到JSON 回傳會錯誤。
1
2
3
4
|
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
|
addMvc: 包含MVC Core和相關第三方常用服務方法。
1
2
3
4
|
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore();
}
|
簡單說,addMvc中間件比addMvcCore中間件功能還多,這邊剛入門的可以先不用太在意這邊。我後面有篇筆記有紀錄這兩種不太一樣東西。
建立 Controller
在專案建立 Controllers,建立HomeController
1
2
3
4
5
6
7
8
9
10
11
12
|
using Microsoft.AspNetCore.Mvc;
namespace WebApplication3.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return Json(new { a = "1", b = "2" });
}
}
}
|
所有 API 可以回傳 IActionResult,這邊其實有很多物件繼承這個。他可以針對不同類型去做回傳。我網路上有找到繼承圖片。
參考:Action results in ASP.NET CORE APIs
Tip
我目前工作常用到的有IActionResult、IJSONResult、FileResult等等。這邊你可以寫的很死,也可以放你新增的類別也可以,我目前我的專案是這樣用,當人也可以這樣寫ActionResult<你的Class>,但我目前不知道直接這樣寫的好處?跟強制直接寫你的Class這樣的好處在哪?
Question
這樣編譯不會錯,這讓我很好奇。有空再整理筆記
新增 Model
新增Dog.cs。
在public class上面打///按 Enter,這邊可以得到下面結果。
1
2
3
4
5
6
7
8
9
|
namespace WebApplication3.Models
{
/// <summary>
/// 狗的測試模型
/// </summary>
public class Dog
{
}
}
|
在public class Dog裡面打上prop按下兩下Tab,可以快速設定屬性setter,getter。
建立 Repository
這邊選擇介面。
1
2
3
4
5
6
7
|
namespace WebApplication3.Models
{
public interface IDogRepository
{
public Dog getDog(int id);
}
}
|
建立 MockRepository
可用 ctor按下 tab 快速建立MockDogRepository建構函示,可以參考:C# 程式碼片段 - Visual Studio (Windows) | Microsoft Docs文章。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
using System.Collections.Generic;
namespace WebApplication3.Models
{
public class MockDogRepository : IDogRepository
{
private List<Dog> _dogList;
public MockDogRepository()
{
}
public Dog getDog(int id)
{
throw new System.NotImplementedException();
}
}
}
|
最後再補上 Mock 資料。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
using System.Collections.Generic;
namespace WebApplication3.Models
{
public class MockDogRepository : IDogRepository
{
private List<Dog> _dogList;
public MockDogRepository()
{
_dogList = new List<Dog>()
{
new Dog(){Id=1,Name ="dog1",Address="tw"},
new Dog(){Id=2,Name ="dog2",Address="tw"},
new Dog(){Id=3,Name ="dog3",Address="tw"},
};
}
public Dog getDog()
{
throw new System.NotImplementedException();
}
}
}
|
Controller 依賴注入 DogRepository
依賴注入這邊跟 Laravel 很像,要完成下面動作做注入。
1
2
3
4
5
6
|
private readonly IDogRepository _dogRepostory;
public HomeController(IDogRepository dogRepository)
{
_dogRepostory = dogRepository;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
using Microsoft.AspNetCore.Mvc;
using WebApplication3.Models;
namespace WebApplication3.Controllers
{
public class HomeController : Controller
{
private readonly IDogRepository _dogRepostory;
public HomeController(IDogRepository dogRepository)
{
_dogRepostory = dogRepository;
}
public string Index()
{
return _dogRepostory.getDog(1).Name;
// return Json(new { a = "1", b = "2" });
}
}
}
|
http://localhost:46201/home/index
程式運行發生錯誤
這邊不用太擔心,這邊是註冊容器,沒辦法入住DI。
依賴注入
Tip
假如你有學過 Spring Boot 和 Laravel 的話應該對這個算滿熟悉。想當初我學 Laravel 第一次碰注入(DI)這個玩意也是看不太懂。這個有點跟類別static有點像,不需要再new出來。
首先你可能要知道依賴注入是要如何註冊的,Spring 依賴注入可以看我之前寫過這篇,因為Laravel 注入還沒學會,所以就沒深入他怎麼註冊的。回到重點,Net Core 依賴注入只有一種方法,都要去手動註冊,內鍵沒有自動注入,但使用第三方套件也能做到。
Warning
這個有點跟類別static有點像,不需要再new出來。
在使用上可能要了解注入物件的生命週期,通常 Scope 有分幾類,這個有時候會影響程式結果,比較常都用單例模式。這邊我工作上面很多都把這個當全域變數用,我覺得Helper相關類的可以用 static,但其實有些可以做到 static,像是 JWT 加密就可以直接用。
但有些會吃IConfiguration可以考慮用一個 Service 出來,把設定用上去,再把 Service注入IConfiguration再引用static Helper執行,這樣做程式分層架構可以更好去模組化。但這樣要寫很多類別,很多人都不會這樣做樣子…
startup.cs
1
2
3
4
5
6
|
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => options.EnableEndpointRouting = false);
services.AddSingleton<IDogRepository, MockDogRepository>();
}
|
實作單例模式,才能正常運作。
http://localhost:46201/home/index
依賴注入種類
- AddSingleton()
- AddTransient()
- AddScoped()
優點
Controller JSON 範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
using Microsoft.AspNetCore.Mvc;
using WebApplication3.Models;
namespace WebApplication3.Controllers
{
public class HomeController : Controller
{
private readonly IDogRepository _dogRepostory;
public HomeController(IDogRepository dogRepository)
{
_dogRepostory = dogRepository;
}
public string Index()
{
return _dogRepostory.getDog(1).Name;
// return Json(new { a = "1", b = "2" });
}
public JsonResult Details()
{
Dog dog = _dogRepostory.getDog(1);
return Json(dog);
}
}
}
|
看 HTTP 協議傳回格式
ObjectResult
startup.cs
1
2
3
4
5
6
|
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options => options.EnableEndpointRouting = false).AddXmlSerializerFormatters();
services.AddSingleton<IDogRepository, MockDogRepository>();
}
|
HomeControler.cs
1
2
3
4
5
|
public ObjectResult Details()
{
Dog dog = _dogRepostory.getDog(1);
return new ObjectResult(dog);
}
|
View
1
2
3
4
5
|
public IActionResult Details()
{
Dog dog = _dogRepostory.getDog(1);
return View(dog);
}
|
這個錯誤會顯示默認 VIEW 三個路徑
- /Views/Home/Details.cshtml
- /Views/Shared/Details.cshtml
- /Pages/Shared/Details.cshtml
除了一個一個加資料夾
也可以再 View 方法右鍵新增 View,按下新增檢視,
Views/Home/Details(View)
1
2
3
4
|
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
|
.Net Core 5 建立檢視失敗可以參考(參考)
c# - How to create MVC5 web project in visual studio 2022 - Stack Overflow
自訂義 View 發現
可以自訂View頁面,有下面三種方式。
View("Test") => /Views/Home/Test
View("~/Test/xxx.cshtml"); => 絕對路徑
View("../../xxx"); => 相對路徑,不用加 cshtml
推薦使用絕對路徑。
1
2
3
4
|
public virtual ViewResult View();
public virtual ViewResult View(string viewName, object model);
public virtual ViewResult View(object model);
public virtual ViewResult View(string viewName);
|
Controller 傳遞資料到 View
- 使用 ViewData
- 使用 ViewBag
- 強型別View
ViewData
- 是弱型別的字典(Dictonary)物件。
- 使用 string 型別的Key值,儲存和查詢 ViewData 字典中的資料
- 執行時動態解析
- 沒有智能感知,編譯時也沒有型別檢查
Controllers\HomeController.cs
1
2
3
4
5
6
7
8
9
10
|
public IActionResult Details()
{
Dog dog = _dogRepostory.getDog(1);
ViewData["PageTitle"] = "Dog Details";
ViewData["Dog"] = dog;
return View(dog);
}
|
Views\Home\Details.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@using WebApplication3.Models
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>@ViewData["PageTitle"]</title>
</head>
<body>
<h3>@ViewData["PageTitle"]</h3>
@{
var dog = ViewData["Dog"] as Dog;
}
<div>
名稱:@dog.Name
</div>
<div>
地址:@dog.Address
</div>
</body>
</html>
|
ViewBag
- ViewBag是ViewData的包裝器
- 它們都建立了一個弱型別的View
- ViewData使用字串Key來儲存和查詢資料
- ViewBag使用動態屬性來儲存和查詢資料
- 均是在運釆時動態解析
- 均不是供編譯時型別檢查,沒有智能提示。
- 首選方法是使用強類別模型物件,將資料從控制器傳遞到View。
Views\Home\Details.cshtml
1
2
3
4
5
6
7
8
9
10
|
public IActionResult Details()
{
Dog dog = _dogRepostory.getDog(1);
ViewBag.PageTitle = "Dog Details";
ViewBag.Dog = dog;
return View(dog);
}
|
Views\Home\Details.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@using WebApplication3.Models
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>@ViewBag.PageTitle</title>
</head>
<body>
<h3>@ViewBag.PageTitle</h3>
<div>
名稱:@ViewBag.Dog.Name
</div>
<div>
地址:@ViewBag.Dog.Address
</div>
</body>
</html>
|
強型別View
- 使用
@model指另指定型別
- 使用
@Model抓出物件屬性
- 強行別類型View提供編譯時類型檢查和智能提示
1
2
3
4
5
6
|
public IActionResult Details()
{
Dog dog = _dogRepostory.getDog(1);
return View(dog);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@model WebApplication3.Models.Dog
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div>
名稱:@Model.Name
</div>
<div>
地址:@Model.Address
</div>
@Model
</body>
</html>
|
ViewModel 視圖模型
模型物件無法滿足視圖所需的所有資料時,就需要使用 ViewModel了。
建立ViewModels資料夾,檔案以 *ViewModel.cs做結尾`。
HomeDetailsViewModel.cs
1
2
3
4
5
6
7
8
9
10
11
|
using WebApplication3.Models;
namespace WebApplication3.ViewModels
{
public class HomeDetailsViewModel
{
public string PageTitle { get; set; }
public Dog Dog { get; set; }
}
}
|
WebApplication3\WebApplication3\Controllers\HomeController.cs
1
2
3
4
5
6
7
8
9
10
11
|
public IActionResult Details()
{
HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
{
Dog = _dogRepostory.getDog(1),
PageTitle = "狗的詳情"
};
return View(homeDetailsViewModel);
}
|
WebApplication3\WebApplication3\Views\Home\Details.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@model WebApplication3.ViewModels.HomeDetailsViewModel
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>@Model.PageTitle</title>
</head>
<body>
<div>
名稱:@Model.Dog.Name
</div>
<div>
地址:@Model.Dog.Address
</div>
@Model
</body>
</html>
|
總結小記:
- 新增 ViewModel 物件,設定好屬性。
- Controler新增ViewModel,把這個物件傳到 View上面
- View 使用 ViewModel物件。
實現 List 視圖
WebApplication3\WebApplication3\Models\IDogRepository.cs
1
2
3
4
5
6
7
8
9
10
11
|
using System.Collections.Generic;
namespace WebApplication3.Models
{
public interface IDogRepository
{
public Dog getDog(int id);
// 新增 GetAllDogs
public IEnumerable<Dog> GetAllDogs();
}
}
|
WebApplication3\WebApplication3\Models\MockDogRepository.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
using System.Collections.Generic;
using System.Linq;
namespace WebApplication3.Models
{
public class MockDogRepository : IDogRepository
{
private List<Dog> _dogList;
public MockDogRepository()
{
_dogList = new List<Dog>()
{
new Dog(){Id=1,Name ="dog1",Address="tw"},
new Dog(){Id=2,Name ="dog2",Address="tw"},
new Dog(){Id=3,Name ="dog2",Address="tw"},
};
}
// 新增這個方法
public IEnumerable<Dog> GetAllDogs()
{
return _dogList;
}
public Dog getDog(int id)
{
return _dogList.FirstOrDefault(a => a.Id == id);
}
}
}
|
WebApplication3\WebApplication3\Controllers\HomeController.cs
1
2
3
4
5
6
|
public IActionResult Index()
{
var model = _dogRepostory.GetAllDogs();
return View(model);
}
|
WebApplication3\WebApplication3\Views\Home\Index.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
@model IEnumerable<WebApplication3.Models.Dog>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>狗的清單</title>
</head>
<body>
<table>
<tr>
<th>序號</th>
<th>名稱</th>
<th>地址</th>
</tr>
@foreach (var dog in Model)
{
<tr>
<td>
@dog.Id
</td>
<td>
@dog.Name
</td>
<td>
@dog.Address
</td>
</tr>
}
</table>
</body>
</html>
|
.NET Core MVC 部局視圖
讓web應用程式中讓所有的視圖保持外觀一致性。布局檔案默認為_Layout.cshtml。通常放在(Views/Pages)/Shared資料夾。
在View或Page 建立 Shared 資料夾。
建立 Shared/_Layout.cshtml。
Shared/_Layout.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
|
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>@ViewBag.Title</title>
</head>
<body>
@RenderBody()
</body>
</html>
|
F:\source\repos\WebApplication3\WebApplication3\Views\Home\Details.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@model WebApplication3.ViewModels.HomeDetailsViewModel
@{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = @Model.PageTitle;
}
<div>
名稱:@Model.Dog.Name
</div>
<div>
地址:@Model.Dog.Address
</div>
@Model
|
WebApplication3\WebApplication3\Views\Home\Index.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
@model IEnumerable<WebApplication3.Models.Dog>
@{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = "狗的清單";
}
<table>
<tr>
<th>序號</th>
<th>名稱</th>
<th>地址</th>
</tr>
@foreach (var dog in Model)
{
<tr>
<td>
@dog.Id
</td>
<td>
@dog.Name
</td>
<td>
@dog.Address
</td>
</tr>
}
</table>
|
布局頁面的結點(Sections)
提了一種方法讓某些頁面元素放在一起。可以強制性使用或者可選擇性使用。使用RenderSection()來呼叫
WebApplication3\WebApplication3\Views\Shared_Layout.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
</head>
<body>
<div>
@RenderBody()
</div>
@RenderSection("Scripts",required: false)
</body>
</html>
|
中間那一段@RenderSection("Scripts",required: false)可以改寫
1
2
3
4
|
@if (IsSectionDefined("Scripts"))
{
@RenderSection("Scripts",required: false)
}
|
WebApplication3\WebApplication3\Views\Home\Details.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@model WebApplication3.ViewModels.HomeDetailsViewModel
@{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = @Model.PageTitle;
}
<div>
名稱:@Model.Dog.Name
</div>
<div>
地址:@Model.Dog.Address
</div>
@Model
@section Scripts{
<script src="~/js/site.js"></script>
}
|
重點
1
2
3
|
@section Scripts{
<script src="~/js/site.js"></script>
}
|
結果會顯示
_ViewStart.cshtml
_ViewStart.cshtml程式碼會先在單個是途中先執行程式碼。減少了程式碼重複性。
新增時候選擇
建立在View資料夾
1
2
3
|
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
|
其他View屬性就不須指定_Layout。
也可以簡略寫成如下
1
2
3
|
@{
Layout = "_Layout2";
}
|
執行_ViewStart.cshtml順序如下圖,裡面的Home資料夾,依序先執行1,在執行2,依序後蓋前設定。
_ViewImport.cshtml
上面我們寫的專案需要寫很長文字去讀取物件出來,如@model WebApplication3.ViewModels.HomeDetailsViewModel,_ViewImport.cshtml就是很好解決這個問題。
新增檔案建立到Views資料夾,參考下圖
WebApplication3\WebApplication3\Views_ViewImports.cshtml
1
|
@using WebApplication3.Models;
|
我們在原有的View就可以不用寫前面的 namespace
簡單整理:
- 需使用
@using指令包含公共命局空間
- _ViewImports 文件還支持以下指令
- @addTagHelper
- @removeTagHelper
- @tagHelperPrefix
- @model
- @inherits
- @inject
- ViewImports 文件支持分層讀取,這邊跟
_ViewStart.cshtml一樣。1先讀2後讀,後蓋前設定。
路由(Route)
預設路由
WebApplication3\WebApplication3\Startup.cs
1
2
3
4
5
6
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseMvcWithDefaultRoute();
...
}
|
我們這邊可以看到使用 app.UseMvcWithDefaultRoute() 做預設路由。從下面註解可以看到預設規則。
這邊使用原本需要手動建立 router 規則。參考如下。
1
2
3
4
5
6
|
//app.UseMvcWithDefaultRoute();
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
|
預設 Router 規則參照如下。
下面直接操作看看。
修改 HomeController.cs details 補上傳入 id 參數
1
2
3
4
5
6
7
8
9
10
11
|
public IActionResult Details(int id)
{
HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
{
Dog = _dogRepostory.getDog(id),
PageTitle = "狗的詳情"
};
return View(homeDetailsViewModel);
}
|
WebApplication3\WebApplication3\Controllers\HomeController.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
using Microsoft.AspNetCore.Mvc;
using WebApplication3.Models;
using WebApplication3.ViewModels;
namespace WebApplication3.Controllers
{
public class HomeController : Controller
{
private readonly IDogRepository _dogRepostory;
public HomeController(IDogRepository dogRepository)
{
_dogRepostory = dogRepository;
}
public IActionResult Index()
{
var model = _dogRepostory.GetAllDogs();
return View(model);
}
public IActionResult Details(int id)
{
HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
{
Dog = _dogRepostory.getDog(id),
PageTitle = "狗的詳情"
};
return View(homeDetailsViewModel);
}
}
}
|
屬性路由
使用屬性路由,我們使用Route()屬性定義路由。
屬性路由通常定義在Controller或Controller的方法上面
強制上層路由呼叫以/和~/來做設定。
這邊我們在Startup.cs先取消預設路由做測試。
這邊注意,我.NET Core 5 版本預設有 Page 資料夾,這邊建議先重新命名資料夾名稱,這樣就不會跟你的 Router 衝突到。
Startup.cs
WebApplication3\WebApplication3\Controllers\HomeController.cs
1
2
3
4
5
6
7
8
9
|
[Route("")]
[Route("Home")]
[Route("Home/Index")]
public IActionResult Index()
{
var model = _dogRepostory.GetAllDogs();
return View(model);
}
|
查看結果:
1
2
3
4
5
6
7
8
9
10
11
12
|
[Route("Home/Details/{id?}")]
public IActionResult Details(int id)
{
HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
{
Dog = _dogRepostory.getDog(id),
PageTitle = "狗的詳情"
};
return View(homeDetailsViewModel);
}
|
id空值會報錯!
1
2
3
4
5
6
7
8
9
10
11
12
|
[Route("Home/Details/{id?}")]
public IActionResult Details(int? id)
{
HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
{
Dog = _dogRepostory.getDog(id??1),
PageTitle = "狗的詳情"
};
return View(homeDetailsViewModel);
}
|
解決問題。
Controller 跟 Router 命名不一樣
這邊看這兩張圖,發現他找View會找不到。
這邊就需要決斷路徑指定View,上面View有提到。
1
2
3
4
5
6
7
8
9
|
[Route("")]
[Route("Home")]
[Route("Home/Index")]
public IActionResult Index()
{
var model = _dogRepostory.GetAllDogs();
return View("~/Views/Home/Index.cshtml",model);
}
|
Route 放置 Controller 上面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
using Microsoft.AspNetCore.Mvc;
using WebApplication3.Models;
using WebApplication3.ViewModels;
namespace WebApplication3.Controllers
{
[Route("Home")]
public class HomeController : Controller
{
private readonly IDogRepository _dogRepostory;
public HomeController(IDogRepository dogRepository)
{
_dogRepostory = dogRepository;
}
[Route("~/")]
[Route("")]
[Route("Index")]
public IActionResult Index()
{
var model = _dogRepostory.GetAllDogs();
return View(model);
}
[Route("Details/{id?}")]
public IActionResult Details(int? id)
{
HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
{
Dog = _dogRepostory.getDog(id??1),
PageTitle = "狗的詳情"
};
return View(homeDetailsViewModel);
}
}
}
|
在自動調整方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
using Microsoft.AspNetCore.Mvc;
using WebApplication3.Models;
using WebApplication3.ViewModels;
namespace WebApplication3.Controllers
{
[Route("[controller]")]
public class HomeController : Controller
{
private readonly IDogRepository _dogRepostory;
public HomeController(IDogRepository dogRepository)
{
_dogRepostory = dogRepository;
}
[Route("~/")]
[Route("")]
[Route("[action]")]
public IActionResult Index()
{
var model = _dogRepostory.GetAllDogs();
return View(model);
}
[Route("[action]/{id?}")]
public IActionResult Details(int? id)
{
HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
{
Dog = _dogRepostory.getDog(id??1),
PageTitle = "狗的詳情"
};
return View(homeDetailsViewModel);
}
}
}
|
在更精簡方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
using Microsoft.AspNetCore.Mvc;
using WebApplication3.Models;
using WebApplication3.ViewModels;
namespace WebApplication3.Controllers
{
[Route("[controller]/[action]")]
public class HomeController : Controller
{
private readonly IDogRepository _dogRepostory;
public HomeController(IDogRepository dogRepository)
{
_dogRepostory = dogRepository;
}
[Route("~/")]
[Route("~/Home")]
public IActionResult Index()
{
var model = _dogRepostory.GetAllDogs();
return View(model);
}
[Route("{id?}")]
public IActionResult Details(int? id)
{
HomeDetailsViewModel homeDetailsViewModel = new HomeDetailsViewModel()
{
Dog = _dogRepostory.getDog(id??1),
PageTitle = "狗的詳情"
};
return View(homeDetailsViewModel);
}
}
}
|
實戰中還是用…
Startup.cs
1
2
3
4
|
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
|
總結
仔細再回來看這篇,這邊已經把剛入手要會的東西。再有相關程式經驗學習 Net Core 也是可以很快上手,再搭配 Visual Studio 智慧提示,你可以很快撰寫程式碼,也可以知道程式哪裡錯。
Net Core的 View 真的讓我滿經驗,在我學 Laravel 和 Spring Boot 的 View 時候,編譯器無法判別哪邊有寫錯誤(也可能我不會設定🤣),Visual Studio 就可以做到。在寫 View 時候有些沒有判斷到都可以早點發現。後面我工作是做純後端,所以平常是不寫 View,但我會建議去了解它怎麼使用。