Обзор одностраничных приложений (SPAs) в ASP.NET Core
Примечание.
Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 9 этой статьи.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в статье о политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 8 этой статьи.
Внимание
Эта информация относится к предварительному выпуску продукта, который может быть существенно изменен до его коммерческого выпуска. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
В текущем выпуске см . версию .NET 9 этой статьи.
Visual Studio предоставляет шаблоны проектов для создания одностраничных приложений (SPAs) на основе платформ JavaScript, таких как Angular, React и Vue с серверной частью ASP.NET Core. Эти шаблоны:
- Создайте решение Visual Studio с интерфейсным проектом и серверным проектом.
- Используйте тип проекта Visual Studio для JavaScript и TypeScript (esproj) для внешнего интерфейса.
- Используйте проект ASP.NET Core для серверной части.
Проекты, созданные с помощью шаблонов Visual Studio, можно запускать из командной строки в Windows, Linux и macOS. Чтобы запустить приложение, используйте dotnet run --launch-profile https
для запуска проекта сервера. Запуск проекта сервера автоматически запускает внешний сервер разработки JavaScript. Сейчас https
требуется профиль запуска.
Учебники по Visual Studio
Чтобы приступить к работе, следуйте одному из учебников в документации по Visual Studio:
- Создание приложения ASP.NET Core с помощью Angular
- Создание приложения ASP.NET Core с помощью React
- Создание приложения ASP.NET Core с помощью Vue
Дополнительные сведения см. в статье JavaScript и TypeScript в Visual Studio
ASP.NET основные шаблоны SPA
Visual Studio включает шаблоны для создания приложений ASP.NET Core с интерфейсом JavaScript или TypeScript. Эти шаблоны доступны в Visual Studio 2022 версии 17.8 или более поздней версии с установленной рабочей нагрузкой ASP.NET и веб-разработки .
Шаблоны Visual Studio для создания приложений ASP.NET Core с интерфейсом JavaScript или TypeScript предоставляют следующие преимущества:
- Очистка разделения проектов для внешнего интерфейса и серверной части.
- Будьте в курсе последних версий интерфейсной платформы.
- Интеграция с новейшими средствами командной строки интерфейсной платформы, такими как Vite.
- Шаблоны для JavaScript и TypeScript (только TypeScript для Angular).
- Расширенные возможности редактирования кода JavaScript и TypeScript.
- Интеграция средств сборки JavaScript с сборкой .NET.
- Пользовательский интерфейс управления зависимостями npm.
- Совместим с отладкой и запуском Visual Studio Code.
- Запустите интерфейсные модульные тесты в обозревателе тестов с помощью платформ тестирования JavaScript.
Устаревшие шаблоны spa ASP.NET Core
В более ранних версиях пакета SDK для .NET добавлены устаревшие шаблоны для создания приложений SPA с ASP.NET Core. Документация по этим старым шаблонам см. в статье ASP.NET Core 7.0 обзора SPA и статьи Angular и React .
Архитектура шаблонов одностраничных приложений
Шаблоны одностраничных приложений (SPA) для Angular и React позволяют разрабатывать приложения Angular и React , размещенные на сервере серверной части .NET.
Во время публикации файлы приложения Angular и React копируются wwwroot
в папку и обслуживаются через ПО промежуточного слоя статических файлов.
Вместо возврата HTTP 404 (Не найдено), резервный маршрут обрабатывает неизвестные запросы к серверной части и служит index.html
для SPA.
Во время разработки приложение настроено на использование внешнего прокси-сервера. React и Angular используют тот же внешний прокси-сервер.
При запуске приложения страница index.html
открывается в браузере. Специальное ПО промежуточного слоя, которое включено только в разработке:
- Перехватывает входящие запросы.
- Проверяет, запущен ли прокси-сервер.
- Перенаправляет URL-адрес прокси-сервера, если он запущен или запускает новый экземпляр прокси-сервера.
- Возвращает страницу в браузер, который автоматически обновляется каждые несколько секунд до тех пор, пока прокси-сервер не будет перенаправляться.
Основное преимущество шаблонов SPA для ASP.NET Core предоставляют следующие преимущества:
- Запускает прокси-сервер, если он еще не запущен.
- Настройка HTTPS.
- Настройка некоторых запросов для прокси-сервера серверной ASP.NET Core.
Когда браузер отправляет запрос на серверную конечную точку, например /weatherforecast
в шаблонах. Прокси-сервер SPA получает запрос и отправляет его обратно на сервер прозрачно. Сервер отвечает, а прокси-сервер SPA отправляет запрос обратно в браузер:
Опубликованные одностраничные приложения
Когда приложение опубликовано, SPA становится коллекцией файлов в папке wwwroot
.
Для обслуживания приложения не требуется компонент среды выполнения:
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();
В созданном выше шаблоне Program.cs
файле:
app.
UseStaticFiles позволяет обслуживать файлы.app.
MapFallbackToFile("index.html")
включает обслуживание документа по умолчанию для любого неизвестного запроса, который получает сервер.
Когда приложение публикуется с помощью dotnet publish, следующие задачи в csproj
файле гарантируют выполнение npm restore
и выполнение соответствующего скрипта npm для создания рабочих артефактов:
<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>
Разработка одностраничных приложений
Файл проекта определяет несколько свойств, которые управляют поведением приложения во время разработки:
<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
: управляет URL-адресом, в котором сервер ожидает запуска прокси-сервера SPA. Это URL-адрес:- Сервер проверяет связь после запуска прокси-сервера, чтобы узнать, готов ли он.
- Где он перенаправляет браузер после успешного ответа.
SpaProxyLaunchCommand
: команда, в которой сервер используется для запуска прокси-сервера SPA, когда он обнаруживает, что прокси-сервер не запущен.
Пакет Microsoft.AspNetCore.SpaProxy
отвечает за предыдущую логику для обнаружения прокси-сервера и перенаправления браузера.
Сборка запуска размещения, определенная в Properties/launchSettings.json
, используется для автоматического добавления необходимых компонентов во время разработки, чтобы определить, запущен ли прокси-сервер и запустить его в противном случае:
{
"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"
}
}
}
}
Настройка клиентского приложения
Эта настройка связана с интерфейсной платформой, используемой приложением, однако многие аспекты конфигурации похожи.
Настройка Angular
Созданный ClientApp/package.json
шаблон файла:
{
"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": {}
}
Содержит скрипты, запускающие сервер разработки angular:
Скрипт
prestart
вызываетсяClientApp/aspnetcore-https.js
, который отвечает за обеспечение доступности HTTPS-сертификата сервера разработки для прокси-сервера SPA.И
start:windows
start:default
:- Запустите сервер разработки Angular с помощью
ng serve
. - Укажите порт, параметры для использования HTTPS и путь к сертификату и связанному ключу. Номер порта соответствует номеру порта, указанному
.csproj
в файле.
- Запустите сервер разработки Angular с помощью
Созданный шаблон ClientApp/angular.json
содержит:
Команда
serve
.Элемент
proxyconfig
вdevelopment
конфигурации, указывающий, чтоproxy.conf.js
следует использовать для настройки внешнего прокси-сервера, как показано в следующем выделенном формате JSON:{ "$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
определяет маршруты, которые должны быть проксированы обратно на серверную часть сервера. Общий набор параметров определяется в по промежуточном слоях http-proxy для react и angular, так как они оба используют один и тот же прокси-сервер.
Следующий выделенный код использует ClientApp/proxy.conf.js
логику на основе переменных среды, заданных во время разработки, чтобы определить порт, на котором выполняется серверная часть:
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;
Настройка React
Раздел
package.json
"Скрипты" содержит следующие скрипты, запускающие приложение React во время разработки, как показано в следующем выделенном коде:{ "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" ] } }
Скрипт
prestart
вызывает:aspnetcore-https.js
, который отвечает за обеспечение доступности сертификата HTTPS сервера разработки для прокси-сервера SPA.aspnetcore-react.js
Вызывается для настройки соответствующего.env.development.local
файла для использования локального сертификата разработки HTTPS.aspnetcore-react.js
настраивает сертификат локальной разработки HTTPS, добавивSSL_CRT_FILE=<certificate-path>
иSSL_KEY_FILE=<key-path>
в файл.
Файл
.env.development
определяет порт для сервера разработки и задает ПРОТОКОЛ HTTPS.
Прокси-сервер src/setupProxy.js
SPA настраивает перенаправление запросов на серверную часть. Общий набор параметров определяется в ПО промежуточного слоя http-proxy-.
Следующий выделенный код использует ClientApp/src/setupProxy.js
логику на основе переменных среды, заданных во время разработки, чтобы определить порт, на котором выполняется серверная часть:
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);
};
Поддерживаемая версия платформы SPA в шаблонах SPA ASP.NET Core
Шаблоны проектов SPA, которые поставляются с каждым выпуском ASP.NET Core, ссылаются на последнюю версию соответствующей платформы SPA.
Платформы SPA обычно имеют более короткий цикл выпуска, чем .NET. Из-за двух разных циклов выпуска поддерживаемая версия платформы SPA и .NET может выйти из синхронизации: основная версия платформы SPA, от которой зависит основной выпуск .NET, может выйти из поддержки, в то время как версия .NET, отправленная на платформу SPA, по-прежнему поддерживается.
Шаблоны spa ASP.NET Core можно обновить в выпуске исправлений до новой версии spa framework, чтобы шаблоны сохранялись в поддерживаемом и безопасном состоянии.
Дополнительные ресурсы
ASP.NET Core