Compartilhar via


Tutorial: Conectar um aplicativo ASP.NET Core a integrações de armazenamento .NET Aspire

Os aplicativos nativos de nuvem geralmente exigem soluções de armazenamento escalonáveis que fornecem recursos como armazenamento de blobs, filas ou bancos de dados NoSQL semiestruturados. .NET Aspire integrações simplificam as conexões com vários serviços de armazenamento, como Azure Blob Storage. Neste tutorial, você criará um aplicativo ASP.NET Core que utiliza integrações .NET Aspire para se conectar aos Armazenamentos de Filas Azure Blob Storage e Azure para enviar tíquetes de suporte. O aplicativo envia os tíquetes para uma fila para processamento e carrega um anexo para o armazenamento. Você aprenderá a:

  • Criar um aplicativo básico de .NET configurado para utilizar as integrações de .NET Aspire
  • Adicionar integrações .NET.NET Aspire para se conectar a vários serviços de armazenamento
  • Configurar e usar recursos do componente .NET.NET Aspire para enviar e receber dados

Pré-requisitos

Para trabalhar com .NET.NET Aspire, você precisa do seguinte instalado localmente:

Para obter mais informações, consulte .NET.NET Aspirede instalação e ferramentas e .NET.NET Aspiredo SDK.

Explorar o aplicativo de exemplo concluído

Uma versão concluída do aplicativo de exemplo deste tutorial está disponível no GitHub. O projeto também é estruturado como um modelo para o Azure Developer CLI, o que significa que você pode usar o comando azd up para automatizar o provisionamento de recursos do Azure, caso tenha a ferramenta instalada.

git clone https://github.com/Azure-Samples/dotnet-aspire-connect-storage.git

Configurar os recursos de Armazenamento Azure

Para este artigo, você precisará ter acesso de colaborador de dados a uma conta de Armazenamento Azure contendo um contêiner de blobs e uma fila de armazenamento. Verifique se você tem os seguintes recursos e configurações disponíveis:

Para este artigo, você precisará criar um contêiner de blob e um recurso de fila de armazenamento em seu ambiente de desenvolvimento local usando um emulador. Para fazer isso, use o Azurite. O Azurite é um Azure (emulador) compatível com a API de Armazenamento server de software livre e de software livre que é executado em um contêiner de Docker.

Para usar o emulador, você precisa instalar o Azurite.

  1. Uma conta de Armazenamento Azure – Criar uma conta de armazenamento.
  2. Um contêiner de Armazenamento de Blobs chamado fileuploads - . Criar um contêiner de Armazenamento de Blobs.
  3. Uma fila de armazenamento chamada tíquetes - . Criar uma fila de armazenamento.

Execute os seguintes comandos na CLI Azure ou no CloudShell para configurar os recursos de armazenamento de Azure necessários:

az group create --name aspirestorage --location eastus2
az storage account create -n aspirestorage -g aspirestorage -l eastus2
az storage container create -n fileuploads --account-name aspirestorage
az storage queue create -n tickets --account-name aspirestorage

Você também precisa atribuir as seguintes funções à conta de usuário na qual você está conectado com Visual Studio:

  • Contribuidor de Dados de Blob de Armazenamento – Atribuir uma Função RBAC Azure
  • Colaborador de dados da fila de armazenamento – atribuir uma função RBAC Azure

O Azure Developer CLI permite provisionar e implantar recursos Azure usando um sistema de modelos. Este tutorial fornece um modelo completo que provisiona os recursos de Azure necessários e inclui o código de aplicativo de exemplo concluído. Execute os seguintes comandos para inicializar e executar o modelo:

  1. Execute azd auth login para entrar no Azure:

    azd auth login
    
  2. Execute azd init para clonar e inicializar o modelo de exemplo:

    azd init --template dotnet-aspire-connect-storage
    
  3. Execute azd up para provisionar os recursos de Azure:

    azd up
    
  4. Quando solicitado, selecione a assinatura e a região Azure dos recursos provisionados. O modelo executa e conclui as seguintes tarefas para você:

    • Cria uma conta de armazenamento Azure com os serviços de blob e fila ativados.
    • Cria um contêiner de armazenamento de blobs chamado fileUploads
    • Cria uma fila chamada tickets
    • Atribui as seguintes funções à conta de usuário que executou o modelo.
      • Colaborador de Dados do Blob de Armazenamento
      • Colaborador de dados da fila de armazenamento

Depois que a operação for concluída com êxito, você terá duas opções para avançar:

  • Opção 1: execute o aplicativo de exemplo .NET no diretório src modelo para experimentar o aplicativo concluído.
  • Opção 2: criar o aplicativo de exemplo passo a passo usando as seções à frente e conectá-lo aos recursos de Azure provisionados pelo azd.

Criar a solução de exemplo

Crie um projeto .NET Aspire usando Visual Studio ou a CLI do .NET.

  1. Na parte superior do Visual Studio, navegue até >Nova>Projeto.
  2. Na janela de diálogo, pesquise Aspire e selecione .NET.NET Aspire Aplicação Inicial. Escolha Próximo.
  3. Na tela Configurar seu novo projeto:
    • Insira um de Nome da Solução do AspireStorage e selecione Próximo.
  4. Na tela Informações adicionais:
    • Desmarque Usar Redis para armazenar em cache (não necessário para este tutorial).
    • Selecione Criar.

Visual Studio cria uma nova solução ASP.NET Core estruturada para usar .NET Aspire.

A solução consiste nos seguintes projetos:

  • AspireStorage.ApiService – um projeto de API com configurações de serviço de .NET.NET Aspire padrão.
  • AspireStorage.AppHost – um projeto de orquestrador projetado para conectar e configurar os diferentes projetos e serviços do seu aplicativo. O orquestrador deve ser definido como o projeto de inicialização.
  • AspireStorage.ServiceDefaults – uma biblioteca de classes compartilhada para manter o código que pode ser reutilizado entre os projetos em sua solução.
  • AspireStorage.Web – um projeto BlazorServer que serve como front-end do seu aplicativo.

Adicionar o projeto Worker Service

Em seguida, adicione um projeto de Worker Service à solução para recuperar e processar mensagens conforme elas são adicionadas à fila de Armazenamento Azure.

  1. No gerenciador de soluções, clique com o botão direito do mouse no nó principal da solução AspireStorage e selecione Adicionar>Novo projeto.
  2. Pesquise e selecione o modelo Worker Service e escolha Próximo.
  3. Para o nome do projeto , insira AspireStorage.WorkerService e selecione Avançar.
  4. Na tela Informações adicionais:
    • Verifique se .NET 9.0 está selecionado.
    • Verifique se Inscrever-se em .NET.NET Aspire de orquestração está marcada e selecione Criar.

Visual Studio adiciona o projeto à sua solução e atualiza o arquivo Program.cs do projeto AspireStorage.AppHost com uma nova linha de código:

builder.AddProject<Projects.AspireStorage_WorkerService>(
    "aspirestorage-workerservice");

Visual Studio ferramentas adicionaram essa linha de código para registrar seu novo projeto com o objeto IDistributedApplicationBuilder, que permite recursos de orquestração. Para obter mais informações, consulte a visão geral da orquestração .NET.NET Aspire.

A estrutura de solução concluída deve ser semelhante à seguinte:

Uma captura de tela mostrando a estrutura da solução de exemplo de armazenamento .NET.NET Aspire.

Adicionar as integrações de .NET Aspire ao aplicativo Blazor

Adicione os pacotes de integração .NET AspireAzure Blob Storage e .NET AspireAzure do Armazenamento de Filas ao seu projeto AspireStorage.Web:

dotnet add package Aspire.Azure.Storage.Blobs
dotnet add package Aspire.Azure.Storage.Queues

Seu projeto AspireStorage.Web agora está configurado para usar integrações .NET.NET Aspire. Aqui está o arquivo atualizado AspireStorage.Web.csproj:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\AspireStorage.ServiceDefaults\AspireStorage.ServiceDefaults.csproj" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Aspire.Azure.Storage.Blobs" Version="9.0.0" />
    <PackageReference Include="Aspire.Azure.Storage.Queues" Version="9.0.0" />
  </ItemGroup>

</Project>

A próxima etapa é adicionar as integrações ao aplicativo.

No arquivo Program.cs do projeto AspireStorage.Web, adicione chamadas aos métodos de extensão AddAzureBlobClient e AddAzureQueueClient após a criação do builder, mas antes da chamada para AddServiceDefaults. Para obter mais informações, consulte .NET.NET Aspire configurações padrão de serviço. Forneça o nome da cadeia de conexão como um parâmetro.

using AspireStorage.Web;
using AspireStorage.Web.Components;

using Azure.Storage.Blobs;
using Azure.Storage.Queues;

var builder = WebApplication.CreateBuilder(args);

builder.AddAzureBlobClient("BlobConnection");
builder.AddAzureQueueClient("QueueConnection");

// Add service defaults & Aspire components.
builder.AddServiceDefaults();

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.AddOutputCache();

builder.Services.AddHttpClient<WeatherApiClient>(client =>
    {
        // This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
        // Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
        client.BaseAddress = new("https+http://apiservice");
    });

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}
else
{
    // In development, create the blob container and queue if they don't exist.
    var blobService = app.Services.GetRequiredService<BlobServiceClient>();
    var docsContainer = blobService.GetBlobContainerClient("fileuploads");

    await docsContainer.CreateIfNotExistsAsync();

    var queueService = app.Services.GetRequiredService<QueueServiceClient>();
    var queueClient = queueService.GetQueueClient("tickets");

    await queueClient.CreateIfNotExistsAsync();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.UseOutputCache();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.MapDefaultEndpoints();

app.Run();
using AspireStorage.Web;
using AspireStorage.Web.Components;

using Azure.Storage.Blobs;
using Azure.Storage.Queues;

var builder = WebApplication.CreateBuilder(args);

builder.AddAzureBlobClient("BlobConnection");
builder.AddAzureQueueClient("QueueConnection");

// Add service defaults & Aspire components.
builder.AddServiceDefaults();

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.AddOutputCache();

builder.Services.AddHttpClient<WeatherApiClient>(client =>
    {
        // This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
        // Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
        client.BaseAddress = new("https+http://apiservice");
    });

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.UseOutputCache();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.MapDefaultEndpoints();

app.Run();

Com as instruções de using adicionais, esses métodos realizam as seguintes tarefas:

Quando o projeto AspireStorage.Web for iniciado, ele criará um contêiner de fileuploads no Armazenamento de Blobs do Azurite e uma fila de tickets no Armazenamento de Filas do Azurite. Isso é condicional quando o aplicativo está em execução em um ambiente de desenvolvimento. Quando o aplicativo está em execução em um ambiente de produção, supõe-se que o contêiner e a fila já tenham sido criados.

Adicionar a integração .NET Aspire ao Worker Service

O serviço de trabalho lida com a retirada de mensagens da fila de armazenamento do Azure para processamento. Adicione o pacote de integração do .NET AspireAzure Queue Storage ao seu aplicativo AspireStorage.WorkerService.

dotnet add package Aspire.Azure.Storage.Queues

No arquivo Program.cs do projeto AspireStorage.WorkerService, adicione uma chamada ao método de extensão AddAzureQueueClient após a criação do builder, mas antes da chamada para AddServiceDefaults:

using AspireStorage.WorkerService;

var builder = Host.CreateApplicationBuilder(args);

builder.AddAzureQueueClient("QueueConnection");

builder.AddServiceDefaults();
builder.Services.AddHostedService<WorkerService>();

var host = builder.Build();
host.Run();

Esse método lida com as seguintes tarefas:

  • Registre um QueueServiceClient com o contêiner de DI para se conectar ao Azure Storage Queues.
  • Habilite automaticamente as verificações de integridade, o registro em log e a telemetria correspondentes para os respectivos serviços.

Criar o formulário

O aplicativo precisa de um formulário para que o usuário possa enviar as informações do tíquete de suporte e fazer o upload de um anexo. O aplicativo carrega o arquivo anexado na propriedade Document (IFormFile) para Azure Blob Storage usando o BlobServiceClientinjetado. O QueueServiceClient envia uma mensagem composta por Title e Description para a Fila de Armazenamento Azure.

Utilize a seguinte marcação Razor para criar um formulário básico, substituindo o conteúdo do arquivo Home.razor no diretório AspireStorage.Web/Components/Pages:

@page "/"

@using System.ComponentModel.DataAnnotations
@using Azure.Storage.Blobs
@using Azure.Storage.Queues

@inject BlobServiceClient BlobClient
@inject QueueServiceClient QueueServiceClient

<PageTitle>Home</PageTitle>

<div class="text-center">
    <h1 class="display-4">Request Support</h1>
</div>

<EditForm Model="@Ticket" FormName="Tickets" method="post"
          OnValidSubmit="@HandleValidSubmit" enctype="multipart/form-data">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <div class="mb-4">
        <label>Issue Title</label>
        <InputText class="form-control" @bind-Value="@Ticket.Title" />
        <ValidationMessage For="() => Ticket.Title" />
    </div>
    <div class="mb-4">
        <label>Issue Description</label>
        <InputText class="form-control" @bind-Value="@Ticket.Description" />
        <ValidationMessage For="() => Ticket.Description" />
    </div>
    <div class="mb-4">
        <label>Attachment</label>
        <InputFile class="form-control" name="Ticket.Document" />
        <ValidationMessage For="() => Ticket.Document" />
    </div>
    <button class="btn btn-primary" type="submit">Submit</button>
    <button class="btn btn-danger mx-2" type="reset" @onclick=@ClearForm>Clear</button>
</EditForm>

@code {
    [SupplyParameterFromForm(FormName = "Tickets")]
    private SupportTicket Ticket { get; set; } = new();

    private async Task HandleValidSubmit()
    {
        var docsContainer = BlobClient.GetBlobContainerClient("fileuploads");

        // Upload file to blob storage
        await docsContainer.UploadBlobAsync(
            Ticket.Document.FileName,
            Ticket.Document.OpenReadStream());

        // Send message to queue
        var queueClient = QueueServiceClient.GetQueueClient("tickets");

        await queueClient.SendMessageAsync(
             $"{Ticket.Title} - {Ticket.Description}");

        ClearForm();
    }

    private void ClearForm() => Ticket = new();

    private class SupportTicket()
    {
        [Required] public string Title { get; set; } = default!;
        [Required] public string Description { get; set; } = default!;
        [Required] public IFormFile Document { get; set; } = default!;
    }
}

Para obter mais informações sobre como criar formulários em Blazor, consulte ASP.NET CoreBlazor visão geral dos formulários.

Atualizar o AppHost

O projeto AspireStorage.AppHost é o orquestrador do seu aplicativo. Ele é responsável por conectar e configurar os diferentes projetos e serviços do seu aplicativo. O orquestrador deve ser definido como o projeto de inicialização.

Para adicionar Azure suporte de hospedagem de Armazenamento ao seu IDistributedApplicationBuilder, instale o 📦Aspire. Hospedagem.Azure. Armazenamento pacote NuGet.

dotnet add package Aspire.Hosting.Azure.Storage

Substitua o conteúdo do arquivo no projeto AspireStorage.AppHost pelo seguinte código:

using Microsoft.Extensions.Hosting;

var builder = DistributedApplication.CreateBuilder(args);

var storage = builder.AddAzureStorage("Storage");

if (builder.Environment.IsDevelopment())
{
    storage.RunAsEmulator();
}

var blobs = storage.AddBlobs("BlobConnection");
var queues = storage.AddQueues("QueueConnection");

var apiService = builder.AddProject<Projects.AspireStorage_ApiService>("apiservice");

builder.AddProject<Projects.AspireStorage_Web>("webfrontend")
    .WithExternalHttpEndpoints()
    .WithReference(apiService)
    .WithReference(blobs)
    .WithReference(queues); 

builder.AddProject<Projects.AspireStorage_WorkerService>("aspirestorage-workerservice")
    .WithReference(queues);

builder.Build().Run();

O código acima adiciona armazenamento Azure, blobs e filas; e, quando está no modo de desenvolvimento, utiliza o emulador. Cada projeto define referências para esses recursos dos quais eles dependem.

using Microsoft.Extensions.Hosting;

var builder = DistributedApplication.CreateBuilder(args);

var storage = builder.AddAzureStorage("Storage");

var blobs = storage.AddBlobs("BlobConnection");
var queues = storage.AddQueues("QueueConnection");

var apiService = builder.AddProject<Projects.AspireStorage_ApiService>("apiservice");

builder.AddProject<Projects.AspireStorage_Web>("webfrontend")
    .WithExternalHttpEndpoints()
    .WithReference(apiService)
    .WithReference(blobs)
    .WithReference(queues); 

builder.AddProject<Projects.AspireStorage_WorkerService>("aspirestorage-workerservice")
    .WithReference(queues);

builder.Build().Run();

O código anterior adiciona armazenamento Azure, blobs e filas, e define referências a esses recursos em cada projeto que depende deles.

Processar os itens na fila

Quando uma nova mensagem é colocada na fila tickets, o serviço de trabalho deve recuperar, processar e excluir a mensagem. Atualize a classe Worker.cs, substituindo o conteúdo pelo seguinte código:

using Azure.Storage.Queues;
using Azure.Storage.Queues.Models;

namespace AspireStorage.WorkerService;

public sealed class WorkerService(
    QueueServiceClient client,
    ILogger<WorkerService> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var queueClient = client.GetQueueClient("tickets");
        await queueClient.CreateIfNotExistsAsync(cancellationToken: stoppingToken);

        while (!stoppingToken.IsCancellationRequested)
        {
            QueueMessage[] messages =
                await queueClient.ReceiveMessagesAsync(
                    maxMessages: 25, cancellationToken: stoppingToken);

            foreach (var message in messages)
            {
                logger.LogInformation(
                    "Message from queue: {Message}", message.MessageText);

                await queueClient.DeleteMessageAsync(
                    message.MessageId,
                    message.PopReceipt,
                    cancellationToken: stoppingToken);
            }

            // TODO: Determine an appropriate time to wait 
            // before checking for more messages.
            await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
        }
    }
}

Antes que o serviço de trabalho possa processar mensagens, ele precisa ser capaz de se conectar à fila de armazenamento Azure. Com o Azurite, você precisa garantir que a fila esteja disponível antes que o serviço de trabalho comece a executar o processamento da fila de mensagens.

using Azure.Storage.Queues;
using Azure.Storage.Queues.Models;

namespace AspireStorage.WorkerService;

public sealed class WorkerService(
    QueueServiceClient client,
    ILogger<WorkerService> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var queueClient = client.GetQueueClient("tickets");
        while (!stoppingToken.IsCancellationRequested)
        {
            QueueMessage[] messages =
                await queueClient.ReceiveMessagesAsync(
                    maxMessages: 25, cancellationToken: stoppingToken);

            foreach (var message in messages)
            {
                logger.LogInformation(
                    "Message from queue: {Message}", message.MessageText);

                await queueClient.DeleteMessageAsync(
                    message.MessageId,
                    message.PopReceipt,
                    cancellationToken: stoppingToken);
            }

            // TODO: Determine an appropriate time to wait 
            // before checking for more messages.
            await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
        }
    }
}

O serviço de trabalho processa mensagens conectando-se à fila de armazenamento Azure e tirando mensagens da fila.

O serviço de trabalho processa as mensagens na fila e as exclui quando elas são processadas.

Configurar as cadeias de conexão

Os projetos AspireStorage e AspireStorage.Worker devem ser configurados para se conectar à conta de armazenamento correta Azure que você criou anteriormente. Você pode especificar os pontos de extremidade para os serviços de blobs e filas na conta de armazenamento usando o arquivo appsettings.json em cada projeto.

  1. No projeto AspireStorage, adicione a seguinte configuração ao arquivo appsettings.Development.json:

      "ConnectionStrings": {
        "BlobConnection": "https://<your-storage-account-name>.blob.core.windows.net/",
        "QueueConnection": "https://<your-storage-account-name>.queue.core.windows.net/"
      }
    
  2. No projeto AspireStorage.Worker, adicione a seguinte configuração ao arquivo appsettings.Development.json:

      "ConnectionStrings": {
        "QueueConnection": "https://<your-storage-account-name>.queue.core.windows.net/"
      }
    

Executar e testar o aplicativo localmente

O aplicativo de exemplo agora está pronto para teste. Verifique se os dados de formulário enviados são enviados para Azure Blob Storage e Azure Armazenamento de Filas concluindo as seguintes etapas:

  1. Pressione o botão executar na parte superior do Visual Studio para iniciar seu painel de projeto .NET Aspire no navegador.

  2. Na página de recursos, na linha aspirestorage.web, clique no link na coluna pontos de extremidade para abrir a interface do usuário do aplicativo.

    Uma captura de tela mostrando a home page do aplicativo de suporte .NET.NET Aspire.

  3. Insira dados de exemplo nos campos Title e Description formulário e selecione um arquivo simples para carregar.

  4. Selecione o botão Enviar e o formulário envia o tíquete de suporte para processamento e limpa o formulário.

  5. Em uma guia separada do navegador, use o portal para navegar até a do navegador de Armazenamento em sua conta de armazenamento do .

  6. Selecione contêineres e navegue até o contêiner documentos para ver o arquivo carregado.

  7. Você pode verificar se a mensagem na fila foi processada consultando os logs do projeto no painel .NET.NET Aspiree selecionando o aspirestorage.workerservice no menu suspenso.

    Uma captura de tela mostrando a saída do console do aplicativo Worker.

Resumo

O aplicativo de exemplo que você criou demonstra a persistência de blobs de um Aplicativo Web ASP.NET CoreBlazor e o processamento de filas em um .NET Worker Service. Seu aplicativo se conecta ao Armazenamento Azure usando as integrações .NET Aspire. O aplicativo envia os tíquetes de suporte para uma fila para processamento e faz upload de um anexo para o armazenamento.

Como você opta por usar o Azurite, não é necessário limpar esses recursos quando terminar de testá-los, pois os criou localmente no contexto de um emulador. O emulador permitiu que você testasse seu aplicativo localmente sem incorrer em custos, pois nenhum recurso Azure foi provisionado ou criado.

Limpar recursos

Execute o seguinte comando Azure CLI para excluir o grupo de recursos quando você não precisar mais dos recursos de Azure que você criou. Excluir o grupo de recursos também exclui os recursos contidos nele.

az group delete --name <your-resource-group-name>

Para obter mais informações, consulte Limpeza de recursos no Azure.