Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.
Advertência
Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 10 deste artigo.
O Visual Studio fornece modelos de projeto para criar aplicativos de página única (SPAs) com base em tecnologias JavaScript, como Angular, React e Vue , que têm um back-end ASP.NET Core. Estes modelos:
- Crie uma solução do Visual Studio com um projeto de frontend e um projeto de back-end.
- Use o tipo de projeto Visual Studio para JavaScript e TypeScript (.esproj) para o frontend.
- Use um projeto ASP.NET Core para o back-end.
Os projetos criados usando os modelos do Visual Studio podem ser executados a partir da linha de comando no Windows, Linux e macOS. Para executar o aplicativo, use dotnet run --launch-profile https para executar o projeto de servidor. A execução do projeto de servidor inicia automaticamente o servidor de desenvolvimento JavaScript frontend. O perfil de lançamento https é necessário atualmente.
Tutoriais do Visual Studio
Para começar, siga um dos tutoriais na documentação do Visual Studio:
- Criar um aplicativo ASP.NET Core com o Angular
- Criar um aplicativo ASP.NET Core com o React
- Crie um aplicativo ASP.NET Core com o Vue
Para obter mais informações, consulte JavaScript e TypeScript no Visual Studio
ASP.NET Modelos de SPA principais
O Visual Studio inclui modelos para criar aplicativos ASP.NET Core com um front-end JavaScript ou TypeScript. Esses modelos estão disponíveis no Visual Studio 2022 versão 17.8 ou posterior com a carga de trabalho de desenvolvimento ASP.NET e Web instalada.
Os modelos do Visual Studio para criar aplicativos ASP.NET Core com um frontend JavaScript ou TypeScript oferecem os seguintes benefícios:
- Separação limpa do projeto entre o frontend e o backend.
- Mantenha-se up-toatualizado com as versões mais recentes do framework frontend.
- Integre com as mais recentes ferramentas de linha de comando do frontend framework, como o Vite.
- Modelos para JavaScript e TypeScript (apenas TypeScript para Angular).
- Rica experiência de edição de código JavaScript e TypeScript.
- Integre ferramentas de compilação JavaScript com a compilação .NET.
- Interface do usuário de gestão de dependências do npm.
- Compatível com depuração de código do Visual Studio e configuração de inicialização.
- Execute testes de unidade frontend no Test Explorer usando estruturas de teste JavaScript.
Modelos de SPA ASP.NET Core herdados
As versões anteriores do SDK do .NET incluíam o que agora são modelos herdados para criar aplicativos SPA com o ASP.NET Core. Para obter documentação sobre esses modelos mais antigos, consulte a versão .NET 7 da visão geral do SPA e os artigos Angular e React .
Arquitetura de modelos de aplicativos de página única
Os modelos de Aplicativo de Página Única (SPA) para Angular e React oferecem a capacidade de desenvolver aplicativos Angular e React hospedados em um servidor back-end .NET.
No momento da publicação, os arquivos do aplicativo Angular e React são copiados para a wwwroot pasta e são servidos por meio do Static File Middleware.
Em vez de retornar HTTP 404 (Não encontrado), uma rota de fallback manipula solicitações desconhecidas para o backend e serve a index.html para a SPA.
Durante o desenvolvimento, o aplicativo é configurado para usar o proxy frontend. React e Angular usam o mesmo proxy frontend.
Quando o aplicativo é iniciado, a index.html página é aberta no navegador. Um middleware especial que só é ativado no desenvolvimento:
- Interceta as solicitações recebidas.
- Verifica se o proxy está em execução.
- Redireciona para a URL do proxy se ele estiver em execução ou inicia uma nova instância do proxy.
- Retorna uma página para o navegador que é atualizada automaticamente a cada poucos segundos até que o proxy esteja ativo e o navegador seja redirecionado.
O principal benefício que os modelos de SPA ASP.NET Core oferecem:
- Inicia um proxy se ele ainda não estiver em execução.
- Configuração de HTTPS.
- Configurando algumas solicitações para serem intermediadas por proxy para o back-end ASP.NET servidor Core.
Quando o navegador envia uma solicitação para um ponto de extremidade de back-end, por exemplo /weatherforecast nos modelos. O proxy SPA recebe a solicitação e a envia de volta ao servidor de forma transparente. O servidor responde e o proxy SPA envia a solicitação de volta para o navegador:
Aplicativos de página única publicados
Quando o aplicativo é publicado, o SPA se torna uma coleção de arquivos na wwwroot pasta.
Não há nenhum componente de tempo de execução necessário para servir o aplicativo:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapFallbackToFile("index.html");
app.Run();
No arquivo gerado Program.cs pelo modelo precedente:
-
app.UseStaticFiles permite que os arquivos sejam servidos. -
app.MapFallbackToFile("index.html")Permite servir o documento padrão para qualquer solicitação desconhecida que o servidor receba.
Quando o aplicativo é publicado com dotnet publish, as seguintes tarefas no csproj arquivo garantem que npm restore seja executado e que o script npm apropriado seja executado para gerar os artefatos de produção:
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="$(SpaRoot)build\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>wwwroot\%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>
Desenvolvendo aplicativos de página única
O arquivo de projeto define algumas propriedades que controlam o comportamento do aplicativo durante o desenvolvimento:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
<SpaRoot>ClientApp\</SpaRoot>
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
<SpaProxyServerUrl>https://localhost:44414</SpaProxyServerUrl>
<SpaProxyLaunchCommand>npm start</SpaProxyLaunchCommand>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="7.0.1" />
</ItemGroup>
<ItemGroup>
<!-- Don't publish the SPA source files, but do show them in the project files list -->
<Content Remove="$(SpaRoot)**" />
<None Remove="$(SpaRoot)**" />
<None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
</ItemGroup>
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="$(SpaRoot)build\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>wwwroot\%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>
-
SpaProxyServerUrl: Controla a URL onde o servidor espera que o proxy SPA seja executado. Este é o URL:- O servidor faz pings depois de iniciar o proxy para saber se ele está pronto.
- Onde redireciona o navegador após uma resposta bem-sucedida.
-
SpaProxyLaunchCommand: O comando que o servidor usa para iniciar o proxy SPA quando deteta que o proxy não está em execução.
O pacote Microsoft.AspNetCore.SpaProxy é responsável pela lógica anterior para detetar o proxy e redirecionar o navegador.
O conjunto de inicialização de hospedagem definido em Properties/launchSettings.json é usado para adicionar automaticamente os componentes necessários durante o desenvolvimento para detetar se o proxy está em execução e iniciá-lo caso contrário.
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:51783",
"sslPort": 44329
}
},
"profiles": {
"MyReact": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:7145;http://localhost:5273",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
}
}
}
}
Configuração para o aplicativo cliente
Essa configuração é específica para a estrutura de frontend que o aplicativo está usando, no entanto, muitos aspetos da configuração são semelhantes.
Configuração angular
O arquivo gerado ClientApp/package.json pelo modelo:
{
"name": "myangular",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"prestart": "node aspnetcore-https",
"start": "run-script-os",
"start:windows": "ng serve --port 44483 --ssl --ssl-cert \"%APPDATA%\\ASP.NET\\https\\%npm_package_name%.pem\" --ssl-key \"%APPDATA%\\ASP.NET\\https\\%npm_package_name%.key\"",
"start:default": "ng serve --port 44483 --ssl --ssl-cert \"$HOME/.aspnet/https/${npm_package_name}.pem\" --ssl-key \"$HOME/.aspnet/https/${npm_package_name}.key\"",
"build": "ng build",
"build:ssr": "ng run MyAngular:server:dev",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^14.1.3",
"@angular/common": "^14.1.3",
"@angular/compiler": "^14.1.3",
"@angular/core": "^14.1.3",
"@angular/forms": "^14.1.3",
"@angular/platform-browser": "^14.1.3",
"@angular/platform-browser-dynamic": "^14.1.3",
"@angular/platform-server": "^14.1.3",
"@angular/router": "^14.1.3",
"bootstrap": "^5.2.0",
"jquery": "^3.6.0",
"oidc-client": "^1.11.5",
"popper.js": "^1.16.0",
"run-script-os": "^1.1.6",
"rxjs": "~7.5.6",
"tslib": "^2.4.0",
"zone.js": "~0.11.8"
},
"devDependencies": {
"@angular-devkit/build-angular": "^14.1.3",
"@angular/cli": "^14.1.3",
"@angular/compiler-cli": "^14.1.3",
"@types/jasmine": "~4.3.0",
"@types/jasminewd2": "~2.0.10",
"@types/node": "^18.7.11",
"jasmine-core": "~4.3.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.1.1",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "^2.0.0",
"typescript": "~4.7.4"
},
"overrides": {
"autoprefixer": "10.4.5"
},
"optionalDependencies": {}
}
Contém scripts que iniciam o servidor de desenvolvimento angular:
O
prestartscript invocaClientApp/aspnetcore-https.js, que é responsável por garantir que o certificado HTTPS do servidor de desenvolvimento esteja disponível para o servidor proxy SPA.O
start:windowsestart:default:- Inicie o servidor de desenvolvimento Angular via
ng serve. - Forneça a porta, as opções para usar HTTPS e o caminho para o certificado e a chave associada. O número da porta fornecido corresponde ao número da porta especificado no ficheiro
.csproj.
- Inicie o servidor de desenvolvimento Angular via
O ficheiro gerado pelo modelo ClientApp/angular.json contém:
O
servecomando.Um
proxyconfigelemento nadevelopmentconfiguração para indicar queproxy.conf.jsdeve ser usado para configurar o proxy frontend, conforme mostrado no seguinte JSON destacado:{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "MyAngular": { "projectType": "application", "schematics": { "@schematics/angular:application": { "strict": true } }, "root": "", "sourceRoot": "src", "prefix": "app", "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "progress": false, "outputPath": "dist", "index": "src/index.html", "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", "allowedCommonJsDependencies": [ "oidc-client" ], "assets": [ "src/assets" ], "styles": [ "node_modules/bootstrap/dist/css/bootstrap.min.css", "src/styles.css" ], "scripts": [] }, "configurations": { "production": { "budgets": [ { "type": "initial", "maximumWarning": "500kb", "maximumError": "1mb" }, { "type": "anyComponentStyle", "maximumWarning": "2kb", "maximumError": "4kb" } ], "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ], "outputHashing": "all" }, "development": { "buildOptimizer": false, "optimization": false, "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true } }, "defaultConfiguration": "production" }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", "configurations": { "production": { "browserTarget": "MyAngular:build:production" }, "development": { "browserTarget": "MyAngular:build:development", "proxyConfig": "proxy.conf.js" } }, "defaultConfiguration": "development" }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { "browserTarget": "MyAngular:build" } }, "test": { "builder": "@angular-devkit/build-angular:karma", "options": { "main": "src/test.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.spec.json", "karmaConfig": "karma.conf.js", "assets": [ "src/assets" ], "styles": [ "src/styles.css" ], "scripts": [] } }, "server": { "builder": "@angular-devkit/build-angular:server", "options": { "outputPath": "dist-server", "main": "src/main.ts", "tsConfig": "tsconfig.server.json" }, "configurations": { "dev": { "optimization": true, "outputHashing": "all", "sourceMap": false, "namedChunks": false, "extractLicenses": true, "vendorChunk": true }, "production": { "optimization": true, "outputHashing": "all", "sourceMap": false, "namedChunks": false, "extractLicenses": true, "vendorChunk": false } } } } } }, "defaultProject": "MyAngular" }
ClientApp/proxy.conf.js Define as rotas que precisam ser intermediadas por proxy de volta para o back-end do servidor. O conjunto geral de opções é definido em http-proxy-middleware para react e angular, uma vez que ambos usam o mesmo proxy.
O código destacado a seguir usa a lógica com base nas variáveis de ClientApp/proxy.conf.js ambiente definidas durante o desenvolvimento para determinar a porta em que o back-end está sendo executado:
const { env } = require('process');
const target = env.ASPNETCORE_HTTPS_PORTS ? `https://localhost:${env.ASPNETCORE_HTTPS_PORTS}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:51951';
const PROXY_CONFIG = [
{
context: [
"/weatherforecast",
],
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
}
}
]
module.exports = PROXY_CONFIG;
Configuração do React
A
package.jsonseção de scripts contém os seguintes scripts que iniciam o aplicativo react durante o desenvolvimento, conforme mostrado no código destacado a seguir:{ "name": "myreact", "version": "0.1.0", "private": true, "dependencies": { "bootstrap": "^5.2.0", "http-proxy-middleware": "^2.0.6", "jquery": "^3.6.0", "merge": "^2.1.1", "oidc-client": "^1.11.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-bootstrap": "^0.26.2", "react-router-dom": "^6.3.0", "react-scripts": "^5.0.1", "reactstrap": "^9.1.3", "rimraf": "^3.0.2", "web-vitals": "^2.1.4", "workbox-background-sync": "^6.5.4", "workbox-broadcast-update": "^6.5.4", "workbox-cacheable-response": "^6.5.4", "workbox-core": "^6.5.4", "workbox-expiration": "^6.5.4", "workbox-google-analytics": "^6.5.4", "workbox-navigation-preload": "^6.5.4", "workbox-precaching": "^6.5.4", "workbox-range-requests": "^6.5.4", "workbox-routing": "^6.5.4", "workbox-strategies": "^6.5.4", "workbox-streams": "^6.5.4" }, "devDependencies": { "ajv": "^8.11.0", "cross-env": "^7.0.3", "eslint": "^8.22.0", "eslint-config-react-app": "^7.0.1", "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jsx-a11y": "^6.6.1", "eslint-plugin-react": "^7.30.1", "nan": "^2.16.0", "typescript": "^4.7.4" }, "overrides": { "autoprefixer": "10.4.5" }, "resolutions": { "css-what": "^5.0.1", "nth-check": "^3.0.1" }, "scripts": { "prestart": "node aspnetcore-https && node aspnetcore-react", "start": "rimraf ./build && react-scripts start", "build": "react-scripts build", "test": "cross-env CI=true react-scripts test --env=jsdom", "eject": "react-scripts eject", "lint": "eslint ./src/" }, "eslintConfig": { "extends": [ "react-app" ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } }O
prestartscript invoca:-
aspnetcore-https.js, que é responsável por garantir que o certificado HTTPS do servidor de desenvolvimento esteja disponível para o servidor proxy SPA. - Invoca
aspnetcore-react.jspara configurar o arquivo apropriado.env.development.localpara usar o certificado de desenvolvimento local HTTPS.aspnetcore-react.jsconfigura o certificado de desenvolvimento local HTTPS adicionandoSSL_CRT_FILE=<certificate-path>eSSL_KEY_FILE=<key-path>ao arquivo.
-
O
.env.developmentarquivo define a porta para o servidor de desenvolvimento e especifica HTTPS.
O src/setupProxy.js configura o proxy SPA para encaminhar as solicitações para o back-end. O conjunto geral de opções é definido em http-proxy-middleware.
O código destacado a seguir usa ClientApp/src/setupProxy.js a lógica com base nas variáveis de ambiente definidas durante o desenvolvimento para determinar a porta em que o back-end está sendo executado:
const { createProxyMiddleware } = require('http-proxy-middleware');
const { env } = require('process');
const target = env.ASPNETCORE_HTTPS_PORTS ? `https://localhost:${env.ASPNETCORE_HTTPS_PORTS}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:51783';
const context = [
"/weatherforecast",
];
const onError = (err, req, resp, target) => {
console.error(`${err.message}`);
}
module.exports = function (app) {
const appProxy = createProxyMiddleware(context, {
target: target,
// Handle errors to prevent the proxy middleware from crashing when
// the ASP NET Core webserver is unavailable
onError: onError,
secure: false,
// Uncomment this line to add support for proxying websockets
//ws: true,
headers: {
Connection: 'Keep-Alive'
}
});
app.use(appProxy);
};
Versão da estrutura SPA suportada em modelos de SPA ASP.NET Core
Os modelos de projeto SPA fornecidos com cada versão ASP.NET Core fazem referência à versão mais recente da estrutura SPA apropriada.
As estruturas SPA normalmente têm um ciclo de lançamento mais curto do que o .NET. Devido aos dois ciclos de lançamento diferentes, a versão suportada da estrutura SPA e do .NET pode ficar fora de sincronia: a versão principal da estrutura SPA, da qual uma versão principal do .NET depende, pode ficar sem suporte, enquanto a versão .NET com a qual a estrutura SPA foi fornecida ainda é suportada.
Os modelos de SPA ASP.NET Core podem ser atualizados em uma versão de patch para uma nova versão da estrutura SPA para manter os modelos em um estado suportado e seguro.