多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Aspire 项目模板完全指南:模板包结构、版本更新、本地化与测试

Aspire 项目模板完全指南:模板包结构、版本更新、本地化与测试 Aspire 项目模板完全指南模板包结构、版本更新、本地化与测试【免费下载链接】aspireAspire is the tool for code-first, extensible, observable dev and deploy.项目地址: https://gitcode.com/GitHub_Trending/as/aspireAspire.NET Aspire在 src/Aspire.ProjectTemplates/ 目录下维护了一套官方项目模板用于快速生成 AppHost 编排器、ServiceDefaults、Starter 示例应用与集成测试项目。本文以仓库中的 README.md 为主线深入模板包的物理结构、打包构建机制、每个新版本发布时的更新流程、本地化文件维护方式以及端到端模板测试的运行方法帮助你在理解模板原理的基础上独立完成模板的更新、构建与验证。一、模板包的整体布局一个目录、一个 csproj、一套 templates模板的所有内容都集中在src/Aspire.ProjectTemplates/目录下其组织方式非常清晰Aspire.ProjectTemplates.csproj 负责构建并产出模板 NuGet 包templates/ 是模板内容的根目录其下每个子目录对应一个独立的项目模板README.md 是本仓库对模板维护者的官方说明即本文讲解的源文档。从 csproj 的元数据可以看出这个包是一个标准的 .NET 模板引擎包PackageTypeTemplate/PackageType DescriptionAspire Template Pack for Microsoft Template Engine/Description ContentTargetFolderscontent/ContentTargetFolders EnableDefaultItemsfalse/EnableDefaultItems UsingToolTemplateLocalizertrue/UsingToolTemplateLocalizer其中PackageTypeTemplate让dotnet new能够识别并安装该 NuGet 包EnableDefaultItemsfalse意味着文件全部通过显式 ItemGroup 纳入见下文 csproj 中的TemplateConfigFiles、TemplateProjectFiles、TemplateFiles三组文件声明UsingToolTemplateLocalizertrue则启用了模板本地化工具链用于生成和维护多语言描述文件。1.1 内置模板清单截至当前仓库templates/目录下共包含 9 个模板其中aspire-js-frontend-starter为纯前端引导模板不含.template.config每个模板都包含一个.template.config/配置目录内含template.json、dotnetcli.host.json、ide.host.json模板目录shortName类型说明aspire-apphostaspire-apphostproject创建 Aspire AppHost编排器项目aspire-apphost-singlefileaspire-apphost-singlefileproject单文件 AppHost适用于脚本化/单文件分发场景aspire-emptyaspire-emptysolution空解决方案AppHost ServiceDefaultsaspire-starteraspire-startersolutionStarter 示例Blazor Web 前端 Web API 后端 可选 Redis 缓存 可选测试项目aspire-ts-cs-starteraspire-ts-cs-startersolutionTypeScript 前端Vite ASP.NET Core 后端aspire-servicedefaultsaspire-servicedefaultsproject服务默认配置项目OpenTelemetry、健康检查、服务发现等aspire-xunitaspire-xunitprojectxUnit.net 集成测试项目aspire-mstestaspire-mstestprojectMSTest 集成测试项目aspire-nunitaspire-nunitprojectNUnit 集成测试项目每个模板的template.json中都声明了identity如Aspire.AppHost.CSharp.8.0、groupIdentity、shortName、sourceName与precedence等元数据供dotnet new、Visual Studio 与 VS Code 识别和分组。值得注意的一点是模板中的文件大量使用占位符如Aspire.AppHost1.csproj、AspireApplication.1.sln其中的版本号与项目名会在创建时由模板引擎替换。二、模板的打包构建机制版本占位符如何被替换模板内容并非原样进入 NuGet 包。Aspire.ProjectTemplates.csproj中定义了一组 MSBuild Target构成了“复制 → 替换版本 → 收进包”的三段式流水线这也是理解“更新模板”这一流程的关键。2.1 文件分组与中间目录csproj 通过三个 ItemGroup 收集模板文件并统一映射到包内content/templates/路径下TemplateConfigFilestemplates\**\.template.config\*.json即每个模板的引擎配置文件TemplateProjectFilestemplates\**\*.csproj以及单文件模板特有的apphost.csTemplateFiles其余所有文件并排除bin\**、obj\**、*.csproj等不需要打包的内容。CopyTemplatesToIntermediateOutputPathTarget 负责把这些文件复制到中间目录$(IntermediateOutputPath)content\templates为后续替换做准备。2.2 版本占位符替换ReplacePackageVersionOnTemplates模板的 csproj、配置与本地化文件中充斥着形如!!REPLACE_WITH_LATEST_VERSION!!的占位符。例如 Aspire.AppHost1.csproj 第一行就是Project SdkAspire.AppHost.Sdk/!!REPLACE_WITH_LATEST_VERSION!!ReplacePackageVersionOnTemplates Target 使用仓库根目录 tools/scripts/replace-text.cs 这一小工具把模板中所有占位符一次性替换为当前构建的真实版本。替换规则在 csproj 中成对声明先占位符、后替换值当前主要的映射包括占位符替换为用途!!REPLACE_WITH_LATEST_VERSION!!$(PackageVersion)完整的 Aspire 包版本如9.0.0!!REPLACE_WITH_LATEST_MAJOR_MINOR_VERSION!!$(PackageVersion)的主.次号用于模板identity等需要主版本区分的场景!!REPLACE_WITH_ASPNETCORE_OPENAPI_9_VERSION!!等对应的 OpenAPI 包版本属性ASP.NET Core OpenAPI 相关包版本!!REPLACE_WITH_DOTNET_EXTENSIONS_VERSION!!$(MicrosoftExtensionsHttpResilienceVersion)Microsoft.Extensions.Http.Resilience!!REPLACE_WITH_SERVICE_DISCOVERY_VERSION!!$(MicrosoftExtensionsServiceDiscoveryVersion)Microsoft.Extensions.ServiceDiscovery!!REPLACE_WITH_OTEL_EXPORTER_VERSION!!/!!REPLACE_WITH_OTEL_HOSTING_VERSION!!/!!REPLACE_WITH_OTEL_ASPNETCORE_VERSION!!/!!REPLACE_WITH_OTEL_HTTP_VERSION!!/!!REPLACE_WITH_OTEL_RUNTIME_VERSION!!对应的 OpenTelemetry 包版本属性OpenTelemetry 相关包版本替换后的文件通过AddTemplatesToPackageAsContentTarget 作为Content项加入包中确保随 NuGet 发布的是已写入正确版本号的最终内容。2.3 模板配置的符号系统以 aspire-apphost 为例每个模板的 template.json 定义了模板引擎可用的“符号”symbols这些符号直接决定了dotnet new的可用参数。以aspire-apphost为例核心符号包括Framework目标框架选择支持net8.0/net9.0/net10.0/net11.0默认值net10.0并通过replaces: net8.0将模板文件中的net8.0字符串替换为所选框架端口类符号appHostHttpPort、appHostHttpsPort、appHostOtlpHttpPort、appHostOtlpHttpsPort、appHostResourceHttpPort、appHostResourceHttpsPort每个端口都由“用户参数 自动生成值port生成器在固定区间内随机选端口 coalesce合并替换器”三段式组成。例如 HTTP 端口在 15000–15300、HTTPS 端口在 17000–17300、OTLP HTTP 在 19000–19300、资源服务 HTTP 在 20000–20300 等区间生成用户也可通过-p:appHostHttpPortxxx显式指定NoHttpsCLI 参数--no-https关闭 HTTPS profileLocalhostTldCLI 参数--localhost-tld仅在net10.0/net11.0下可用将应用 URL 改为https://myapp.dev.localhost:12345形式模板通过hostName派生符号与一组 forms转小写、将非法 DNS 字符替换为连字符、压缩重复连字符、去除首尾连字符生成合法的域名前缀skipRestoreCLI 参数--no-restore创建时跳过自动dotnet restoreHasHttpsProfile由NoHttps计算得出的派生符号用于条件性地生成launchSettings.json中的 HTTPS profile。postActions部分则定义了创建后的动作在 Visual Studio 中把 AppHost 设为启动项目以及在未指定--no-restore时执行 NuGet 还原。2.4 Windows 下的目录签名Catalog 文件csproj 中还包含一个仅在 Windows 上运行的GenerateCatalogFilesTargetAspire.ProjectTemplates.csproj。它调用 eng/generate-catalog.ps1对模板中随附的 JS 文件如 Bootstrap、eslint 配置生成.cat目录签名文件并打进 NuGet。原因在注释中写得很清楚这些 JS 文件是用户创建项目后可以自行修改的若使用 Authenticode 直接签名会在文件尾部追加// SIG //块导致后续修改即失效目录签名则是把文件哈希进.cat而不改动文件本身从而在满足 VS 签名合规的同时保留用户可编辑性。三、版本更新流程模板内容、本地化与测试三步走README.md 的核心价值在于给出了“新版本发布时如何更新模板”的明确指引。整个流程分为三个互不干扰的环节。3.1 更新模板内容每个模板的内容中都包含大量由构建过程替换的版本占位符因此在日常开发中无需手工修改版本号——只要版本属性Versions.props中的PackageVersion、各依赖版本属性正确dotnet pack时占位符就会被自动替换。需要特别注意的是非 Aspire 依赖包的版本更新不包含在这个流程内。README 明确说明Updating the versions for non-Aspire packages referenced in all*.csprojfiles isnt covered as part of this process. These package versions should be updated by our regular process for updating the versions of our dependencies.也就是说模板中引用的第三方包如 OpenTelemetry、Microsoft.Extensions 相关包的版本走的是仓库常规的依赖版本更新流程即通过eng/Versions.props与 eng/Version.Details.xml 的依赖流机制而不是模板维护流程。从 csproj 的替换表也能印证OpenTelemetry、ServiceDiscovery 等第三方包的版本同样来自版本属性它们跟随依赖更新而非手改模板。3.2 更新本地化文件模板的template.json中name、description、displayName等可读字符串需要通过本地化文件提供多语言版本。本地化文件位于每个模板的.template.config/localize/目录下如templatestrings.de.json、templatestrings.zh-Hans.jsoncsproj 中的TemplateLocalizedStringsFiles会收集templates\**\.template.config\localize\templatestrings.*.json并参与版本替换。保持本地化文件与模板内容同步的方法就是重新打包。按 README 的指引对模板包项目执行dotnet pack即可让本地化文件随构建重新生成/校验以匹配所有模板内容变更dotnet pack ./src/Aspire.ProjectTemplates/Aspire.ProjectTemplates.csproj这背后的机制是UsingToolTemplateLocalizertrue——Arcade 构建工具链会在打包过程中调用模板本地化工具检查并补齐templatestrings.*.json中缺失或过期的翻译条目。3.3 更新测试模板测试Aspire.Templates.Tests可以用标准测试命令运行。按 tests/Aspire.Templates.Tests/README.md 的说明既可以本地运行也可以直接提交 PR 后在 CI 中观察测试输出——CI 中这些测试通过tests-templates.yml对应的 GitHub Actions 工作流执行。四、模板的端到端测试Aspire.Templates.Tests模板测试与普通单元测试有本质区别它不是测试模板引擎本身而是端到端地模拟真实用户行为——用模板创建项目、构建项目、运行并与 Aspire 项目交互从而验证模板在 CI 流水线和本地开发环境中的可用性。这些测试针对的是预构建好的 NuGet 包nupkg。4.1 测试环境的前提测试需要三个前提条件详见 tests/Aspire.Templates.Tests/README.md一套安装了必要组件的 .NET SDKSdkVersionForTemplateTesting默认取global.json中的 SDK 版本使用本地构建出的 NuGet 包位于artifacts/packages/*/Shipping来安装必要的 Aspire 组件通过指向artifacts中本地包的nuget.config把 SDK 配置为使用本地包源。这套机制把 SDK 安装到artifacts/bin/dotnet-tests模拟“用户机器上独立于 aspire 仓库安装的 SDK”。测试时构建的项目解析 Aspire 组件包时会命中artifacts中的本地包并使用测试专用的 NuGet 缓存例如artifacts/bin/Aspire.Template.Tests/Release/net8.0/nuget-cache-Net80路径会在测试套件启动时打印。本地构建的包版本形如8.0.0-dev或8.0.0-ci。4.2 本地安装测试 SDK# 1. 构建所有 NuGet 包 ./build.sh -pack # Windows 上为 .\build.cmd -pack # 2. 安装 SDK 与必要组件从 artifacts/packages/*/Shipping 安装到 artifacts/bin/dotnet-tests dotnet build tests/workloads.proj /p:Configurationconfig说明artifacts/bin/dotnet-none中只包含带组件清单但不含组件的 SDK真正可用的是artifacts/bin/dotnet-tests。4.3 在仓库外使用测试 SDK手动方式将/path-to-aspire-repo/artifacts/bin/dotnet-tests加入PATH为你的项目添加artifacts/packages/$(Configuration)/Shipping作为 NuGet 源使本地构建的包可被解析。tests/Shared/TemplateTesting/data/nuget8.config 可作模板——它通过环境变量BUILT_NUGETS_PATH引用本地包路径因此需要先设置BUILT_NUGETS_PATH/path-to-aspire/artifacts/packages/$(Configuration)/Shipping。更省事的替代方案Linux/macOSsource /path-to-aspire-repo/dogfood.sh cp /path-to-aspire-repo/tests/Shared/TemplateTesting/data/nuget8.config nuget.config export BUILT_NUGETS_PATH/path-to-aspire/artifacts/packages/$(Configuration)/Shipping4.4 内循环inner loop调试提示工作负载workload与 SDK不会自动更新一旦安装完成即使artifacts中的源码二进制发生变化已安装的 workload packs 也不会被覆盖。测试使用三类 NuGet 包workload packsAspire.Dashboard.Sdk.*、Aspire.TerminalHost.Sdk.*、Aspire.Hosting.Orchestration.*、Aspire.AppHost.Sdk安装在artifacts/bin/dotnet-tests/packs/安装后永不自动更新——修改这些位bits需要手动复制覆盖普通 Aspire NuGet 包不属于 workload由用户项目引用构建时从artifacts解析并使用上述测试专用 NuGet 缓存项目模板包安装在artifacts/bin/dotnet-tests/template-packs/。若修改了模板需要先构建模板 NuGet 包再手动复制到template-packs/aspire.projecttemplates.*.nupkg才能让改动在测试中生效。对于其他 NuGet 包的本地改动有两个选项重新构建包并删除缓存中解包出来的旧副本或者直接复制更改过的文件到 NuGet 缓存中覆盖。后续测试运行即可自动拾取改动。五、快速上手用模板创建项目模板随 Aspire SDK/工作负载一起提供。使用方式与其他dotnet new模板一致例如创建 Starter 应用dotnet new aspire-starter -o MyAspireAppaspire-starter模板template.json在 9.x 版本中提供了比 AppHost 模板更丰富的参数可用于生成不同形态的解决方案--frameworknet8.0/net9.0/net10.0/net11.0默认net10.0--use-redis-cache是否启用 Redis 缓存需要受支持的容器运行时--test-fx是否创建集成测试项目可选None默认、MSTest、NUnit、xUnit.net--xunit-version当选择xUnit.net时可选v2默认、v3、v3mtpxUnit.net v3 Microsoft Test Platform--no-https/--no-restore关闭 HTTPS、跳过创建后还原。生成的 Starter 解决方案包含 AppHost、Web 前端Blazor、ApiService、ServiceDefaults 以及可选测试项目。以 AppHost 的 AppHost.cs 为例可以看到模板预设的资源编排骨架注册 Redis 缓存条件编译符号UseRedisCache控制、注册apiservice并附加 HTTP 健康检查、注册webfrontend并声明外部 HTTP 端点与健康检查再通过WithReference/WaitFor建立服务引用与启动依赖——UseRedisCache、TestFx等符号正是通过条件编译和模板sources.modifiers.exclude例如未选测试框架时排除*.Tests/**来控制最终生成的文件集合。六、维护者速查清单综合 README 与 csproj 源码为模板维护者整理一份操作清单改模板内容直接修改src/Aspire.ProjectTemplates/templates/template/下的文件版本号一律使用!!REPLACE_WITH_...!!占位符不要手写具体版本改第三方依赖版本走常规依赖更新流程更新eng/Versions.props等版本属性模板文件本身不动同步本地化对 Aspire.ProjectTemplates.csproj 执行dotnet pack让本地化文件与模板变更保持同步验证测试先build.sh -pack构建全部 NuGet 包再dotnet build tests/workloads.proj安装测试 SDK最后运行Aspire.Templates.Tests或在 CIGitHub Actions 的tests-templates.yml中观察结果模板本地改动生效构建模板包后手动复制到artifacts/bin/dotnet-tests/template-packs/aspire.projecttemplates.*.nupkgworkload 位改动生效手动把改动后的 workload pack 覆盖到artifacts/bin/dotnet-tests/packs/。按此流程即可让模板包在新版本发布时保持内容、本地化与测试三者一致确保用户在dotnet new和 IDE 新建项目向导中拿到的模板始终可用、可构建、可运行。【免费下载链接】aspireAspire is the tool for code-first, extensible, observable dev and deploy.项目地址: https://gitcode.com/GitHub_Trending/as/aspire创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表