Contents

NET Core MVC 入門操作筆記

這邊使用 .Net Core 5 版本,Net Core 6 會不太一樣,大致原理都是一致的。這邊我觀看學習教學影片邊紀錄,我覺得動手做比較容易了解問題與忽略問題地方,我那個影片有操作一些錯誤地方,不是他忘記步驟,而是當我們忘記那些步驟可以快速從那個地方找出問題解決。

專案選擇

這邊要選擇 Web API 選項。

https://i.imgur.com/v71u5H7.png

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();
            });
        }
    }

程式上面重點:

  1. Configure 註冊容器服務,會在這邊配置訊息。
  2. 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

https://i.imgur.com/RVUiNia.png

https://i.imgur.com/WiJTYZF.png

https://i.imgur.com/c9H1XMv.png

https://i.imgur.com/A86pKvF.png

 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,這邊其實有很多物件繼承這個。他可以針對不同類型去做回傳。我網路上有找到繼承圖片。

https://i.imgur.com/gdDWFWC.png
參考:Action results in ASP.NET CORE APIs

Tip
我目前工作常用到的有IActionResultIJSONResultFileResult等等。這邊你可以寫的很死,也可以放你新增的類別也可以,我目前我的專案是這樣用,當人也可以這樣寫ActionResult<你的Class>,但我目前不知道直接這樣寫的好處?跟強制直接寫你的Class這樣的好處在哪?
Question
https://i.imgur.com/7lSBqJ7.png
這樣編譯不會錯,這讓我很好奇。有空再整理筆記

新增 Model

https://i.imgur.com/VtcIT42.png

新增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

https://i.imgur.com/F5USq25.png

建立 Repository

這邊選擇介面。

https://i.imgur.com/fjcN4YN.png

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

程式運行發生錯誤

https://i.imgur.com/HLxq4rt.png

這邊不用太擔心,這邊是註冊容器,沒辦法入住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
https://i.imgur.com/UzJd2Z4.png

依賴注入種類

  • AddSingleton()
  • AddTransient()
  • AddScoped()

優點

  • 低耦合
  • 提供了高測試姓,使用測試單元更加容易。

Controller JSON 範例

https://i.imgur.com/m88IM8U.png

 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);
        }

https://i.imgur.com/v7IslV5.png

View

1
2
3
4
5
        public IActionResult Details()
        {
            Dog dog = _dogRepostory.getDog(1);
            return View(dog);
        }

https://i.imgur.com/KAJYKoC.png

這個錯誤會顯示默認 VIEW 三個路徑

  • /Views/Home/Details.cshtml
  • /Views/Shared/Details.cshtml
  • /Pages/Shared/Details.cshtml

除了一個一個加資料夾
也可以再 View 方法右鍵新增 View,按下新增檢視,

https://i.imgur.com/VZjdYsl.png

https://i.imgur.com/5zzv4k1.png

https://i.imgur.com/GYaMvp7.png

Views/Home/Details(View)

1
2
3
4
@{
    ViewData["Title"] = "Details";
}
<h1>Details</h1>

.Net Core 5 建立檢視失敗可以參考(參考)

https://i.imgur.com/nWNKczr.png

c# - How to create MVC5 web project in visual studio 2022 - Stack Overflow

自訂義 View 發現

可以自訂View頁面,有下面三種方式。

  1. View("Test") => /Views/Home/Test
  2. View("~/Test/xxx.cshtml"); => 絕對路徑
  3. 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>

https://i.imgur.com/LyfKalf.png

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>

https://i.imgur.com/B6aoL9H.png

強型別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>

https://i.imgur.com/5NgqOU1.png

ViewModel 視圖模型

模型物件無法滿足視圖所需的所有資料時,就需要使用 ViewModel了。

建立ViewModels資料夾,檔案以 *ViewModel.cs做結尾`。

https://i.imgur.com/L6JE6kb.png

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>

https://i.imgur.com/w6OPQxc.png

總結小記:

  1. 新增 ViewModel 物件,設定好屬性。
  2. Controler新增ViewModel,把這個物件傳到 View上面
  3. 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

https://i.imgur.com/DZ5zy2x.png

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

https://i.imgur.com/xxwOoym.png

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>

https://i.imgur.com/dQkcjhF.png

布局頁面的結點(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>
}

結果會顯示
https://i.imgur.com/7B2vn4Q.png

_ViewStart.cshtml

_ViewStart.cshtml程式碼會先在單個是途中先執行程式碼。減少了程式碼重複性。

新增時候選擇
https://i.imgur.com/Z5Y5pi4.png

建立在View資料夾

https://i.imgur.com/5tqHMAL.png

1
2
3
@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

其他View屬性就不須指定_Layout
也可以簡略寫成如下

1
2
3
@{
    Layout = "_Layout2";
}

執行_ViewStart.cshtml順序如下圖,裡面的Home資料夾,依序先執行1,在執行2,依序後蓋前設定。

https://i.imgur.com/ftaRviN.png

_ViewImport.cshtml

上面我們寫的專案需要寫很長文字去讀取物件出來,如@model WebApplication3.ViewModels.HomeDetailsViewModel_ViewImport.cshtml就是很好解決這個問題。

新增檔案建立到Views資料夾,參考下圖
https://i.imgur.com/sbsThTd.png

WebApplication3\WebApplication3\Views_ViewImports.cshtml

1
@using WebApplication3.Models;

我們在原有的View就可以不用寫前面的 namespace

https://i.imgur.com/exAluJQ.png

簡單整理:

  • 需使用@using指令包含公共命局空間
  • _ViewImports 文件還支持以下指令
    • @addTagHelper
    • @removeTagHelper
    • @tagHelperPrefix
    • @model
    • @inherits
    • @inject
  • ViewImports 文件支持分層讀取,這邊跟_ViewStart.cshtml一樣。1先讀2後讀,後蓋前設定。

https://i.imgur.com/fysGKbW.png

路由(Route)

預設路由

WebApplication3\WebApplication3\Startup.cs

1
2
3
4
5
6
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
    ...
    app.UseMvcWithDefaultRoute();
    ...
        }

我們這邊可以看到使用 app.UseMvcWithDefaultRoute() 做預設路由。從下面註解可以看到預設規則。
https://i.imgur.com/z4Cngz2.png

這邊使用原本需要手動建立 router 規則。參考如下。

1
2
3
4
5
6
            //app.UseMvcWithDefaultRoute();

            app.UseMvc(routes =>
            {
                routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
            });

預設 Router 規則參照如下。
https://i.imgur.com/Jn0WB4M.png

下面直接操作看看。

修改 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()屬性定義路由。
屬性路由通常定義在ControllerController的方法上面
強制上層路由呼叫以/~/來做設定。

這邊我們在Startup.cs先取消預設路由做測試。
這邊注意,我.NET Core 5 版本預設有 Page 資料夾,這邊建議先重新命名資料夾名稱,這樣就不會跟你的 Router 衝突到。

https://i.imgur.com/JvFOEIR.png

Startup.cs

1
            app.UseMvc();

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);
        }

查看結果:

https://i.imgur.com/eEk9CuO.png

https://i.imgur.com/ssQ4fIv.png

https://i.imgur.com/wl5umJM.png

 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);
        }

https://i.imgur.com/9CNeRcR.png

id空值會報錯!
https://i.imgur.com/5briUL4.png

 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);
        }

https://i.imgur.com/qLiO5HB.png

解決問題。

Controller 跟 Router 命名不一樣

https://i.imgur.com/9A4SVgX.png

https://i.imgur.com/LM3bEp3.png

這邊看這兩張圖,發現他找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);
        }

https://i.imgur.com/qVi5e5D.png

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);
        }
    }
}

https://i.imgur.com/lpLotoS.png

https://i.imgur.com/3yTa3yw.png

https://i.imgur.com/y5zuEFI.png

https://i.imgur.com/jJ5b152.png

在自動調整方法

 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,但我會建議去了解它怎麼使用。