Nguon: Microsoft Learn · .NET 8.0

Tổng quan về Single Page Apps (SPA) trong ASP.NET Core

Nguồn: Overview of Single Page Apps (SPAs) - ASP.NET Core

Visual Studio cung cấp các project template (mẫu dự án) để tạo các ứng dụng trang đơn (SPA - Single Page App) dựa trên các công nghệ JavaScript, chẳng hạn như Angular, React, và Vue, có backend ASP.NET Core. Các template này:

Các dự án được tạo bằng Visual Studio template có thể chạy từ dòng lệnh trên Windows, Linux và macOS. Để chạy ứng dụng, hãy dùng dotnet run --launch-profile https để chạy server project. Việc chạy server project sẽ tự động khởi động frontend JavaScript development server. Launch profile https hiện là bắt buộc.

Hướng dẫn của Visual Studio

Để bắt đầu, hãy làm theo một trong các hướng dẫn trong tài liệu Visual Studio:

Để biết thêm thông tin, xem JavaScript and TypeScript in Visual Studio.

ASP.NET Core SPA templates

Visual Studio bao gồm các template để xây dựng ứng dụng ASP.NET Core với frontend JavaScript hoặc TypeScript. Các template này có sẵn trong Visual Studio 2022 phiên bản 17.8 trở lên với workload ASP.NET and web development được cài đặt.

Các Visual Studio template để xây dựng ứng dụng ASP.NET Core với frontend JavaScript hoặc TypeScript mang lại những lợi ích sau:

Legacy ASP.NET Core SPA templates (Template cũ)

Các phiên bản trước của .NET SDK bao gồm những gì bây giờ là các template cũ để xây dựng ứng dụng SPA với ASP.NET Core. Để xem tài liệu về các template cũ hơn này, hãy xem phiên bản .NET 7 của SPA overview và các bài viết AngularReact.


Kiến trúc của Single Page Application templates (Phiên bản .NET 6/7)

Các SPA template cho AngularReact cung cấp khả năng phát triển ứng dụng Angular và React được lưu trữ bên trong máy chủ backend .NET.

Khi xuất bản (publish), các tệp của ứng dụng Angular và React được sao chép vào thư mục wwwroot và được phục vụ thông qua Static File Middleware (phần mềm trung gian tệp tĩnh).

Thay vì trả về HTTP 404 (Not Found), một fallback route (tuyến dự phòng) xử lý các yêu cầu không xác định đến backend và phục vụ index.html cho SPA.

Trong quá trình phát triển, ứng dụng được cấu hình để sử dụng frontend proxy. React và Angular sử dụng cùng một frontend proxy.

Khi ứng dụng khởi chạy, trang index.html được mở trong trình duyệt. Một middleware đặc biệt chỉ được bật trong môi trường phát triển:

Lợi ích chính mà các ASP.NET Core SPA template cung cấp:

Khi trình duyệt gửi yêu cầu đến một backend endpoint, ví dụ /weatherforecast trong các template. SPA proxy nhận yêu cầu và gửi trở lại máy chủ một cách trong suốt. Máy chủ phản hồi và SPA proxy gửi yêu cầu trở lại trình duyệt.

Single Page Apps đã xuất bản (Published)

Khi ứng dụng được xuất bản, SPA trở thành một tập hợp các tệp trong thư mục wwwroot.

Không cần thành phần runtime (thời gian chạy) nào để phục vụ ứng dụng:

csharp
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();

Trong tệp Program.cs được tạo từ template ở trên:

Khi ứng dụng được xuất bản với dotnet publish, các tác vụ sau trong tệp csproj đảm bảo rằng npm restore chạy và script npm phù hợp chạy để tạo ra các artifacts production:

xml
  <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>

Phát triển Single Page Apps

Tệp project xác định một số thuộc tính kiểm soát hành vi của ứng dụng trong quá trình phát triển:

xml
<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>

  ...
</Project>

Package Microsoft.AspNetCore.SpaProxy chịu trách nhiệm cho logic trên để phát hiện proxy và chuyển hướng trình duyệt.

Hosting startup assembly được định nghĩa trong Properties/launchSettings.json được sử dụng để tự động thêm các thành phần cần thiết trong quá trình phát triển để phát hiện xem proxy có đang chạy không và khởi chạy nó nếu không:

json
{
  "profiles": {
    "MyReact": {
      "commandName": "Project",
      "launchBrowser": true,
      "applicationUrl": "https://localhost:7145;http://localhost:5273",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
      }
    }
  }
}

Thiết lập cho ứng dụng client

Thiết lập này dành riêng cho framework frontend mà ứng dụng sử dụng, tuy nhiên nhiều khía cạnh của cấu hình là tương tự nhau.

Thiết lập Angular

Tệp ClientApp/package.json được tạo từ template chứa các script để khởi chạy Angular development server trong quá trình phát triển. Script prestart gọi ClientApp/aspnetcore-https.js để đảm bảo certificate HTTPS của development server có sẵn cho SPA proxy server. Các script start:windowsstart:default khởi chạy Angular development server qua ng serve với port, HTTPS và đường dẫn certificate phù hợp.

Tệp ClientApp/angular.json chứa lệnh serve và phần tử proxyConfig trong cấu hình development để chỉ định rằng proxy.conf.js sẽ được sử dụng để cấu hình frontend proxy.

ClientApp/proxy.conf.js xác định các tuyến cần được proxy đến backend server. Các tùy chọn chung được xác định tại http-proxy-middleware cho React và Angular vì chúng đều sử dụng cùng một proxy.

javascript
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;

Thiết lập React

Script prestart trong package.json gọi aspnetcore-https.jsaspnetcore-react.js. Script aspnetcore-react.js cấu hình certificate HTTPS local development bằng cách thêm SSL_CRT_FILE=<certificate-path>SSL_KEY_FILE=<key-path> vào tệp .env.development.local.

Tệp src/setupProxy.js cấu hình SPA proxy để chuyển tiếp các yêu cầu đến backend:

javascript
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",
];

module.exports = function (app) {
  const appProxy = createProxyMiddleware(context, {
    target: target,
    onError: (err, req, resp, target) => { console.error(`${err.message}`); },
    secure: false,
    headers: {
      Connection: 'Keep-Alive'
    }
  });

  app.use(appProxy);
};

Phiên bản SPA framework được hỗ trợ trong ASP.NET Core SPA templates

Các SPA project template đi kèm với mỗi bản phát hành ASP.NET Core tham chiếu đến phiên bản mới nhất của framework SPA tương ứng.

Các framework SPA thường có chu kỳ phát hành ngắn hơn .NET. Do hai chu kỳ phát hành khác nhau, phiên bản được hỗ trợ của framework SPA và .NET có thể bị lệch nhau: phiên bản SPA framework chính mà .NET phụ thuộc vào có thể hết hỗ trợ, trong khi phiên bản .NET mà framework SPA được xuất bản cùng vẫn còn được hỗ trợ.

Các ASP.NET Core SPA template có thể được cập nhật trong bản phát hành patch lên phiên bản framework SPA mới để giữ cho các template ở trạng thái được hỗ trợ và an toàn.