Partilhar via


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

Os aplicativos nativos da nuvem geralmente exigem soluções de armazenamento escaláveis que fornecem recursos como armazenamento de blob, 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, irá criar uma aplicação ASP.NET Core que usa integrações .NET Aspire para se conectar ao Armazenamento de Filas do Azure Blob Storage e Azure para enviar pedidos 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 .NET básico configurado para usar integrações .NET Aspire
  • Adicione integrações .NET.NET Aspire para se conectar a vários serviços de armazenamento
  • Configurar e usar os recursos do .NET.NET Aspire Componente 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 Aspire configuração e ferramentas, e .NET.NET Aspire SDK.

Explore o aplicativo de exemplo concluído

Uma versão completa do aplicativo de exemplo deste tutorial está disponível em 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 Azure se tiver 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á de acesso de contribuidor de dados a uma conta de armazenamento Azure com um contêiner de blob e fila de armazenamento. Certifique-se de ter 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 Azurite. O Azurite é um server (emulador) gratuito, de código aberto e compatível com a API de armazenamento Azure multiplataforma que é executado em um contêiner Docker.

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

  1. Uma conta de armazenamento Azure - Crie uma conta de armazenamento.
  2. Um contêiner de Armazenamento de Blob chamado fileuploads - Criar um contêiner de armazenamento de blob.
  3. Uma fila de armazenamento nomeada - Criar uma fila de armazenamento.

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

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 com a qual você está conectado em Visual Studio:

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 iniciar sessão 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 Azure:

    azd up
    
  4. Quando solicitado, selecione a assinatura e a região Azure para os recursos provisionados. O modelo é executado 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 blob chamado fileUploads
    • Cria uma fila chamada tickets
    • Atribui as seguintes funções à conta de usuário que executou o modelo.
      • Contribuidor de dados de Blob de armazenamento
      • Contribuidor 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 de modelo src para experimentar o aplicativo concluído.
  • Opção 2: Crie o aplicativo de exemplo passo a passo usando as seções à frente e conecte-o aos recursos de Azure provisionados pelo azd.

Criar a solução de exemplo

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

  1. No topo de Visual Studio, vá para Arquivo>Novo>Projeto.
  2. Na janela de diálogo, procure por Aspire e selecione Aplicação Inicial .NET.NET Aspire. Escolha Próximo.
  3. Na tela Configurar seu novo projeto:
    • Insira um Nome da Solução de AspireStorage e selecione Avançar.
  4. No ecrã de Informações adicionais
    • Desmarque Usar Redis para de cache (não necessário para este tutorial).
    • Selecione Criar.

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

A solução consiste nos seguintes projetos:

  • AspireStorage.ApiService - Um projeto de API com configurações de serviço .NET.NET Aspire padrão.
  • AspireStorage.AppHost - Um projeto orquestrador projetado para conectar e configurar os diferentes projetos e serviços do seu aplicativo. O orquestrador deve ser definido como o projeto de arranque.
  • AspireStorage.ServiceDefaults - Uma biblioteca de classes compartilhada para armazenar código que pode ser reutilizado em todos 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 Worker Service à solução para recuperar e processar mensagens à medida que são adicionadas à fila de armazenamento Azure.

  1. No Explorador de Soluções, clique com o botão direito do rato no nó de nível superior AspireStorage e selecione Adicionar>Novo Projeto.
  2. Procure e selecione o modelo Worker Service e escolha Avançar.
  3. Para o nome do projeto , digite AspireStorage.WorkerService e selecione Avançar.
  4. No ecrã de Informações adicionais
    • Certifique-se de .NET 9.0 está selecionado.
    • Verifique se Inscrever-se em .NET.NET Aspire de orquestração está marcado e, em seguida, selecione Criar.

adiciona o projeto à sua solução e atualiza o arquivo de 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 da solução concluída deve ser semelhante à seguinte:

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

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

Adicione a integração .NET AspireAzure Blob Storage e os pacotes de integração do Queue Storage .NET AspireAzure 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>

O próximo passo é adicionar as integrações ao aplicativo.

No arquivo do projeto AspireStorage.Web , adicione chamadas aos métodos de extensão e após a criação do mas antes da chamada para . Para mais informações, consulte os padrões de serviço .NET.NET Aspire. 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 using adicionais, esses métodos realizam as seguintes tarefas:

Quando o projeto AspireStorage.Web for iniciado, ele criará um contêiner de fileuploads no Azurite Blob Storage e uma fila de tickets no Azurite Queue Storage. Isso é condicional quando o aplicativo está sendo executado em um ambiente de desenvolvimento. Quando o aplicativo está sendo executado em um ambiente de produção, presume-se que o contêiner e a fila já tenham sido criados.

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

O serviço de trabalho encarrega-se de retirar mensagens da fila de armazenamento Azure para processamento. Adicione o pacote de integração .NET AspireAzure Queue Storage ao seu aplicativo AspireStorage.WorkerService:

dotnet add package Aspire.Azure.Storage.Queues

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

using AspireStorage.WorkerService;

var builder = Host.CreateApplicationBuilder(args);

builder.AddAzureQueueClient("QueueConnection");

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

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

Este método lida com as seguintes tarefas:

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

Criar o formulário

A aplicação requer um formulário para que o utilizador possa submeter informações de pedido de suporte e anexar um ficheiro. A aplicação envia o ficheiro em anexo na propriedade Document (IFormFile) para Azure Blob Storage utilizando o BlobServiceClientinjetado. O QueueServiceClient envia uma mensagem, composta por Title e Description, à fila de armazenamento Azure.

Use a seguinte marcação Razor para criar um formulário básico, substituindo o conteúdo do ficheiro 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 no Blazor, consulte ASP.NET CoreBlazor visão geral de 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 arranque.

Para adicionar o suporte de hospedagem do Azure Storage 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 com o 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 anterior adiciona armazenamento Azure, blobs e filas e, quando no modo de desenvolvimento, usa o emulador. Cada projeto define referências para esses recursos dos quais depende.

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 Azure armazenamento, blobs e filas e define referências para esses recursos dentro de cada projeto que dependem deles.

Processar os itens na fila

Quando uma nova mensagem é colocada na fila de 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 de Azure e retirando mensagens da fila.

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

Configurar as cadeias de conexão

O projeto AspireStorage e o projeto AspireStorage.Worker devem ser configurados para se conectar à correta Conta de Armazenamento Azure que você criou anteriormente. Pode especificar os endpoints para os serviços de blob e fila na conta de armazenamento, utilizando o ficheiro 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 do formulário enviado são enviados para o 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 o painel do 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 de utilizador do seu aplicativo.

    Uma captura de tela mostrando a página inicial do aplicativo de suporte do .NET.NET Aspire.

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

  4. Selecione o botão Enviar, e o formulário envia automaticamente o pedido de suporte para processamento e limpa o formulário.

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

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

  7. Você pode verificar se a mensagem na fila foi processada examinando o logs do Project do painel .NET.NET Aspiree selecionando o aspirestorage.workerservice na lista suspensa.

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

Resumo

O aplicativo de exemplo que você criou demonstra blobs persistentes de um ASP.NET CoreBlazor Web App e filas de processamento em um .NET Worker Service. A sua aplicação liga-se ao armazenamento Azure através de integrações .NET Aspire. A aplicação envia os pedidos de suporte para uma fila para processamento e carrega um anexo para armazenamento.

Como você opta por usar o Azurite, não há necessidade de limpar esses recursos quando terminar de testá-los, pois você os criou localmente no contexto de um emulador. O emulador permitiu que você testasse seu aplicativo localmente sem incorrer em custos, já que nenhum Azure recursos foram provisionados ou criados.

Limpar recursos

Execute o seguinte comando Azure CLI para excluir o grupo de recursos quando não precisar mais dos recursos Azure criados. A exclusão do grupo de recursos também exclui os recursos contidos nele.

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

Para mais informações, veja Limpeza de recursos em Azure.