Partilhar via


Crie integrações personalizadas de hospedagem .NET.NET Aspire

.NET .NET Aspire melhora a experiência de desenvolvimento fornecendo blocos de construção reutilizáveis que podem ser usados para organizar rapidamente as dependências do aplicativo e expô-las ao seu próprio código. Um dos principais blocos de construção de um aplicativo baseado em Aspireé o recurso . Considere o código abaixo:

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);

No código anterior estão representados quatro recursos:

  1. cache: Um contentor Redis.
  2. pgserver: Um contentor Postgres.
  3. inventorydb: Um banco de dados hospedado em pgserver.
  4. inventoryservice: Uma aplicação ASP.NET Core.

A maior parte do código relacionado a .NET.NET Aspireque o programador médio escreve foca-se em adicionar recursos ao modelo de aplicação e criar referências entre eles.

Elementos-chave de um recurso personalizado .NET.NET Aspire

A criação de um recurso personalizado no .NET.NET Aspire requer o seguinte:

  1. Um tipo de recurso personalizado que implementa IResource
  2. Um método de extensão para IDistributedApplicationBuilder chamado Add{CustomResource} onde {CustomResource} é o nome do recurso personalizado.

Quando um recurso personalizado requer uma configuração opcional, os desenvolvedores poderão querer implementar métodos de extensão com sufixo With* para tornar essas opções de configuração descobertas usando o padrão do builder .

Um exemplo prático: MailDev

Para ajudar a entender como desenvolver recursos personalizados, este artigo mostra um exemplo de como criar um recurso personalizado para MailDev. MailDev é uma ferramenta de código aberto que fornece um server de email local projetado para permitir que os desenvolvedores testem comportamentos de envio de e-mail em seu aplicativo. Para obter mais informações, consulte o repositório MailDevGitHub.

Neste exemplo, você cria um novo projeto de .NET Aspire como um ambiente de teste para o recurso MailDev que você cria. Embora você possa criar recursos personalizados em projetos de .NET Aspire existentes, é uma boa ideia considerar se o recurso personalizado pode ser usado em várias soluções baseadas em .NET Aspiree deve ser desenvolvido como uma integração reutilizável.

Configurar o projeto inicial

Crie um novo projeto de .NET.NET Aspire que seja usado para testar o novo recurso que estamos desenvolvendo.

dotnet new aspire -o MailDevResource
cd MailDevResource
dir

Uma vez que o projeto é criado, você deve ver uma lista contendo o seguinte:

  • MailDevResource.AppHost: O host da aplicação utilizado para testar o recurso personalizado.
  • MailDevResource.ServiceDefaults: O serviço define por padrão para uso em projetos relacionados com o serviço.
  • MailDevResource.sln: O arquivo de solução que faz referência a ambos os projetos.

Verifique se o projeto pode ser compilado e executado com êxito executando o seguinte comando:

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

A saída do console deve ser semelhante à seguinte:

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.

Selecione o link do dashboard no navegador para visualizar o dashboard .NET.NET Aspire.

Uma captura de tela do painel de .NET.NET Aspire vazio para o projeto de teste.

Pressione Ctrl+C para desligar o aplicativo (você pode fechar a guia do navegador).

Criar biblioteca para extensão de recursos

.NET Aspire recursos são apenas classes e métodos contidos numa biblioteca de classes que faz referência à biblioteca de hospedagem .NET Aspire (Aspire.Hosting). Ao colocar o recurso em um projeto separado, você pode compartilhá-lo mais facilmente entre aplicativos baseados em .NET.NET Aspiree, potencialmente, empacotá-lo e compartilhá-lo no NuGet.

  1. Crie o projeto de biblioteca de classes chamado MailDev. Hospedagem.

    dotnet new classlib -o MailDev.Hosting
    
  2. Adicione Aspire.Hosting à biblioteca de classes como uma referência de pacote.

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

    Importante

    A versão especificada aqui deve corresponder à versão da carga de trabalho .NET.NET Aspire instalada.

  3. Adicione a referência à biblioteca de classes ao projeto MailDevResource.AppHost.

    dotnet add ./MailDevResource.AppHost/MailDevResource.AppHost.csproj reference ./MailDev.Hosting/MailDev.Hosting.csproj
    
  4. Adicione o projeto de biblioteca de classes ao arquivo de solução.

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

Depois que as seguintes etapas forem executadas, você poderá iniciar o projeto:

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

Isso resulta em um aviso sendo exibido para o console:

.\.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]

Isso ocorre porque .NET.NET Aspire trata as referências de projeto no host do aplicativo como se fossem projetos de serviço. Para dizer aos .NET.NET Aspire que a referência do projeto deve ser tratada como um projeto não serviço, modifique a referência de arquivos MailDevResource.AppHostMailDevResource.AppHost.csproj para o projeto MailDev.Hosting para ser o seguinte:

<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>

Agora, quando você inicia o host do aplicativo, não há nenhum aviso exibido no console.

Definir os tipos de recursos

O MailDev.Hosting biblioteca de classes de contém o tipo de recurso e os métodos de extensão para adicionar o recurso ao anfitrião da aplicação. Você deve primeiro pensar na experiência que deseja oferecer aos desenvolvedores ao usar seu recurso personalizado. No caso desse recurso personalizado, você gostaria que os desenvolvedores pudessem escrever código como o seguinte:

var builder = DistributedApplication.CreateBuilder(args);

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

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

Para conseguir isso, precisas de um recurso personalizado chamado MailDevResource que implementa IResourceWithConnectionString para que os utilizadores possam utilizá-lo com a extensão WithReference para injectar os detalhes de conexão para o MailDevserver como uma cadeia de conexão.

MailDev está disponível como um recurso de contêiner, então você também vai querer derivar de ContainerResource para que possamos usar várias extensões pré-existentes focadas em contêiner no .NET.NET Aspire.

Substitua o conteúdo do arquivo Class1.cs no projeto MailDev.Hosting e renomeie o arquivo para MailDevResource.cs com o seguinte código:

// 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)}"
        );
}

No recurso personalizado anterior, os EndpointReference e ReferenceExpression são exemplos de vários tipos que implementam uma coleção de interfaces, como IManifestExpressionProvider, IValueProvidere IValueWithReferences. Para mais informações sobre esses tipos e o seu papel em .NET.NET Aspire, veja os detalhes técnicos em .

Definir as extensões de recursos

Para facilitar o uso do recurso personalizado pelos desenvolvedores, um método de extensão chamado AddMailDev precisa ser adicionado ao projeto MailDev.Hosting. O método de extensão AddMailDev é responsável por configurar o recurso para que ele possa iniciar com êxito como um contêiner.

Adicione o seguinte código a um novo arquivo chamado MailDevResourceBuilderExtensions.cs no projeto MailDev.Hosting:

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";
}

Validar a integração personalizada dentro do host do aplicativo

Agora que a estrutura básica para o recurso personalizado está completa, é hora de testá-lo em um projeto AppHost real. Abra o arquivo Program.cs no projeto MailDevResource.AppHost e atualize-o com o seguinte código:

var builder = DistributedApplication.CreateBuilder(args);

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

builder.Build().Run();

Depois de atualizar o arquivo Program.cs, inicie o projeto de host do aplicativo e abra o painel:

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

Após alguns momentos, o painel mostra que o recurso maildev está em execução e um hiperlink estará disponível para navegar até o aplicativo Web MailDev, que mostra o conteúdo de cada email enviado pelo aplicativo.

O painel .NET.NET Aspire deve ser semelhante ao seguinte:

recursoMailDev visível no painel de controlo .NET Aspire.

O aplicativo Web MailDev deve ser semelhante ao seguinte:

MailDev interface de usuário baseada na Web em execução como um contêiner gerenciado pelo .NET Aspire.

Adicionar um projeto de serviço .NET ao host do aplicativo para teste

Uma vez que .NET Aspire possa iniciar com sucesso a integração de MailDev, é hora de consumir as informações de conexão para MailDev dentro de um projeto .NET. Além .NET.NET Aspire é comum que haja um pacote de hospedagem e um ou mais pacotes de componentes . Por exemplo, considere:

  • Pacote de hospedagem: Usado para representar recursos dentro do modelo de aplicativo.
    • Aspire.Hosting.Redis
  • Pacotes de componentes: Usados para configurar e consumir bibliotecas client.
    • Aspire.StackExchange.Redis
    • Aspire.StackExchange.Redis.DistributedCaching
    • Aspire.StackExchange.Redis.OutputCaching

No caso do recurso MailDev, a plataforma .NET já possui um protocolo de transferência de correio simples (SMTP) client na forma de SmtpClient. Neste exemplo, você usa essa API existente por uma questão de simplicidade, embora outros tipos de recursos possam se beneficiar de bibliotecas de integração personalizadas para ajudar os desenvolvedores.

Para testar o cenário de ponta a ponta, você precisa de um projeto .NET no qual possamos injetar as informações de conexão para o recurso MailDev. Adicione um projeto de API Web:

  1. Crie um novo projeto .NET chamado MailDevResource.NewsletterService.

    dotnet new webapi --use-minimal-apis -o MailDevResource.NewsletterService
    
  2. Adicione uma referência ao projeto MailDev.Hosting.

    dotnet add ./MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj reference ./MailDev.Hosting/MailDev.Hosting.csproj
    
  3. Adicione uma referência ao projeto MailDevResource.AppHost.

    dotnet add ./MailDevResource.AppHost/MailDevResource.AppHost.csproj reference ./MailDevResource.NewsletterService/MailDevResource.NewsletterService.csproj
    
  4. Adicione o novo projeto ao arquivo de solução.

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

Depois que o projeto tiver sido adicionado e as referências tiverem sido atualizadas, abra o Program.cs do projeto MailDevResource.AppHost.csproj e atualize o arquivo de origem para ter a seguinte aparência:

var builder = DistributedApplication.CreateBuilder(args);

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

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

builder.Build().Run();

Depois de atualizar o arquivo Program.cs, inicie o host do aplicativo novamente. Em seguida, verifique se o Serviço de Newsletter foi iniciado e se a variável de ambiente ConnectionStrings__maildev foi adicionada ao processo. Na página Recursos, localize a linha newsletterservice e selecione o link Ver na coluna Detalhes.

Variáveis de ambiente para o serviço de boletins informativos no .NET.NET Aspire Dashboard.

A captura de tela anterior mostra as variáveis de ambiente para o projeto newsletterservice. A variável de ambiente ConnectionStrings__maildev é a cadeia de conexão que foi injetada no projeto pelo recurso maildev.

Usar cadeia de conexão para enviar mensagens

Para usar os detalhes da conexão SMTP que foram injetados no projeto de serviço de boletim informativo, injete uma instância de SmtpClient no contêiner de injeção de dependência como um singleton. Adicione o seguinte código ao arquivo Program.cs no projeto MailDevResource.NewsletterService para configurar o serviço singleton. Na classe Program, imediatamente após o comentário // Add services to the container, adicione o seguinte código:

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

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

    return smtpClient;
});

Dica

Este trecho de código depende do SmtpClientoficial, no entanto; Este tipo é obsoleto em algumas plataformas e não recomendado em outras. Para obter uma abordagem mais moderna usando MailKit, consulte Criar integrações .NET Aspireclient personalizadas.

Para testar o client, adicione dois métodos simples subscribe e unsubscribe POST ao serviço de newsletter. Adicione o seguinte código, substituindo a chamada de "weatherforecast" MapGet no arquivo Program.cs do projeto MailDevResource.NewsletterService, para configurar as rotas 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);
});

Dica

Lembre-se de fazer referência aos namespaces System.Net.Mail e Microsoft.AspNetCore.Mvc no Program.cs se o editor de código não os adicionar automaticamente.

Depois que o arquivo Program.cs for atualizado, inicie o host do aplicativo e use seu navegador ou curl para acessar as seguintes URLs (alternativamente, se você estiver usando Visual Studio você pode usar .http arquivos):

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

Para usar essa API, você pode usar curl para enviar a solicitação. O comando curl a seguir envia uma solicitação HTTP de POST para o ponto de extremidade subscribe, e espera que um valor da cadeia de caracteres de consulta email seja usado para subscrever a newsletter. O cabeçalho Content-Type é definido como application/json para indicar que o corpo da solicitação está em JSON formato.:

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

A próxima API é o endpoint unsubscribe. Este endpoint é utilizado para cancelar a subscrição da newsletter.

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

Para cancelar a assinatura do boletim informativo, você pode usar o seguinte comando curl, passando um parâmetro email para o ponto de extremidade unsubscribe como uma cadeia de caracteres de consulta:

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

Dica

Certifique-se de substituir o https://localhost:7251 pela porta localhost correta (a URL do host do aplicativo que você está executando).

Se essas chamadas de API retornarem uma resposta bem-sucedida (HTTP 200, Ok), você poderá selecionar no recurso maildev o painel e o MailDev UI mostrará os e-mails que foram enviados para o ponto de extremidade SMTP.

E-mails visíveis na interface do utilizador do MailDev

Detalhes técnicos

Nas seções a seguir, vários detalhes técnicos são discutidos que são importantes entender ao desenvolver recursos personalizados para .NET.NET Aspire.

Rede segura

Neste exemplo, o recurso MailDev é um recurso de contêiner que é exposto à máquina host por HTTP e SMTP. O recurso MailDev é uma ferramenta de desenvolvimento e não se destina ao uso em produção. Para usar HTTPS, consulte MailDev: Configurar HTTPS.

Ao desenvolver recursos personalizados que expõem pontos de extremidade de rede, é importante considerar as implicações de segurança do recurso. Por exemplo, se o recurso for um banco de dados, é importante garantir que o banco de dados seja seguro e que a cadeia de conexão não seja exposta à Internet pública.

O tipo ReferenceExpression e o tipo EndpointReference

No código anterior, o MailDevResource tinha duas propriedades:

Esses tipos estão entre os vários que são usados ao longo .NET Aspire para representar dados de configuração, que não são finalizados até que o projeto .NET Aspire seja executado ou publicado na nuvem por meio de uma ferramenta como Azure Developer CLI (azd).

O problema fundamental que esses tipos ajudam a resolver, é adiar a resolução de informações concretas de configuração até todas as informações estejam disponíveis.

Por exemplo, o MailDevResource expõe uma propriedade chamada ConnectionStringExpression conforme exigido pela interface IResourceWithConnectionString. O tipo da propriedade é ReferenceExpression e é criado passando uma cadeia de caracteres interpolada para o método Create.

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

A assinatura para o método Create é a seguinte:

public static ReferenceExpression Create(
    in ExpressionInterpolatedStringHandler handler)

Este não é um argumento String regular. O método faz uso do padrão de manipulador de cadeia de caracteres interpolado , para capturar o modelo de cadeia de caracteres interpolado e os valores referenciados nele para permitir o processamento personalizado. No caso de .NET.NET Aspire, esses detalhes são capturados em um ReferenceExpression que pode ser avaliado à medida que cada valor referenciado na cadeia interpolada se torna disponível.

Veja como funciona o fluxo de execução:

  1. Um recurso que implementa IResourceWithConnectionString é adicionado ao modelo (por exemplo, AddMailDev(...)).
  2. O IResourceBuilder<MailDevResource> é passado para o WithReference que tem uma sobrecarga especial para o manuseio de implementadores IResourceWithConnectionString.
  3. O WithReference encapsula o recurso em uma instância ConnectionStringReference e o objeto é capturado em um EnvironmentCallbackAnnotation que é avaliado depois que o projeto .NET.NET Aspire é criado e começa a ser executado.
  4. À medida que o processo que faz referência à cadeia de conexão é iniciado, .NET.NET Aspire começa a avaliar a expressão. Primeiro, recebe o ConnectionStringReference e chama IValueProvider.GetValueAsync.
  5. O método GetValueAsync obtém o valor da propriedade ConnectionStringExpression para obter a instância ReferenceExpression.
  6. Em seguida, o método IValueProvider.GetValueAsync chama GetValueAsync para processar a cadeia de caracteres interpolada capturada anteriormente.
  7. Como a cadeia de caracteres interpolada contém referências a outros tipos de referência, como EndpointReference, estas também são avaliadas e os valores reais são substituídos (que agora estão disponíveis).

Publicação de manifesto

A interface IManifestExpressionProvider foi projetada para resolver o problema de compartilhamento de informações de conexão entre recursos na implantação. A solução para esse problema específico é descrita no .NET.NET Aspire visão geral da rede de loop interno. Da mesma forma que o desenvolvimento local, muitos dos valores são necessários para configurar o aplicativo, mas não podem ser determinados até que o aplicativo esteja sendo implantado por meio de uma ferramenta, como azd (Azure Developer CLI).

Para resolver esse problema, .NET.NET Aspire produz um arquivo de manifesto que azd e outras ferramentas de implantação interpretam. Em vez de especificar valores concretos para informações de conexão entre recursos, é usada uma sintaxe de expressão que as ferramentas de implantação avaliam. Geralmente, o arquivo de manifesto não é visível para os desenvolvedores, mas é possível gerar um para inspeção manual. O comando abaixo pode ser usado no host do aplicativo para produzir um manifesto.

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

Este comando produz um arquivo de manifesto como o seguinte:

{
  "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"
        }
      }
    }
  }
}

Como o MailDevResource implementa IResourceWithConnectionString a lógica de publicação de manifesto no .NET.NET Aspire sabe que, embora MailDevResource seja um recurso de contêiner, ele também precisa de um campo connectionString. O campo connectionString faz referência a outras partes do recurso maildev no manifesto para produzir a cadeia de caracteres final:

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

.NET .NET Aspire sabe como formar essa string porque olha para ConnectionStringExpression e constrói a string final através da interface IManifestExpressionProvider (da mesma forma que a interface IValueProvider é usada).

O MailDevResource é automaticamente incluído no manifesto porque é derivado de ContainerResource. Os autores de recursos podem optar por impedir a saída de conteúdo para o manifesto usando o método de extensão ExcludeFromManifest no construtor de recursos.

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
}

Deve ponderar-se cuidadosamente se o recurso deve estar presente no manifesto ou se deve ser suprimido. Se o recurso estiver sendo adicionado ao manifesto, ele deve ser configurado de tal forma que seja seguro e protegido para uso.

Resumo

No tutorial de recurso personalizado, você aprendeu como criar um recurso de .NET Aspire personalizado que usa um aplicativo conteinerizado existente (MailDev). Em seguida, você usou isso para melhorar a experiência de desenvolvimento local, facilitando o teste de recursos de email que podem ser usados em um aplicativo. Esses aprendizados podem ser aplicados à criação de outros recursos personalizados que podem ser usados em aplicativos baseados em .NET.NET Aspire. Este exemplo específico não incluiu integrações personalizadas, mas é possível criar integrações personalizadas para facilitar o uso do recurso pelos desenvolvedores. Neste cenário, você pôde confiar na classe de SmtpClient existente na plataforma .NET para enviar e-mails.

Próximos passos