本文是針對已有 ASP.NET MVC 開發經驗的朋友所做的 ASP.NET Core 入門介紹,著重在實務面上,不會對一些已知名詞如 MVC、Controller 等做詳細介紹。

ASP.NET Core 聽起來像是 ASP.NET 的後續版本而已,事實不然,它是基於全新的 .NET Core 所發展出來的新網站開發架構,除了使用情境承襲了 ASP.NET 外,整個裡子都是新的。

要對 .NET Core 有基本認識,可以參考這篇:準備就緒的 .NET Core 2.0 來了

ASP.NET Core 簡介

參考官方的特色說明,先對 ASP.NET Core 有個基本的認識。

ASP.NET Core 是一種跨平台且高效能的開放原始碼程式架構,用於建置現代化、雲端式、網際網路連線的應用程式。 利用 ASP.NET Core,可以:

  • 建置 Web 應用程式和服務、IoT 應用程式,以及行動後端。
  • 在 Windows、macOS 和 Linux 上開發及執行。
  • 部署到一般伺服器或雲端。
  • 在 .NET Core 或 .NET Framework 上執行。

ASP.NET Core 是 ASP.NET 的重新設計,提供精簡且模組化的架構,提供下列優點:

  • 提供建置 Web UI 和 Web API 的統一環境 (基於相同的 Controller)。
  • 整合現代化的用戶端架構和開發工作流程。
  • 雲端就緒、以運行環境為基礎的組態系統。
  • 內建的相依性插入。
  • 基於 Open Web Interface for .NET (OWIN) 的全新起始及執行環境。輕量型、高效能且模組化的 HTTP 要求管線。
  • 能夠在 IIS 上裝載,或自我裝載於自己的處理序。
  • 可以在 .NET Core 上執行,其支援真正的並存應用程式版本控制。
  • 可簡化現代網頁程式開發的工具。
  • 能夠在 Windows、macOS 和 Linux 上建置並執行。
  • 開放原始碼和社群導向。
  • ASP.NET Core 完全以 NuGet 套件的形式提供。 這可讓您最佳化您的應用程式,使其只包含需要的 NuGet 套件。 應用程式介面區較小的優點包括更嚴密的安全性、減少維護工作,以及提升效能。
  • 執行速度快。
  • 是 ASP.NET 的未來

開發工具

Visual Studio 2017 對最新版本的 .NET Core 及 .NET Standard 有完整的支援,在 windows 上做開發還是建議用 Visual Studio。

Visual Studio Code 則提供 Windows、macOS 和 Linux 上皆可使用的輕量開發環境。

ASP.NET Core 基礎

全新的 ASP.NET Core 新增了以往沒有的核心功能, 像是 Dependency injection,執行環境的切換,Logging 等,讓平台功能更完備。

Web App Startup

建一個新 ASP.NET Core 專案,來看看新的專案內容有什麼。

打開 Visual Studio 2017 建立新專案 > Visual C# > Web > ASP.NET Core Web 應用程式
選擇 Web 應用程式 (MVC),按下 [確定] 建立專案。

新建專案

ASP.NET Core 的專案內容和 ASP.NET 有很大不同。
wwwroot 目錄存放全部的靜態檔。根目錄下有一些主要檔案:

  • Program.cs – 程式執行的進入點。ASP.NET Core 本身已經高度模組及獨立化了,它直接就以 Console 的方式來做自我裝載 (像是 embedded 進 console 應用中)。
  • [Project Name].csproj - XML-based 的專案設定。放棄了最初的 project.json 改回 csproj,以和 MSBuild 能更好的協作。
  • Startup.cs – ASP.NET Core 系統的進入點, 和 ASP.NET 時的 Global.asax 有相同效用.
  • web.config – 可選, 就算有也沒有什麼內容了, 主要只是用來和 IIS 溝通,像是指示 IIS 用 ASP.NET Core handler 來處理 request.

Dependency injection 依賴注入

什麼是 dependency injection?

物件一旦在程式中被 new 建立,它就已經和使用它的程式碼或物件緊密耦合在一起,怎麼解決這個問題?

先基於 “抽象”(Abstractions) 通常是 “介面”(interface) 來定義功能,再由平台或 DI 套件依照設定值來產生實作物件,並透過 constructor 或 method 傳給使用方(injection), 使用方不用知道實作物件的細節, 只透過介面功能來使用它.

ASP.NET Core 加入的新功能之一就是內建 DI 機制。平台和元件都已基於這個機制來開發和執行,不需要再依不同的 DI 實作做整合和設定。

dependency injection 的設定

由 Startup.cs 中的 ConfigureServices(IServiceCollection services) {} 來做設定.

根據物件生命週期的不同,提供了三個 method 來做元件的註冊:

  • Transient – 無狀態, 每次注入都會 new 新的,services.AddTransient()
  • Scoped – 在同 request 中會重覆使用,services.AddScoped()
  • Singleton – 共用同一個物件,services.AddSingleton()

平台有預先開發好特定功能來注入 MVC 及 Entity Framework 相關類別,如:

  • services.AddMvc() – 把 ASP.NET Core MVC 的 Controllers 和 Filters 加到註冊表中。
  • services.AddDbContext(…) - 把 Entity Framework 的 DBContext 加到註冊表中。

dependency injection 的使用

  1. 基於 interface 定義功能並實作。
  2. 在 ConfigureServices 中設定。
  3. 在 constructor 中依 interface 要求注入實作物件。
    public HomeController(IClock clock) 可取得 IClock 的實作物件。
  4. 依 interface 使用功能, 如 clock.GetTime()。

Environments 執行環境

程式開發執行至少會有 2 個以上的執行環境需求,如 開發(Development) 和 正式(Production)。

ASP.NET Core 提供了新的環境設定機制,稱為 Hosting Environment Management,方便在多環境需求下做開發及部署。它預設提供了 3 個環境狀態名:Development、Staging、Production,並定義在 EnvironmentName 類別中。

ASP.NET Core 經由 IHostingEnvironment 這個 interface 取得參數值,而 IHostingEnvironment 則是透過作業系統的環境變數 ASPNETCORE_ENVIRONMENT 決定環境。
如果值是 Development,表示在開發環境。由於是去讀環境變數,所以是在執行階段才決定,而不是在編譯時就決定了,更有機動性。IHostingEnvironment 也配合預設環境狀態名,擴充了 3 個判斷 method: IsDevelopment(), IsProduction(), IsStaging(),方便程式做判斷。

Visual Studio

預設下,Visual Studio 會自動用 Development mode,設定值是位於 Properties 目錄下的launchSettings.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
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:18762/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"CoreWebApplication1": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:18763/"
}
}
}

profiles 區段包含的就是 ASP.NET Core 的伺服器實作 Kestrel 會用到的設定。
若要做調整,可以直接改檔案或是透過專案的 [屬性] -> [偵錯] 畫面來做調整及加入新環境值。

IHostingEnvironment 的使用

如果程式中需要依環境做不同的處理, 可以 inject IHostingEnvironment 來做判斷.

1
2
3
4
5
6
7
// 由 constructor 取得
public HomeController(IHostingEnvironment env)
{…}

// 環境判斷
if(_env.IsDevelopment())
{…}

Startup class

Startup class 是最重要的設定入口,在不同的 Environment 下可能有不同的內容。 WebHostBuilder 的 UseStartup() 很聰明的會依照 method name + environment name 的規則去載入及叫用對應的環境實作.

建立自己的環境值

如果預設的 3 個環境不符合使用,可以增加自己的環境值。

  1. 在 IHostingEnvironment 加上 extension method 如 IsTesting()。
  2. IsTesting() 中,取 IHostingEnvironment instance 的 EnvironmentName 值做判斷。

Static files

由於 Web API 不需要 static files 的服務,ASP.NET Core 預設不啟用 static files 支援。

啟用 Static Files 的支援

利用 Microsoft.AspNetCore.StaticFiles 套件,用 OWIN 的方式啟用它:

1
app.UseStaticFiles();

Single page application

由於 SPA 的入口頁面為 html,必須在 Startup 的 Configure 中做設定:

1
app.UseFileServer();

預設支援 default.htm、default.html、index.htm 和 index.html。

也可以自定檔名:

1
2
3
var options = new DefaultFilesOptions();
options.DefaultFileNames.Clear();
options.DefaultFileNames.Add("mydefault.html");

Error handling and exception pages

ASP.NET Core 針對錯誤,提供了新機制方便開發者除錯及處理。

Developer Exception Page

Microsoft.AspNetCore.Diagnostics package 提供了 UseDeveloperExceptionPage() middleware,當接到 Exception 時會顯示清楚完整的錯誤內容,分 4 個 tab 來分類提供資訊,如下圖所示:

Developer Exception Page

啟用方式如下:

1
2
3
4
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

User-friendly error page

有 2 個 middleware 可依 http status code 設定客製的錯誤頁:

  • 導到靜態頁 – UseStatusCodePagesWithRedirects(“~/errors/{0}.html”);
  • 導到 MVC route - UseStatusCodePagesWithReExecute(“~/errors/{0}”);

若不做專用頁面,平台有提供簡單的程序:
app.UseStatusCodePages();

或是導到統一的 MVC route 做處理:
app.UseExceptionHandler("/error");

Configuration files

appsettings.json

web.config 不再用來存放 appSettings 的設定值,預設改放到 appsettings.json。

前面提過,當 WebHost.CreateDefaultBuilder() 初始化時,預設會從 appsettings.jsonappsettings.[EnvironmentName].json 載入 IConfiguration 的內容,而 appsettings.[EnvironmentName].json 只需放差異的部份即可。

讀取設定值

以 “ : “ 做階層名稱的分隔,陣列直接用數字索引,如:0

假設有 appsettings.json ,內容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"MyConfig1": {
"Level1": 123
},
"MyConfig2": [
{
"Name": "John"
},
{
"Name": "Mary"
}
]
}

透過 inject 取得 IConfiguration

1
2
3
4
5
6
using Microsoft.Extensions.Configuration;

public HomeController(IConfiguration configuration)
{
_configuration = configuration;
}

取值

1
2
var v1 = _configuration["MyConfig1:Level1"]; // get “123”
var v2 = _configuration["MyConfig2:1:Name"]; // get “Mary”

客製自己的設定檔

這裡是產生獨立的類別組, 不是加入 IConfiguration 中。

  1. 定義一個 json 檔, 有 3 段設定值,就叫 myappsettings.json 吧。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    "database": {
    "databaseName": "my-db-name",
    "serverHost": "mySqlHost",
    "port": 1433,
    "username": "username",
    "password": "password"
    },
    "facebook": {
    "appId": "app-id",
    "appSecret": "app-secret"
    }
    }
  2. 定義一組和 json 設定值有相同結構的類別。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Configuration
    {
    public DatabaseConfiguration Database { get; set; }
    public FacebookConfiguration Facebook { get; set; }
    public SmtpConfiguration Smtp { get; set; }
    }

    public class DatabaseConfiguration
    {
    public string DatabaseName { get; set; }
    public string ServerHost { get; set; }
    public int Port { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public string ConnectionString => $"Server=tcp:{ServerHost},{Port}; Database={DatabaseName}; User ID = { Username }; Password={Password};Encrypt=True;TrustServerCertificate=False;Connection Timeout = 30;";
    }

    public class FacebookConfiguration
    {
    public string AppId { get; set; }
    public string AppSecret { get; set; }
    }
  3. 在 Startup.cs 中初始化, 並以 Singleton 模式註冊到 DI 中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    private readonly IHostingEnvironment _hostingEnvironment;
    private readonly Configuration _configuration;

    public Startup(IHostingEnvironment hostingEnvironment)
    {
    _hostingEnvironment = hostingEnvironment;

    var builder = new ConfigurationBuilder()
    .SetBasePath(hostingEnvironment.ContentRootPath)
    .AddJsonFile("myappsettings.json", false, true)
    .Build();

    _configuration = new Configuration();
    builder.Bind(_configuration);
    }

    public void ConfigureServices(IServiceCollection services)
    {
    services.AddSingleton(_configuration);
    }
  4. 利用dependency injection 取得

    1
    public HomeController(Configuration myconfiguration)

Logging

ASP.NET Core 內建提供了類似 Common.Logging.NET 的 Log 中介機制,套件及功能開發都應該利用標準的方式來處理 log,不用再透過外部套件。

Providers

內建提供了 6 個 logging providers,可以記錄到不同地方:

  • Console
  • Debug - 加入到 Visual Studio Debug 中的 ”偵錯”內容。
  • EventSource - 加入到 Windows 的 Event Tracing (ETW),macOS 及 linux 不支援。
  • EventLog - 加入到 Windows 的 Event Log。
  • TraceSource
  • Azure App Service

也可和第三方套件協作:

  • elmah.io - Elmah.Io service。
  • JSNLog - logs JavaScript exceptions 和其它的 client-side events 到 server-side log。
  • Loggr - Loggr service。
  • NLog - NLog library。
  • Serilog - Serilog library。
  • log4net - log4net。

在應用程式中啟用 Providers

在 Hosting 時如果是用 CreateDefaultBuilder(), 已預設啟用 Console 和 Debug。

在 new WebHostBuilder() 時,啟用各式 Providers:

1
2
3
4
5
6
7
8
new WebHostBuilder()
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
})

Console 畫面
console

在程式中使用 logger

直接利用 DI 取得 ILogger,並叫用 method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyService
{
private readonly ILogger<MyService> _logger;

public MyService(ILogger<MyService> logger)
{
_logger = logger;
}

public void DoSomething()
{
_logger.LogInformation("Doing something ...");
//.... do something
}
}

Log event ID

輸出 log 時,可指定一個 event id 作為把 log 歸類用。

1
_logger.LogInformation(1001, "Getting item {ID}", id);

輸出內容

1
2
3
4
info: TodoApi.Controllers.TodoController[1001]
Getting item invalidid
warn: TodoApi.Controllers.TodoController[4000]
GetById(invalidid) NOT FOUND

Change Log Level

ASP.NET Core logging 有 6 種 levels,依重要性的輕到重排列如下:

  • Trace = 0
  • Debug = 1
  • Information = 2
  • Warning = 3
  • Error = 4
  • Critical = 5

各有對應的輸出 method 供使用,如 Information 就用 logger.LogInformation(…)。
可以利用 log filtering 來控制要輸出哪個 level 以上的 log 內容

Log filtering

如果是利用 CreateDefaultBuilder() 做初始化,則預先取 appsettings.json 中的Logging 段內容做設定值,像logging.AddConfiguration(hostingContext.Configuration.GetSection(“Logging”));

手動設定

1
2
3
4
5
6
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureLogging(logging =>
logging.AddFilter("System", LogLevel.Debug)
.AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Trace))
.Build();

第一個 AddFilter 是對全部 Provider 即預設,第二個是針對 DebugLoggerProvider。

Default minimum level

當設定或程式中沒有規則適用於給定的 Provider 和 category 時,一個 minimum level 值會生效,可以用以下方式做設定:

1
.ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Warning))

Filter functions

可以利用程式邏輯來處理 log 的過濾

1
2
3
4
5
6
7
8
9
10
11
12
.ConfigureLogging(logBuilder =>
{
logBuilder.AddFilter((provider, category, logLevel) =>
{
if (provider == "Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider" &&
category == "TodoApi.Controllers.TodoController")
{
return false;
}
return true;
});
})

ASP.NET Core Application Frameworks

ASP.NET Core 已將 MVC 和 Web API 整合到相同的 Controller 中,只要啟用 MVC 即已提供 Web API 功能。

Startup 中啟用 MVC 的情況

1
2
3
4
5
6
7
8
9
10
11
12
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}

public void Configure(IApplicationBuilder app)
{
app.UseMvcWithDefaultRoute();
}
}

Web API 範例

Route

通常會以 api 開頭

1
2
[Route("api/[controller]")]
public class UsersController : Controller

Return data from an API

get item / get items,限定 [HttpGet]

1
2
3
4
5
6
7
8
9
10
11
[HttpGet]
public User[] Get()
{
return new[] {new User() {...}, new User() {...}};
}

[HttpGet("{id}")]
public User Get(int id)
{
return users.FirstOrDefault(x => x.Id == id);
}

Update data using APIs

add item,限定 [HttpPost]
remove item,限定 [HttpDelete]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Adding user
[HttpPost]
public IActionResult Update([FromBody] User user)
{
return new CreatedResult($"/api/users/{user.Id}", user);
}

// Deleting user
[HttpDelete]
public IActionResult Delete([FromQuery] int id)
{
var user = users.SingleOrDefault(x => x.Id == id);

if (user != null)
{
users.Remove(user);
return new EmptyResult();
}

return new NotFoundResult();
}

  • [FromBody][FromQuery] 用來限定資料來源。
  • Web API 提供了常用的標準 Result,如:
    • CreatedResult() - 用於資料新增會回 201(created)
    • EmptyResult() - 回 200(OK)
    • NotFoundResult() - 回 404

ASP.NET MVC Core

ASP.NET MVC Core 延續 ASP.NET MVC 的使用體驗,對有經驗的開發人員可以很快上手。

包含以下功能

  • Routing
  • Model binding
  • Model validation
  • Dependency injection
  • Filters
  • Areas
  • Web APIs
  • Testability
  • Razor view engine
  • Strongly typed views
  • Tag Helpers
  • View Components

一些新語法或新功能說明如下:

Routing

於 Startup設定基本 routing

1
2
3
4
5
6
7
8
9
app.UseMvcWithDefaultRoute();

// 等同於
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});

Attribute routing

1
2
3
4
5
6
7
8
9
[Route("api/[controller]")]
public class ProductsController : Controller
{
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
...
}
}

Dependency injection into views

在 ASP.NET Core 的 DI 機制下,除了 controllers 可 inject 所需的元件外,View 也可以。

使用 @inject directive,語法:@inject <type> <name>

1
2
3
4
5
6
7
8
9
10
@inject SomeService ServiceName
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ServiceName.GetTitle</title>
</head>
<body>
<h1>@ServiceName.GetTitle</h1>
</body>
</html>

View inject 的使用場合可以是:

A. 頁面資料提供

View inject 的一個應用場合就是取得選單資料。較獨立的選單資料,以往要透過 ViewBag 或 Model 來傳遞給 View,現在可以利用獨立元件提供並 inject 到 view 中。

1
2
3
4
@inject ProfileOptionsService Options

Gender: @Html.DropDownList("Gender",
Options.ListGenders().Select(g => new SelectListItem() { Text = g, Value = g }))

B. Overriding Services

除了 inject 新元件,也可以用來取代已有的元件,如 HTML Helpers。
下例即為用 MyHtmlHelper 取代系統的 Html Helper

1
2
3
4
5
@inject MyHtmlHelper Html

<div>
Test: @Html.Value
</div>

View 相關改進

Tag Helpers

利用 server side 的程式去建立 Razor 的自定 tag (element) 如 或取代 html 的 tag 加上和 MVC 功能的結合,讓頁面內容更易讀。
系統已有內建一般用途的 Tag Helpers 如 input, form, label, and select,從 NuGet 及 GitHub 可取得更多。

像 EnvironmentTagHelper 能夠載入不同的 scripts 到頁面中

1
2
3
4
5
6
7
8
9
<environment names="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
</environment>
<environment names="Staging,Production">
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery">
</script>
</environment>

針對 Text Box,以往用 HTML Helper 是

1
@Html.TextBoxFor(m=>m.Email, new { @class = "form-control" })

改用 Tag Helper 則是

1
<input asp-for="FirstName" class="form-control" />

ASP.NET Core 的 _Layout.cshtml 已用 Tag Helper 改寫,可以和 ASP.NET MVC 5 的版本比較,就可以知道差異。

客製自己的 Tag Helpers

假設要建一個 url tag <url>…</url> ,用法如:
<url>https://www.web.com/resources/ebooks</url>

作法如下

  • 命名為 XXX + TagHelper
  • 繼承 TagHelper
  • overriding Process() or ProcessAsync(),接 2 個參數 context 和 output

範例如下

預設放在 TagHelpers 目錄下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace MvcSample.TagHelpers
{
public class UrlTagHelper: TagHelper
{
public override async Task ProcessAsync(TagHelperContext context,
TagHelperOutput output)
{
output.TagName = "a";
var content = await output.GetChildContentAsync();
output.Attributes.SetAttribute("href", content.GetContent());
}
}
}

用法如下

要告知 VS 和 ASP.NET Core 要導哪個元件。在 Views 目錄下有個 _ViewImports.cshtml 專門做這個定義,在它裡面新增一行宣告:

1
@addTagHelper *, MvcSample

注意,namespace 後面不用加上 Tag 名稱, * 已表示載入 MvcSample 下的 tags 了。

View components

有點像 partial view,但更元件化,有不同的使用情境。

它不存取 Model,單純用傳入的參數內容來產生頁面,可用來製作有邏輯處理的重覆使用頁面元件。

建立 view component

view component 由兩個部份組成:類別razor view
類別統一放在 ViewComponents 目錄中,razor view 建議放在 Views/Shared/Components 下。

View component 類別

  • 繼承 ViewComponent 或在 class 上掛 [ViewComponent] attribute
  • 命名為 XXX + ViewComponent
  • 要實作或提供 Invoke 或 InvokeAsync method,並回傳 IViewComponentResult

View search path

  • Views/Shared/Components// - 官方建議位置
  • Views//Components//

檔名為 Default.cshtml

範例,建立 Sidebar 元件

SideBarViewComponent class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SideBarViewComponent : ViewComponent
{
private readonly ILinkRepository db;

public SideBarViewComponent(ILinkRepository repository)
{
db = repository;
}

public async Task<IViewComponentResult> InvokeAsync(int max = 10)
{
var items = await db.GetLinks();
return View(items.Take(max));
}
}

View : \Views\Shared\Components\SideBar\Default.cshtml

1
2
3
4
5
6
7
8
@model IEnumerable<CoreWebApplication1.Models.Link>
<h3>Blog Roll</h3>
<ul>
@foreach (var link in Model)
{
<li><a href="@link.Url">@link.Title</a></li>
}
</ul>

使用 view component

有 2 種方式:

  1. @Component.InvokeAsync(“SideBar”, new { max = 5 }) 或
    Async 版本 @await Component.InvokeAsync(“SideBar”, new { max = 5 })
  2. Tag Helper 方式 , 即

Deploy ASP.NET Core Apps

Deploying on IIS

基本上,ASP.NET Core 不需要依賴特定外部伺服器就能自己執行,配合自帶的 Kestrel Server 能夠提供很高的執行效率,但輕量化的結果是缺少完整的安全及管理功能。

配合 IIS 後,IIS 接近 proxy 的角色,主要負責轉發 request 和 response 並維持服務的執行狀態,把 2 者的優點結合起來用。

ASP.NET Core Module - ANCM

為了讓 IIS 和 ASP.NET Core 能合作,必須在 IIS 上安裝具 reverse proxy 功能的專用 module 叫 AspNetCoreModule。在開發環境中,此 module 已隨著 SDK 安裝,所以不必額外安裝。

安裝 ANCM

ANCM 包含在這個安裝包裡:.NET Core Windows Server Hosting bundle (現在是version 2.0.3)。另外還包含 .NET Core Runtime, .NET Core Library。

安裝後,可在 IIS 的 模組 介面中看到
IIS Module

執行 UseIISIntegration

當 WebHostBuilder() 執行在 IIS 環境下時,UseIISIntegration() extension method 會自動被執行來配合 ANCM 和 IIS;在 macOS 或 Linux 下,則是 Kestrel 跑起來做 web server。

Create IIS Website

web.config

ASP.NET Core Module 及 IIS 的相關設定是設在 web.config 中,如果專案中沒有 web.config,在發佈時會自動加入;如果有,則會做適當的整合,加上必要的設定值,如下例:

1
2
3
4
5
6
7
8
9
10
11
12
<system.webServer>
<handlers>
<add name="aspNetCore"
path="*" verb="*"
modules="AspNetCoreModule"
resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet"
arguments=".\CoreWebApplication1.dll"
stdoutLogEnabled="false"
stdoutLogFile=".\logs\stdout" />
</system.webServer>

應用程式集區 Application Pools 的設定

因為只是 proxy 的角色沒有執行任何的 .NET code,所以應用程式集區的 .NET Framework 版本要設定為 沒有 Managed 程式碼 (No Managed Code)。

Deploy

  1. 可透過命令列, 如:

    1
    2
    3
    4
    dotnet publish 
    --framework netcoreapp2.0
    --output "D:\Publish\CoreWebsite1"
    --configuration Release
  2. 透過 Visual Studio 的 發行。

ASP.NET Core 的相關開發工具

dotnet CLI

可以用 dotnet CLI 來執行以下常用的開發操作

  • new - to create a new .NET Core project.
  • restore - to download all dependencies from NuGet.
  • build - to compile the projects.
  • publish - to generate a self-contained folder used for deployment.
  • run - to run a project, building it if it’s not already built.
  • pack - to package the project as a NuGet package.
  • msbuild - used as a proxy for the standard MSBuild command.

new

new 命令後面配合不同參數,可以指定建立的專案類型:

  • new console - 建立 Console app
  • new mvc – 建立 MVC app
  • new classlib – 建立 Class Library

build 和 publish

能指定 framework, runtimes (-r) 和 configuration (debug or release)。

runtimes 可利用 [os].[version]-[arch] 的樣式來指定不同系統,如 osx.10.11-x64,而內容也會輸出到 osx.10.11-x64 子目錄下。如:

1
2
3
dotnet publish -r win10-x64
dotnet publish -r osx.10.11-x64
dotnet publish -r ubuntu.14.04-x64

Visual Studio Code

Visual Studio Code 是一個多平台 (Windows, Mac, or Linux) 的輕量編輯器。利用 Extensions 功能可以擴展功能以支援各種不同的程式語言及系統開發。

用 CLI 建立的專案,可以配合 VSCode 來做開發。

OmniSharp

為一個 open-source 專案,提供各式編輯器開發 .NET 專案的能力,VSCode 即是透過 OmniSharp 的 Extensiion 來提供 C# 的支援。

設定 Visual Studio Code

  • https://code.visualstudio.com/ 下載 VSCode 並完成安裝。
  • 從 Extensions 面板,安裝 C# extension (identifier: ms-vscode.csharp) 現在的版本是 1.13.1 支援 .NET Core 2.0 及 C# 7.1。
  • 第一次開啟 .NET Core 專案, 會下載並安裝適當版本的套件,輸出內容如:

    1
    2
    Installing package 'OmniSharp for Windows (.NET 4.6 / x64)'
    Installing package '.NET Core Debugger (Windows / x64)'
  • 詢問是否加入 VSCode 所需的設定 (Required assets …),請回答 Yes,會新增 .vscode 目錄。

用 Visual Studio Code 開發

當在編輯程式時,和 Visual Studio 類似可以得到以下協助:

  • IntelliSense and code completion
  • linting and refactoring suggestions
  • code navigation

Debugging

進到 Debug 面板,可以對程式做 debug。設定中斷點,並點選上方綠色執行鍵開始 debug。

結語

.NET Core 帶來 .NET 未來十年應用程式開發的新環境,而 2.0 版的推出讓它的實用性大大提升,近來文章分享也都集中在 .NET Core 上,現在是學習及應用的好時機點。

參考資料及圖片來源

  1. .NET 指南
  2. .NET Core 指南
  3. Announcing the .NET Framework 4.7.1