Estrutura de projeto de aplicativos do Blazor

Dica

Esse conteúdo é um trecho do livro eletrônico, Blazor para Desenvolvedores do ASP NET Web Forms para o Azure, disponível no .NET Docs ou como um PDF para download gratuito que pode ser lido offline.

BlazorImagem em miniaturada da capa do livro eletrônico para-Desenvolvedores-do-ASP-NET-Web-Forms.

Apesar das diferenças significativas na estrutura do projeto, o ASP.NET Web Forms e o Blazor compartilham muitos conceitos semelhantes. Aqui, veremos a estrutura de um projeto do Blazor e a compararemos com um projeto do ASP.NET Web Forms.

Para criar seu primeiro aplicativo do Blazor, siga as instruções nas etapas de introdução do Blazor. Siga as instruções para criar um aplicativo do Blazor Server ou um aplicativo WebAssembly do Blazorhospedado no ASP.NET Core. Exceto pela lógica específica do modelo de hospedagem, grande parte do código nos projetos é igual.

Arquivo de projeto

Aplicativos do Blazor Server são projetos .NET. O arquivo de projeto do aplicativo Blazor Server é o mais simples possível:

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

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

</Project>

O arquivo de projeto de um aplicativo WebAssembly do Blazorparece um pouco mais intrincado (os números exatos da versão podem variar):

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

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0" PrivateAssets="all" />
  </ItemGroup>

</Project>

O projeto de WebAssemblydo Blazor visa Microsoft.NET.Sdk.BlazorWebAssembly em vez do SDK do Microsoft.NET.Sdk.Web, pois é executado no navegador em um runtime .NET com base em WebAssembly. Você não pode instalar o .NET em um navegador da Web como pode em um servidor ou computador de desenvolvedor. Consequentemente, o projeto faz referência à estrutura do Blazor usando referências de pacote individuais.

Em comparação, um projeto de ASP.NET Web Forms padrão inclui quase 300 linhas de XML em seu arquivo .csproj, a maioria das quais está listando explicitamente os vários arquivos de código e conteúdo no projeto. Desde o lançamento do .NET 5, os aplicativos BlazorServer e WebAssembly do Blazor podem compartilhar facilmente um runtime unificado.

Apesar do suporte, as referências de assembly individuais são menos comuns em projetos do .NET. A maioria das dependências do projeto é tratada como referências de pacote NuGet. Você só precisa fazer referência a dependências de pacote de nível superior em projetos do .NET. As dependências transitivas são incluídas automaticamente. Em vez de usar o arquivo packages.config, normalmente encontrado em projetos do ASP.NET Web Forms para fazer referência a pacotes, as referências de pacote são adicionadas ao arquivo de projeto usando o elemento <PackageReference>.

<ItemGroup>
  <PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>

Ponto de entrada

O ponto de entrada do aplicativo Blazor Server é definido no arquivo Program.cs, como você veria em um aplicativo do Console. Quando o aplicativo é executado, ele cria e executa uma instância de host da Web usando padrões específicos aos aplicativos Web. O host da Web gerencia o ciclo de vida do aplicativo do Blazor Server e configura serviços no nível do host. Entre os exemplos desses serviços estão configuração, registro em log, injeção de dependência e o servidor HTTP. Esse código é principalmente clichê e geralmente permanece inalterado.

using BlazorApp3.Areas.Identity;
using BlazorApp3.Data;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
builder.Services.AddSingleton<WeatherForecastService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    // 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.UseRouting();

app.UseAuthorization();

app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

Aplicativos WebAssemblydo Blazor também definem um ponto de entrada em Program.cs. O código parece um pouco diferente. O código é semelhante pelo fato de configurar o host do aplicativo para fornecer os mesmos serviços de nível de host para o aplicativo. No entanto, o host do aplicativo WebAssembly não configura um servidor HTTP, pois ele é executado diretamente no navegador.

Os aplicativos Blazor não usam um arquivo Global.asax para definir a lógica de inicialização do aplicativo. Em vez disso, essa lógica está contida em Program.cs ou em uma classe Startup relacionada referenciada a partir de Program.cs. De qualquer forma, esse código é usado para configurar o aplicativo e quaisquer serviços específicos ao aplicativo.

Em um aplicativo do Blazor Server, o arquivo Program.cs mostrado é usado para configurar o ponto de extremidade para a conexão em tempo real usada pelo Blazor entre os navegadores cliente e o servidor.

Em um aplicativo WebAssembly do Blazor, o arquivo Program.cs define os componentes raiz do aplicativo e onde eles devem ser renderizados:

using BlazorApp1;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

await builder.Build().RunAsync();

Arquivos estáticos

Ao contrário de projetos do ASP.NET Web Forms, nem todos os arquivos em um projeto do Blazor podem ser solicitados como arquivos estáticos. Somente os arquivos na pasta wwwroot são endereçáveis na Web. Essa pasta é conhecida como a "raiz da Web" do aplicativo. Qualquer coisa fora da raiz da Web do aplicativo não é endereçável na Web. Essa configuração fornece um nível adicional de segurança que impede a exposição acidental de arquivos de projeto pela Web.

Configuração

A configuração em aplicativos do ASP.NET Web Forms normalmente é tratada usando um ou mais arquivos web.config. Normalmente, os aplicativos Blazor não têm arquivos web.config. Se tiverem, o arquivo só será usado para definir configurações específicas ao IIS, quando hospedado no IIS. Em vez disso, aplicativos do Blazor Server usam as abstrações de configuração do ASP.NET Core. (Atualmente, os aplicativos WebAssembly do Blazor não dão suporte às mesmas abstrações de configuração, mas esse recurso pode ser adicionado futuramente.) Por exemplo, o aplicativo padrão do Blazor Server armazena algumas configurações em appsettings.json.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Você aprenderá mais sobre a configuração em projetos ASP.NET Core na seção Configuração.

Componentes do Razor

A maioria dos arquivos em projetos do Blazor tem extensão .razor. Razor é uma linguagem de modelagem baseada em HTML e em C# que é usada para gerar dinamicamente a interface do usuário da Web. Os arquivos .razor definem componentes que compõem a interface do usuário do aplicativo. Na maioria das vezes, os componentes são idênticos para aplicativos do Blazor Server e do WebAssembly do Blazor. Os componentes no Blazor são análogos aos controles de usuário no ASP.NET Web Forms.

Cada arquivo de componente Razor é compilado em uma classe .NET quando o projeto é criado. A classe gerada captura o estado do componente, a lógica de renderização, os métodos de ciclo de vida, os manipuladores de eventos e outras lógicas. Você aprenderá mais sobre a criação de componentes na seção Compilação de componentes da interface do usuário reutilizáveis com Blazor.

Os arquivos _Imports.razor não são arquivos de componente Razor. Em vez disso, eles definem um conjunto de diretivas Razor para importar em outros arquivos .razor dentro da mesma pasta e em suas subpastas. Por exemplo, um arquivo _Imports.razor é uma maneira convencional de adicionar diretivas using para namespaces comumente usados:

@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using BlazorApp1
@using BlazorApp1.Shared

Páginas

Onde estão as páginas nos aplicativos Blazor? Blazor não define uma extensão de arquivo separada para páginas endereçáveis, como os arquivos .aspx em aplicativos ASP.NET Web Forms. Em vez disso, as páginas são definidas pela atribuição de rotas aos componentes. Normalmente, uma rota é atribuída usando a diretiva Razor @page. Por exemplo, o componente Counter criado no arquivo Pages/Counter.razor define a seguinte rota:

@page "/counter"

O roteamento no Blazor é tratado no lado do cliente, não no servidor. À medida que o usuário usa o navegador, Blazor intercepta a navegação e renderiza o componente com a rota correspondente.

No momento, as rotas de componente não são inferidas pelo local do arquivo do componente, como são com páginas .aspx ou com Razor Pages do ASP.NET Core. Esse recurso pode ser adicionado no futuro. Cada rota deve ser especificada explicitamente no componente. Armazenar componentes roteáveis em uma pasta Pages não tem significado especial e é puramente uma convenção.

Você aprenderá mais sobre o roteamento no Blazor na seção Páginas, roteamento e layouts.

Layout

Em aplicativos ASP.NET Web Forms, um layout de página comum é tratado usando páginas mestras (Site.Master). Em aplicativos Blazor, o layout da página é tratado usando componentes de layout (Shared/MainLayout.razor). Os componentes de layout são discutidos mais detalhadamente na seção Página, roteamento e layouts.

Inicialização do Blazor

Para inicializar o Blazor, o aplicativo deve:

  • Especificar onde, na página, o componente raiz (App.Razor) deve ser renderizado.
  • Adicionar o script de estrutura do Blazor correspondente.

No aplicativo Blazor Server, a página de host do componente raiz é definida no arquivo _Host.cshtml. Esse arquivo define uma Razor Page, não um componente. As Razor Pages usam a sintaxe Razor para definir uma página endereçável ao servidor, muito parecida com uma página .aspx.

@page "/"
@namespace BlazorApp3.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = "_Layout";
}

<component type="typeof(App)" render-mode="ServerPrerendered" />

O atributo render-mode é usado para definir onde um componente de nível raiz deve ser renderizado. A opção RenderMode indica a maneira pela qual o componente deve ser renderizado. A tabela a seguir descreve as opções de RenderMode com suporte.

Opção Descrição
RenderMode.Server Renderizado interativamente quando uma conexão com o navegador é estabelecida
RenderMode.ServerPrerendered Primeiro pré-renderizado e, em seguida, renderizado interativamente
RenderMode.Static Renderizado como conteúdo estático

O arquivo _Layout.cshtml inclui o HTML padrão para o aplicativo e seus componentes.

@using Microsoft.AspNetCore.Components.Web
@namespace BlazorApp3.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="BlazorApp3.styles.css" rel="stylesheet" />
    <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>
<body>
    @RenderBody()

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <script src="_framework/blazor.server.js"></script>
</body>
</html>

A referência de script para _framework/blazor.server.js estabelece a conexão em tempo real com o servidor e, em seguida, lida com todas as interações do usuário e atualizações de interface do usuário.

No aplicativo WebAssembly do Blazor, a página do host é um arquivo HTML estático simples em wwwroot/index.html. O elemento <div> com a id chamada app é usado para indicar onde o componente raiz deve ser renderizado.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>BlazorApp1</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="BlazorApp1.styles.css" rel="stylesheet" />
</head>

<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
</body>

</html>

O componente raiz a ser renderizado é especificado no arquivo Program.cs do aplicativo com a flexibilidade de registrar serviços por meio da injeção de dependência. Para saber mais, confira Injeção de dependência do Blazor no ASP.NET Core.

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

Saída do build

Quando um projeto Blazor é criado, todos os arquivos de código e componente Razor são compilados em um único assembly. Ao contrário dos projetos em ASP.NET Web Forms, o Blazor não dá suporte à compilação de runtime da lógica da interface do usuário.

Executar o aplicativo com Recarga Dinâmica

Para executar o aplicativo Blazor Server, pressione F5 no Visual Studio para executar com o depurador anexado ou Ctrl + F5 para executar sem o depurador anexado.

Para executar o aplicativo WebAssembly do Blazor, escolha uma das seguintes abordagens:

  • Execute o projeto cliente diretamente usando o servidor de desenvolvimento.
  • Execute o projeto do servidor ao hospedar o aplicativo com ASP.NET Core.

Os aplicativos WebAssemblydo Blazor podem ser depurados no navegador e no Visual Studio. Confira Depurar WebAssembly do Blazor no ASP.NET Core para obter detalhes.

Os aplicativos Blazor Server e WebAssembly do Blazor dão suporte à Recarga Dinâmica no Visual Studio. Recarga Dinâmica é um recurso que atualiza automaticamente e dinamicamente as alterações feitas em um aplicativo Blazor no navegador. Você pode habilitar ou desabilitar a Recarga Dinâmica no ícone na barra de ferramentas:

Visual Studio 2022: item de menu e ícone de Recarga Dinâmica.

Selecionar o circunflexo ao lado do ícone revela opções adicionais. Você pode habilitar ou desabilitar a Recarga Dinâmica, reiniciar o aplicativo e escolher se a Recarga Dinâmica deve ocorrer sempre que um arquivo for salvo.

Visual Studio 2022: item de menu de Recarga Dinâmica com opções expandidas.

Também é possível acessar opções de configuração adicionais. A caixa de diálogo de configuração permite que você especifique se a Recarga Dinâmica deve ser habilitada durante a depuração (juntamente com Editar e Continuar), ao iniciar sem depuração ou quando um arquivo é salvo.

Visual Studio 2022: opções de configuração da Recarga Dinâmica na caixa de diálogo

O "loop interno do desenvolvedor" foi muito simplificado com a Recarga Dinâmica. Sem a Recarga Dinâmica, um desenvolvedor de Blazor normalmente precisaria reiniciar e executar novamente o aplicativo após cada alteração, navegando até a parte apropriada do aplicativo conforme o necessário. Com a Recarga Dinâmica, as alterações podem ser feitas no aplicativo em execução sem a necessidade de reiniciar na maioria dos casos. A Recarga Dinâmica ainda mantém o estado das páginas, portanto, não é necessário inserir novamente os valores do formulário ou devolver o aplicativo de alguma outra maneira ao estado necessário.