ASP.NET Core 6 專案範本
ASP.NET Core 6 專案範本
前言
當開發同類型的專案一段時間都會有一些固定的設計模式,
 或是一些常開發的功能:身分驗證、資料修改、郵件發送、報表產出。
然而每次開發專案都要從舊專案複製過來刪改,
 或是另開新的專案範本重新建立資料夾和各類程式片段,
 無形之中浪費許多人的時間,
 對於多人協作專案沒有可依循的規範容易造成許多歧見。

本次目標為建立一個 Web API 專案範本,然後丟到 Nuget 上。
動機
之所以會想要搞個 ASP.NET Core 專案範本,
 其動機由來是因為接觸了 Laravel 框架。
體驗到一個專注以網頁開發為主的框架對於開發體驗的幫助。
網頁開發會碰到的需求幾乎都有支援的套件可以快速實作,
 官方也有詳細的官方文件讓多人團隊有規範可以依循,
 進而提升團隊整體的軟體品質與降低後續接手維護的成本。
範本類型
ASP.NET Core 有兩種專案範本
- 專案範本
- 項目範本
本次教學以專案範本為例。
專案範本
用 Visual Studio 建立新專案會看到的就是專案範本

項目範本
用 Visual Studio 在專案內新建項目的就是項目範本

專案範本
建立專案範本
建立一個 Web API 專案範本。

修改專案範本
加入自己要的東西或是做一些修改。
例如本次範例我們建立一個 ViewModels 資料夾,
 並且把 WeatherForecast.cs 放進去。

建立範本設定
範本資料夾中的所有檔案和資料夾都會包含為範本的一部分,除了特殊設定資料夾之外。
 預設會被忽略的檔案和資料夾可見 範本設定參考。
在專案資料夾內新增範本設定檔 .template.config/template.json。
 可參考官方的範例或是下方範例
{
  "$schema": "http://json.schemastore.org/template",
  "author": "Pamis Wang",
  "classifications": ["Web", "WebAPI"],
  "identity": "Pamis ASP.NET Core 6 Web API",
  "name": "Pamis ASP.NET Core 6 Web API",
  "shortName": "pamis-webapi",
  "sourceName": "WebAPI",
  "tags": {
    "language": "C#",
    "type": "project"
  },
  "preferNameDirectory": true,
  "sources": [
    {
      "modifiers": [
        {
          "exclude": ["**/[Bb]in/**", "**/[Oo]bj/**", ".template.config/**/*", "**/*.filelist", "**/*.user", "**/*.lock.json", "**/.vs/**", "**/.vscode/**", "**/.git/**/*"]
        }
      ]
    }
  ]
}範本設定說明
這下表格為必須使用的設定
| 成員 | 類型 | 描述 | 
|---|---|---|
| $schema | URI | template.json 檔案的 JSON 結構描述。 VSC 需要此成員才能啟用 IntelliSense。 | 
| author | string | 範本的作者。 | 
| classifications | array(string) | 範本的分類。出現在使用 dotnet new list 命令時 | 
| identity | string | 此範本的唯一名稱。 | 
| name | string | 使用者應該會看到的範本名稱。 | 
| shortName | string | 此範本名稱是由使用者指定,以 dotnet CLI 命令可輸入簡短名稱。 | 
| sourceName | string | 要取代的專案名稱,詳細說明看下方補充 | 
以下則為其他可用的設定
| 成員 | 類型 | 描述 | 
|---|---|---|
| preferNameDirectory | string | 指出是否要在指定名稱但未設定輸出目錄時,為範本建立目錄。預設值為 false。 | 
| tags | object | 物件屬性有 language和type 。 | 
| language | string | language是程式語言,通常為C#、VB、 F#。 | 
| type | enum | type 是範本類型,project(專案範本)、item(項目範本)、solution(解決方案) | 
| exclude | array(string) | 要排除的資料夾和檔案 | 
當建立範本用 dotnet new <範本名稱> -n <專案名稱> ,
 會將 sourceName 的字串替換成 -n 指定的專案名稱,
 一般來說就是打範本的專案名稱。
當建立範本用 dotnet new <範本名稱> -n <專案名稱>preferNameDirectory 會影響專案建立的資料夾設定
 預設是 false(很多教學可能因為版本差異都寫成true)
- true:在目前指向的目錄建立一個與專案名稱相同的資料夾與專案內容。 
- false:在目前指向的目錄建立專案內容,「不會」建立與專案名稱相同的資料夾。 
exclude 預設只會排除以下規則的檔案與資料夾:bin、obj、.template.config、.filelist、.user、.lock.json
考慮到專案可能會加入版控,
 以及用 IDE 開專案會冒出一堆鬼東西,
 故手動加入其他要排除的資料夾。
匯出範本專案
執行 dotnet CLI 指令
dotnet new install .\dotnet new install ./出現下面的訊息就是成功了。

打包套件
剛剛我們建立好了專案範本,現在要打包成 NuGet 套件包。
調整目錄結構
建立一個空資料夾,裡面要新增一個 .csproj 和一個 templates 資料夾。
例如下圖這樣:

templates 資料夾就放上面做好的專案範本。
撰寫套件描述
開啟外層的的 .csproj 檔案,加入必要的 XML 格式標籤。
 參考官方教學提供的範例來調整。
套件描述範例
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <PackageId>PamisWang.Template.WebAPI</PackageId>
        <PackageType>Template</PackageType>
        <PackageVersion>0.0.3</PackageVersion>
        <Title>Pamis Wang Template Web API</Title>
        <Authors>Pamis Wang</Authors>
        <Description>Web API 範本</Description>
        <PackageTags>dotnet-new;templates</PackageTags>
        <PackageReadmeFile>README.md</PackageReadmeFile>
        <PackageIcon>favicon.png</PackageIcon>
        <RepositoryUrl>https://github.com/pamis-wang/PamisWangTemplateWebAPI</RepositoryUrl>
        <PackageReleaseNotes>修正套件的目標框架與補上相依套件</PackageReleaseNotes>
        <PackageLicenseExpression>MIT</PackageLicenseExpression>
        <TargetFramework>net6.0</TargetFramework>
        <IncludeContentInPack>true</IncludeContentInPack>
        <IncludeBuildOutput>false</IncludeBuildOutput>
        <ContentTargetFolders>content</ContentTargetFolders>
        <NoWarn>$(NoWarn);NU5128</NoWarn>
        <NoDefaultExcludes>true</NoDefaultExcludes>
    </PropertyGroup>
    <ItemGroup>
        <Content Include="templates\**\*" Exclude="templates\**\bin\**;templates\**\obj\**" />
        <Compile Remove="**\*" />
        <None Update="favicon.png" Pack="true" PackagePath="\" />
        <None Update="README.md" Pack="true" PackagePath="README.md" />
    </ItemGroup>
    <ItemGroup>
        <PackageReference Include="Dapper" Version="2.0.143" />
        <PackageReference Include="MailKit" Version="4.1.0" />
        <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer" Version="5.1.0" />
        <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.10" />
        <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
        <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.10" />
        <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
        <PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.2" />
        <PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.15" />
        <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
        <PackageReference Include="Oracle.EntityFrameworkCore" Version="7.21.11" />
        <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
        <PackageReference Include="Razor.Templating.Core" Version="1.8.0" />
        <PackageReference Include="RestSharp" Version="110.2.0" />
        <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
        <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0" />
        <PackageReference Include="System.DirectoryServices" Version="7.0.1" />
        <PackageReference Include="System.DirectoryServices.AccountManagement" Version="7.0.0" />
        <PackageReference Include="System.DirectoryServices.Protocols" Version="7.0.1" />
    </ItemGroup>
</Project>套件描述說明
列出一些會用到的,更多的屬性標籤與用法請參考官方文件。
| 屬性名稱 | 描述 | 
|---|---|
| PackageId | 套件名稱或識別碼。 | 
| PackageType | 指出套件的封裝類型 | 
| PackageVersion | NuGet 套件版本。 | 
| Title | 套件的簡短標題 | 
| Authors | 以逗號分隔的套件作者清單 | 
| Description | 套件的描述。 | 
| PackageTags | 以空格分隔的標記與關鍵字清單 | 
| PackageReadmeFile | 套件讀我檔案的路徑。 | 
| PackageIcon | 套件圖示影像檔的路徑。 | 
| RepositoryUrl | 用來建置封裝的存放庫 URL | 
| IsPackable | 指定是否可封裝專案 | 
| EnableDefaultContentItems | 設定是否要啟用 SDK 型專案預設排除的檔案設定 | 
| NoDefaultExcludes | 設定不要排除 NuGet 文件以及以點開頭的檔案和資料夾,例如 .svn 和 .gitignore。 | 
| IncludeContentInPack | 類型 Content 的專案是否自動包含在產生的套件中。預設為 true。 | 
| IncludeBuildOutput | 決定是否應該在套件中包含組建輸出組件。 | 
| ContentTargetFolders | 套件產生的檔案預設會放在 content | 
| TargetFramework | 設定可確保 MSBuild 會在您執行 pack 命令以編譯和封裝專案時正常執行。 | 
| PackageReference | 指定套件相依性 | 
| Content | 設定有哪些檔案要被打包或被排除 | 
| None Update | 排除指定檔案被打包到 content | 
打包套件
在專案目錄下輸入指令就會開始打包
dotnet pack打包成功的話預設會在 \bin\Debug 產生 .nupkg 檔案。
安裝套件
當套件打包好之後可以直接在本機進行安裝
dotnet new install [nupkg的檔案路徑]
使用範本
安裝好之後就可以來測試看看,
 首先查詢專案範本列表看有沒有安裝成功。
dotnet new list
建立專案指令後看到成功建立專案項目。
dotnet new pamis-webapi
懶得打指令的也可以從 Visual Studio 的專案項目畫面做選擇
 只是不懂為什麼我家貓貓臉變黑了......

發佈到 NuGet 上
首先要註冊帳號
 接著右上角的上傳

上傳完畢等伺服器建立索引就會出現套件頁面了

不過目前 NuGet 上傳後就不能刪除了 XD
後記
撰寫這篇文章的時候花很多時間在排除套件打包的問題,
 因為微軟官方文件關於建立範本套件的教學是基於主控台為例子
 而每種專案內容的專案描述都有很多預設設定要另外查。
 所以整個套件打包的過程是各種坑,
 不過還好 dotnet CLI 的錯誤訊息都滿清楚的,
 只要跟著訊息去逐一解決就會成功。
參考資料
範本專案
教學課程:建立專案範本
教學課程:建立範本套件
dotnet new 的自訂範本
自訂 dotnet new 專案範本的重要觀念與範例
Reference for template.json
發布範本
NuGet 以目標的形式 MSBuild 封裝和還原
.NET 專案 SDK
使用 MSBuild 建立 NuGet 套件
套件撰寫最佳做法