近年來,我日常寫 ASP.NET Core 網站的起手式都是先開 Minimal API 專案,功能不太複雜的話,單一 Program.cs 不到 200 行程式把功能寫完,用最少資源搞定,不含半點贅肉,符合我追求的極簡精神。

不過,當程式愈寫愈複雜,就得朝向前後端分離,建個 wwwroot 資料夾放 html、css、js 處理 UI 是個好主意;若後端邏輯再複雜一些,伺服端邏輯全擠在 Program.cs 的 MapGet("/...")、MapPost("/...") 太過雜亂,不好讀又難維護,此時我會選擇拆出一到多個 Controller 把相關邏輯集中在一起;若 UI 更複雜一點且前後端邏輯交錯,則導入 Razor View .cshtml 可讓 UI 層簡潔有條理一些。整個發展策略走一個「先用較少資源完成,能動就好,有需要再用架構複雜度換取簡潔及可維護性」的漸進式升級概念。

要採用這種漸進式擴充戰術,學會怎麼在 Minimal API 專案新增 wwwroot 靜態檔資料夾、啟用 Controller,乃至於使用 .cshtml View,便成了基本技能。

這篇筆記整理為 Minimal API 擴充 MVC 功能的做法,以利日後參考。

我用一個簡單 Minimal API 專案示範,逐步擴充 wwwroot、Controller 到 .cshtml View 功能,為避免失焦,各階段以可運作的最小組合為準,整理要啟用各項功能的最小關鍵要素。

初陣

系統建立的空白專案,全部邏輯都集中在 Program.cs,只有三行:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");

wwwroot 與 index.html

我打算用 MapPost("/sha1") 寫個簡單的計算 SHA1 雜湊功能,UI 則放在 index.html。故新增 wwwroot 資料夾放入 index.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Home</title>
    <style>
        iframe {
            height: 40px;
            width: 300px;
            margin-top: 5px;
        }
    </style>
</head>
<body>
<form action="/sha1" method="post" target="result">
    <input name="data" value="Hello, World!" />
    <button>Calculate SHA1</button>
</form>
<iframe name="result"></iframe>
</body>
</html>

Program.cs 需 app.UseDefaultFiles();app.UseFileServer(); 以啟用 wwwroot 並將 index.html 設為預設文件 (URL / 結尾自動導向 /index.html):

using System.Security.Cryptography;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

// 指定 index.html 為 wwwroot 預設文件
app.UseDefaultFiles();
// 啟用 wwwroot 靜態檔資料夾
app.UseFileServer();

app.MapPost("/Sha1", async (HttpContext ctx) =>
{
    var hash = BitConverter.ToString(
        SHA1.Create().ComputeHash(
            Encoding.UTF8.GetBytes(ctx.Request.Form["data"].ToString())))
    .Replace("-", string.Empty);
    ctx.Response.ContentType = "text/html";
    await ctx.Response.WriteAsync($"<pre>{hash}</pre>");
});

app.Run();

此階段修改可參考 Add wwwroot & index.html Commit

啟用 Controller

接著,試著將伺服器端邏輯拆出來,新增 Controllers/CryptoController.cs,將 MapPost("/sha1"...) 邏輯搬過來:

using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Mvc;

namespace min_api_add_mvc.Controllers
{
    public class CryptoController : Controller
    {
        [HttpPost]
        public IActionResult Sha1(string data)
        {
            var hash = BitConverter.ToString(
                    SHA1.Create().ComputeHash(
                        Encoding.UTF8.GetBytes(data.ToString())))
                .Replace("-", string.Empty);
            return Content($"<pre>{hash}</pre>", "text/html");
        }
    }
}

Program.cs 則加入 builder.Services.AddControllers()app.MapControllers();app.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");

var builder = WebApplication.CreateBuilder(args);

// 啟用 Controller
builder.Services.AddControllers();

var app = builder.Build();
app.UseDefaultFiles();
app.UseFileServer();

// 指定 Controller 路由
app.MapControllers();
app.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");

index.html Form 送出 URL 由 /sha1 修改為 /crypto/sha1。

此階段修改可參考 Add controller Commit

啟用 .cshtml View

要啟用 Razor View,修改幅度較大,首先 .csproj 加入參照: (此處以 .NET 8 為例)

<ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter" Version="8.0.0" />
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.0" />
</ItemGroup>

新增 Views/Shared/_Layout.cshtml、Views/_ViewStart.cshtml 及 Views/Crypto/Sha1.cshtml (Sha1.cshtml 內容如下)。

@model string
<pre>@Model</pre>

CryptoController.Sha1(string data) 由原本 return Content($"<pre>{hash}</pre>", "text/html"); 改為 return View("Sha1", hash);

Program.cs 的 builder.Services.AddControllers(); 改為 builder.Services.AddMvc();

這部分細節有點瑣碎,建議看 Enable .csthml views Commit 的修改前後對照,應該就很清楚了。

就醬,寫程式也能仿效 MVP (Minimum Viable Product) 精神,先從 Minimal API 專案出發,視需要逐步擴充到 MVC,由簡到繁,只把氣力花在刀口上,讚!

Tutorial of how to enable MVC features on ASP.NET Minimal API projects.


Comments

Be the first to post a comment

Post a comment