创建自定义 .NET.NET Aspire 托管集成

.NET .NET Aspire 通过提供可重用的构建模块来提高开发体验,这些模块可用于快速管理应用程序依赖并使其可用于您自己的代码。 基于 Aspire的应用程序的关键构建基块之一是 资源。 请考虑以下代码:

var builder = DistributedApplication.CreateBuilder(args);

var redis = builder.AddRedis("cache");

var db = builder.AddPostgres("pgserver")
                .AddDatabase("inventorydb");

builder.AddProject<Projects.InventoryService>("inventoryservice")
       .WithReference(redis)
       .WithReference(db);

在前面的代码中,有四个资源表示:

  1. cache:Redis 容器。
  2. pgserver:Postgres 容器。
  3. inventorydb:托管在 pgserver上的数据库。
  4. inventoryservice:ASP.NET Core 应用程序。

平均开发人员编写的大多数 .NET.NET Aspire相关代码都集中在将资源添加到 应用模型 并在它们之间创建引用。

.NET .NET Aspire 自定义资源的关键元素

在 .NET.NET Aspire 中生成自定义资源需要以下各项:

  1. 实现 IResource 的自定义资源类型
  2. 一个为 IDistributedApplicationBuilder 命名为 Add{CustomResource} 的扩展方法,其中 {CustomResource} 是自定义资源的名称。

当自定义资源需要可选配置时,开发人员可能希望实现 With* 后缀扩展方法,以便于通过 生成器模式轻松发现这些配置选项。

一个实际示例:MailDev

为了帮助了解如何开发自定义资源,本文演示了如何为 MailDev生成自定义资源的示例。 MailDev 是一种开源工具,它提供了一个本地邮件 server,旨在允许开发人员在其应用中测试电子邮件发送行为。 有关详细信息,请参阅 MailDevGitHub 存储库

在此示例中,将创建一个新的 .NET Aspire 项目作为所创建的 MailDev 资源的测试环境。 虽然可以在现有 .NET Aspire 项目中创建自定义资源,但最好考虑自定义资源是否可以跨多个基于 .NET Aspire的解决方案使用,并且应作为可重用集成进行开发。

设置起始项目

创建一个新的 .NET.NET Aspire 项目,用于测试我们正在开发的新资源。

dotnet new aspire -o MailDevResource
cd MailDevResource
dir

创建项目后,应会看到包含以下项的列表:

  • MailDevResource.AppHost:用于测试自定义资源的 应用主机
  • MailDevResource.ServiceDefaults服务默认 项目用于与服务相关的项目。
  • MailDevResource.sln:引用这两个项目的解决方案文件。

通过执行以下命令验证项目是否可以成功生成并运行:

dotnet run --project MailDevResource.AppHost/MailDevResource.AppHost.csproj

控制台输出应如下所示:

Building...
info: Aspire.Hosting.DistributedApplication[0]
      Aspire version: 9.0.0
info: Aspire.Hosting.DistributedApplication[0]
      Distributed application starting.
info: Aspire.Hosting.DistributedApplication[0]
      Application host directory is:
      ..\docs-aspire\docs\extensibility\snippets\MailDevResource\MailDevResource.AppHost
info: Aspire.Hosting.DistributedApplication[0]
      Now listening on: https://localhost:17251
info: Aspire.Hosting.DistributedApplication[0]
      Login to the dashboard at https://localhost:17251/login?t=928db244c720c5022a7a9bf5cf3a3526
info: Aspire.Hosting.DistributedApplication[0]
      Distributed application started. Press Ctrl+C to shut down.

在浏览器中选择 仪表板链接 以查看 .NET.NET Aspire 仪表板:

测试项目的空 .NET.NET Aspire 仪表板的屏幕截图。

Ctrl+C 关闭应用(可以关闭浏览器选项卡)。

创建资源扩展库

.NET Aspire 资源只是引用 .NET Aspire 托管库(Aspire.Hosting)的类库中包含的类和方法。 通过将资源放置在单独的项目中,可以更轻松地在基于 .NET.NET Aspire的应用之间共享资源,并在 NuGet 上打包和共享它。

  1. 创建名为 MailDev的类库项目。承载

    dotnet new classlib -o MailDev.Hosting
    
  2. Aspire.Hosting 作为包引用添加到类库。

    dotnet add ./MailDev.Hosting/MailDev.Hosting.csproj package Aspire.Hosting --version 9.0.0
    

    重要

    此处指定的版本应与安装的 .NET.NET Aspire 工作负荷的版本匹配。

  3. 将类库引用添加到 MailDevResource.AppHost 项目。

    dotnet add ./MailDevResource.AppHost/MailDevResource.AppHost.csproj reference ./MailDev.Hosting/MailDev.Hosting.csproj
    
  4. 将类库项目添加到解决方案文件。

    dotnet sln ./MailDevResource.sln add ./MailDev.Hosting/MailDev.Hosting.csproj
    

执行以下步骤后,可以启动项目:

dotnet run --project ./MailDevResource.AppHost/MailDevResource.AppHost.csproj

这会导致向控制台显示警告:

.\.nuget\packages\aspire.hosting.apphost\9.0.0\build\Aspire.Hosting.AppHost.targets(174,5): warning ASPIRE004: '..\MailDev.Hosting\MailDev.Hosting.csproj' is referenced by an A
spire Host project, but it is not an executable. Did you mean to set IsAspireProjectResource="false"? [D:\source\repos\docs-aspire\docs\extensibility\snippets\MailDevResource\MailDevResource.AppHost\MailDevRe
source.AppHost.csproj]

这是因为 .NET.NET Aspire 将应用主机中的项目引用视为服务项目。 若要告知 .NET.NET Aspire 应将项目引用视为非服务项目,请修改对 MailDev.Hosting 项目的 MailDevResource.AppHostMailDevResource.AppHost.csproj 文件引用,如下所示:

<ItemGroup>
  <!-- The IsAspireProjectResource attribute tells .NET Aspire to treat this 
       reference as a standard project reference and not attempt to generate
       a metadata file -->
  <ProjectReference Include="..\MailDev.Hosting\MailDev.Hosting.csproj"
                    IsAspireProjectResource="false" />
</ItemGroup>

现在,启动应用主机时,控制台上没有显示任何警告。

定义资源类型

MailDev。托管 类库包含用于将资源添加到应用主机的资源类型和扩展方法。 应首先考虑在使用自定义资源时要为开发人员提供的体验。 对于此自定义资源,你希望开发人员能够编写如下所示的代码:

var builder = DistributedApplication.CreateBuilder(args);

var maildev = builder.AddMailDev("maildev");

builder.AddProject<Projects.NewsletterService>("newsletterservice")
       .WithReference(maildev);

为此,需要一个名为 MailDevResource 的自定义资源来实现 IResourceWithConnectionString,以便使用者可以将它与 WithReference 扩展一起使用,以将 MailDevserver 的连接详细信息作为连接字符串注入。

MailDev 可以作为容器资源使用,因此你还需要从 ContainerResource 派生,这样我们才能在 .NET.NET Aspire中使用各种已有的以容器为中心的扩展。

MailDev.Hosting 项目中 Class1.cs 文件的内容替换为以下代码,并将该文件重命名为 MailDevResource.cs

// For ease of discovery, resource types should be placed in
// the Aspire.Hosting.ApplicationModel namespace. If there is
// likelihood of a conflict on the resource name consider using
// an alternative namespace.
namespace Aspire.Hosting.ApplicationModel;

public sealed class MailDevResource(string name) : ContainerResource(name), IResourceWithConnectionString
{
    // Constants used to refer to well known-endpoint names, this is specific
    // for each resource type. MailDev exposes an SMTP endpoint and a HTTP
    // endpoint.
    internal const string SmtpEndpointName = "smtp";
    internal const string HttpEndpointName = "http";

    // An EndpointReference is a core .NET Aspire type used for keeping
    // track of endpoint details in expressions. Simple literal values cannot
    // be used because endpoints are not known until containers are launched.
    private EndpointReference? _smtpReference;

    public EndpointReference SmtpEndpoint =>
        _smtpReference ??= new(this, SmtpEndpointName);

    // Required property on IResourceWithConnectionString. Represents a connection
    // string that applications can use to access the MailDev server. In this case
    // the connection string is composed of the SmtpEndpoint endpoint reference.
    public ReferenceExpression ConnectionStringExpression =>
        ReferenceExpression.Create(
            $"smtp://{SmtpEndpoint.Property(EndpointProperty.Host)}:{SmtpEndpoint.Property(EndpointProperty.Port)}"
        );
}

在前面的自定义资源中,EndpointReferenceReferenceExpression 是多种类型的示例,这些类型可实现接口集合,例如 IManifestExpressionProviderIValueProviderIValueWithReferences。 有关这些类型及其 .NET.NET Aspire角色的详细信息,请参阅 技术详细信息

定义资源扩展

为了便于开发人员使用自定义资源,需要将名为 AddMailDev 的扩展方法添加到 MailDev.Hosting 项目中。 AddMailDev 扩展方法负责配置资源,以便它可以作为容器成功启动。

将以下代码添加到 MailDev.Hosting 项目中一个新文件,文件名为 MailDevResourceBuilderExtensions.cs

using Aspire.Hosting.ApplicationModel;

// Put extensions in the Aspire.Hosting namespace to ease discovery as referencing
// the .NET Aspire hosting package automatically adds this namespace.
namespace Aspire.Hosting;

public static class MailDevResourceBuilderExtensions
{
    /// <summary>
    /// Adds the <see cref="MailDevResource"/> to the given
    /// <paramref name="builder"/> instance. Uses the "2.1.0" tag.
    /// </summary>
    /// <param name="builder">The <see cref="IDistributedApplicationBuilder"/>.</param>
    /// <param name="name">The name of the resource.</param>
    /// <param name="httpPort">The HTTP port.</param>
    /// <param name="smtpPort">The SMTP port.</param>
    /// <returns>
    /// An <see cref="IResourceBuilder{MailDevResource}"/> instance that
    /// represents the added MailDev resource.
    /// </returns>
    public static IResourceBuilder<MailDevResource> AddMailDev(
        this IDistributedApplicationBuilder builder,
        string name,
        int? httpPort = null,
        int? smtpPort = null)
    {
        // The AddResource method is a core API within .NET Aspire and is
        // used by resource developers to wrap a custom resource in an
        // IResourceBuilder<T> instance. Extension methods to customize
        // the resource (if any exist) target the builder interface.
        var resource = new MailDevResource(name);

        return builder.AddResource(resource)
                      .WithImage(MailDevContainerImageTags.Image)
                      .WithImageRegistry(MailDevContainerImageTags.Registry)
                      .WithImageTag(MailDevContainerImageTags.Tag)
                      .WithHttpEndpoint(
                          targetPort: 1080,
                          port: httpPort,
                          name: MailDevResource.HttpEndpointName)
                      .WithEndpoint(
                          targetPort: 1025,
                          port: smtpPort,
                          name: MailDevResource.SmtpEndpointName);
    }
}

// This class just contains constant strings that can be updated periodically
// when new versions of the underlying container are released.
internal static class MailDevContainerImageTags
{
    internal const string Registry = "docker.io";

    internal const string Image = "maildev/maildev";

    internal const string Tag = "2.1.0";
}

验证应用主机内的自定义集成

现在,自定义资源的基本结构已完成,现在可以在实际的 AppHost 项目中对其进行测试了。 在 MailDevResource.AppHost 项目中打开 Program.cs 文件,并使用以下代码对其进行更新:

var builder = DistributedApplication.CreateBuilder(args);

var maildev = builder.AddMailDev("maildev");

builder.Build().Run();

更新 Program.cs 文件后,启动应用主机项目并打开仪表板:

dotnet run --project ./MailDevResource.AppHost/MailDevResource.AppHost.csproj

片刻后,仪表板会显示 maildev 资源正在运行,并且将有一个超链接导航到 MailDev Web 应用,其中显示了应用发送的每个电子邮件的内容。

.NET .NET Aspire 仪表板应如下所示:

MailDev.NET Aspire 仪表板中显示的资源。

MailDev Web 应用程序应如下所示:

MailDev 基于 Web 的用户界面,作为由 .NET Aspire管理的容器运行。

将 .NET 服务项目添加到应用主机以进行测试

.NET Aspire 成功完成 MailDev 集成后,即可在 .NET 项目中使用 MailDev 的连接信息。 在 .NET.NET Aspire,通常会有一个 托管包 和一个或多个 组件包。 例如,请考虑:

  • 托管包:用于表示应用模型中的资源。
    • Aspire.Hosting.Redis
  • 组件包:用于配置和使用 client 库。
    • Aspire.StackExchange.Redis
    • Aspire.StackExchange.Redis.DistributedCaching
    • Aspire.StackExchange.Redis.OutputCaching

对于 MailDev 资源,.NET 平台已经有一个简单的邮件传输协议(SMTP)client,其形式为 SmtpClient。 在此示例中,为了简单起见,使用此现有 API,尽管其他资源类型可能受益于自定义集成库来帮助开发人员。

为了测试端到端方案,需要一个 .NET 项目,我们可以将连接信息注入到 MailDev 资源中。 添加 Web API 项目:

  1. 创建名为 MailDevResource.NewsletterService的新 .NET 项目。

    dotnet new webapi --use-minimal-apis -o MailDevResource.NewsletterService
    
  2. 添加对 MailDev.Hosting 项目的引用。

    dotnet add ./MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj reference ./MailDev.Hosting/MailDev.Hosting.csproj
    
  3. 添加对 MailDevResource.AppHost 项目的引用。

    dotnet add ./MailDevResource.AppHost/MailDevResource.AppHost.csproj reference ./MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj
    
  4. 将新项目添加到解决方案文件。

    dotnet sln ./MailDevResource.sln add ./MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj
    

添加项目并更新引用后,打开 MailDevResource.AppHost.csproj 项目的 Program.cs,并更新源文件,使其如下所示:

var builder = DistributedApplication.CreateBuilder(args);

var maildev = builder.AddMailDev("maildev");

builder.AddProject<Projects.MailDevResource_NewsletterService>("newsletterservice")
       .WithReference(maildev);

builder.Build().Run();

更新 Program.cs 文件后,再次启动应用主机。 然后验证新闻稿服务是否已启动,并将环境变量 ConnectionStrings__maildev 添加到该过程。 在 资源 页中,找到 newsletterservice 行,然后选择 详细信息 列上的 视图 链接:

为 .NET.NET Aspire 仪表板中的新闻稿服务设置的环境变量 。

前面的屏幕截图显示了 newsletterservice 项目的环境变量。 ConnectionStrings__maildev 环境变量是由 maildev 资源注入到项目中的连接字符串。

使用连接字符串发送消息

若要使用注入到新闻稿服务项目中的 SMTP 连接详细信息,可将 SmtpClient 实例作为单一实例注入到依赖项注入容器中。 将以下代码添加到 Program.cs 文件中以设置 MailDevResource.NewsletterService 项目的单例服务。 在 Program 类中,紧跟 // Add services to the container 注释,添加以下代码:

builder.Services.AddSingleton<SmtpClient>(sp =>
{
    var smtpUri = new Uri(builder.Configuration.GetConnectionString("maildev")!);

    var smtpClient = new SmtpClient(smtpUri.Host, smtpUri.Port);

    return smtpClient;
});

提示

但是,此代码片段依赖于官方 SmtpClient;此类型在某些平台上已过时,不建议将其用于其他平台。 有关使用 MailKit的更新式方法,请参阅 创建自定义 .NET Aspireclient 集成

若要测试 client,请将两个简单的 subscribeunsubscribe POST 方法添加到新闻稿服务。 在 Program.cs 文件中添加以下代码,替换 MailDevResource.NewsletterService 项目中的 “weatherforecast” MapGet 调用,以设置 ASP.NET Core 路由:

app.MapPost("/subscribe", async (SmtpClient smtpClient, string email) =>
{
    using var message = new MailMessage("newsletter@yourcompany.com", email)
    {
        Subject = "Welcome to our newsletter!",
        Body = "Thank you for subscribing to our newsletter!"
    };

    await smtpClient.SendMailAsync(message);
});

app.MapPost("/unsubscribe", async (SmtpClient smtpClient, string email) =>
{
    using var message = new MailMessage("newsletter@yourcompany.com", email)
    {
        Subject = "You are unsubscribed from our newsletter!",
        Body = "Sorry to see you go. We hope you will come back soon!"
    };

    await smtpClient.SendMailAsync(message);
});

提示

如果代码编辑器不自动添加这些命名空间,请记得在 Program.cs 中引用 System.Net.MailMicrosoft.AspNetCore.Mvc 命名空间。

更新 Program.cs 文件后,启动应用程序主机,并使用浏览器或 curl 访问以下 URL(如果使用 Visual Studio,也可以使用 .http 文件):

POST /subscribe?email=test@test.com HTTP/1.1
Host: localhost:7251
Content-Type: application/json

若要使用此 API,可以使用 curl 发送请求。 以下 curl 命令向 subscribe 终结点发送 HTTP POST 请求,并且需要一个 email 查询字符串值来订阅新闻稿。 Content-Type 标头设置为 application/json,以指示请求正文采用 JSON 格式。:

curl -H "Content-Type: application/json" --request POST https://localhost:7251/subscribe?email=test@test.com

下一个 API 是 unsubscribe 端点。 此终结点用于取消订阅新闻稿。

POST /unsubscribe?email=test@test.com HTTP/1.1
Host: localhost:7251
Content-Type: application/json

若要取消订阅新闻稿,可以使用以下 curl 命令,将 email 参数作为查询字符串传递到 unsubscribe 终结点:

curl -H "Content-Type: application/json" --request POST https://localhost:7251/unsubscribe?email=test@test.com

提示

请确保将 https://localhost:7251 替换为正确的 localhost 端口(正在运行的应用主机的 URL)。

如果这些 API 调用返回成功的响应(HTTP 200,正常),则您应该能够在资源 maildev 上选择仪表板,并且 MailDev UI 将显示已发送到 SMTP 终结点的电子邮件。

UI 中显示的电子邮件

技术详细信息

在以下各节中,将讨论各种技术详细信息,这些细节对于开发 .NET.NET Aspire的自定义资源非常重要。

安全网络

在此示例中,MailDev 资源是通过 HTTP 和 SMTP 向主机公开的容器资源。 MailDev 资源是一种开发工具,不适用于生产用途。 若要改用 HTTPS,请参阅 MailDev:配置 HTTPS

开发公开网络终结点的自定义资源时,请务必考虑资源的安全影响。 例如,如果资源是数据库,请务必确保数据库安全且连接字符串不会公开到公共 Internet。

ReferenceExpressionEndpointReference 类型

在前面的代码中,MailDevResource 有两个属性:

这些类型在 .NET Aspire 中用于表示配置数据,只有在运行或通过如 Azure Developer CLI(azd这样的工具将 .NET Aspire 项目发布到云端时,配置数据才会最终确定。

这些类型帮助解决的基本问题是推迟解决具体配置信息,直到 所有 信息都可用。

例如,MailDevResource 根据 IResourceWithConnectionString 接口的要求公开名为 ConnectionStringExpression 的属性。 该属性的类型是 ReferenceExpression,并通过将插值字符串传入 Create 方法来创建。

public ReferenceExpression ConnectionStringExpression =>
    ReferenceExpression.Create(
        $"smtp://{SmtpEndpoint.Property(EndpointProperty.Host)}:{SmtpEndpoint.Property(EndpointProperty.Port)}"
    );

Create 方法的签名如下所示:

public static ReferenceExpression Create(
    in ExpressionInterpolatedStringHandler handler)

这不是一个常规的 String 参数。 该方法利用 内插字符串处理程序模式,捕获内插字符串模板及其中引用的值,以允许进行自定义处理。 对于 .NET.NET Aspire,这些详细信息是在 ReferenceExpression 中捕获的,可以在内插字符串中引用的每个值都可用时进行评估。

下面是执行流的工作原理:

  1. 将实现 IResourceWithConnectionString 的资源添加到模型(例如,AddMailDev(...))。
  2. IResourceBuilder<MailDevResource> 传递给具有特殊重载的 WithReference,用于处理 IResourceWithConnectionString 实现器。
  3. WithReference 将资源包装在 ConnectionStringReference 实例中,并在生成并开始运行 .NET.NET Aspire 项目后在 EnvironmentCallbackAnnotation 中捕获该对象。
  4. 引用连接字符串的进程开始时,.NET.NET Aspire 开始计算表达式。 它首先获取 ConnectionStringReference,然后调用 IValueProvider.GetValueAsync
  5. GetValueAsync 方法获取 ConnectionStringExpression 属性的值以获取 ReferenceExpression 实例。
  6. 然后,IValueProvider.GetValueAsync 方法调用 GetValueAsync 来处理以前捕获的内插字符串。
  7. 由于插值字符串包含对其他引用类型(例如 EndpointReference)的引用,它们也会被求值,并用实际值替换(此时现在可用)。

清单发布

IManifestExpressionProvider 接口旨在解决在部署时共享资源之间的连接信息的问题。 此特定问题的解决方法在 .NET.NET Aspire 内部循环网络概述中介绍。 与本地开发类似,许多值是配置应用所必需的,但在通过工具(如 azd(Azure Developer CLI)部署应用之前,无法确定这些值。

若要解决此问题,.NET.NET Aspire 生成一个由 azd 和其他部署工具解释的清单文件。 部署工具使用表达式语法来评估资源之间的连接信息,而不是直接指定具体值。 通常,清单文件对开发人员不可见,但可以生成一个用于手动检查的文件。 以下命令可用于应用主机上生成清单。

dotnet run --project MailDevResource.AppHost/MailDevResource.AppHost.csproj -- --publisher manifest --output-path aspire-manifest.json

此命令生成如下所示的清单文件:

{
  "resources": {
    "maildev": {
      "type": "container.v0",
      "connectionString": "smtp://{maildev.bindings.smtp.host}:{maildev.bindings.smtp.port}",
      "image": "docker.io/maildev/maildev:2.1.0",
      "bindings": {
        "http": {
          "scheme": "http",
          "protocol": "tcp",
          "transport": "http",
          "targetPort": 1080
        },
        "smtp": {
          "scheme": "tcp",
          "protocol": "tcp",
          "transport": "tcp",
          "targetPort": 1025
        }
      }
    },
    "newsletterservice": {
      "type": "project.v0",
      "path": "../MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj",
      "env": {
        "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES": "true",
        "OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true",
        "OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY": "in_memory",
        "ASPNETCORE_FORWARDEDHEADERS_ENABLED": "true",
        "ConnectionStrings__maildev": "{maildev.connectionString}"
      },
      "bindings": {
        "http": {
          "scheme": "http",
          "protocol": "tcp",
          "transport": "http"
        },
        "https": {
          "scheme": "https",
          "protocol": "tcp",
          "transport": "http"
        }
      }
    }
  }
}

由于 MailDevResource 实现 IResourceWithConnectionString.NET.NET Aspire 中的清单发布逻辑知道,即使 MailDevResource 是容器资源,它也需要 connectionString 字段。 connectionString 字段引用清单中 maildev 资源的其他部分,以生成最终字符串:

{
    // ... other content omitted.
    "connectionString": "smtp://{maildev.bindings.smtp.host}:{maildev.bindings.smtp.port}"
}

.NET .NET Aspire 知道如何形成此字符串,因为它查看 ConnectionStringExpression 并通过 IManifestExpressionProvider 接口构建最终字符串(与使用 IValueProvider 接口的方式大致相同)。

MailDevResource 自动包含在清单中,因为它派生自 ContainerResource。 资源作者可以选择通过在资源生成器上使用 ExcludeFromManifest 扩展方法禁止将内容输出到清单。

public static IResourceBuilder<MailDevResource> AddMailDev(
    this IDistributedApplicationBuilder builder, 
    string name,
    int? httpPort = null,
    int? smtpPort = null)
{
    var resource = new MailDevResource(name);

    return builder.AddResource(resource)
                  .WithImage(MailDevContainerImageTags.Image)
                  .WithImageRegistry(MailDevContainerImageTags.Registry)
                  .WithImageTag(MailDevContainerImageTags.Tag)
                  .WithHttpEndpoint(
                      targetPort: 1080,
                      port: httpPort,
                      name: MailDevResource.HttpEndpointName)
                  .WithEndpoint(
                      targetPort: 1025,
                      port: smtpPort,
                      name: MailDevResource.SmtpEndpointName)
                  .ExcludeFromManifest(); // This line was added
}

应仔细考虑资源是否应出现在清单中,还是应省略。 如果将资源添加到清单中,则应以安全可靠的方式进行配置。

总结

在自定义资源教程中,你学习了如何创建自定义 .NET Aspire 资源,该资源使用现有的容器化应用程序(MailDev)。 然后,通过轻松测试应用内可能使用的电子邮件功能来改进本地开发体验。 这些学习可以应用于构建可在基于 .NET.NET Aspire的应用程序中使用的其他自定义资源。 此特定示例不包含任何自定义集成,但可以生成自定义集成,以便开发人员更轻松地使用资源。 在此情境中,你能够依赖 .NET 平台中的现有 SmtpClient 类发送电子邮件。

后续步骤