主插座
- 在 .NET 6 中,SPA 前端和后端 .NET API 之间的通信已更改。
- 从.NET 6开始,模板使用前端代理方案将请求发送到后端,后端更加独立。
- 代理允许开发服务器为前端和后端提供可读和可调试的代码。
- 将 Microsoft Yarp 的反向代理解决方案与 SpaYarp 包一起使用仍然是一个可行的替代方案。
- .NET 6 更改也适用于以下版本。
ASP.NET 包含多个模板,可帮助您开始在 .NET 生态系统中进行开发。 模板提供服务器端渲染解决方案(Razor Pages、MVC、Blazor Server)和客户端渲染解决方案(Blazor WebAssembly),以及单页应用程序(Angular 和 React)。
本文将描述后者,以及 SPA模板,以及针对从 .NET 5 到 .NET 6、.NET 7 和更高版本的开发环境所做的代理更改。
.NET 5 支持于 2022 年 5 月 10 日停止。2021 年 5 月 25 日,.NET 6 预览版 4 中引入了一个分水岭更改,它改变了代理在 ASP.NET SPA 模板中的使用方式。用户对没有明确的方法感到沮丧升级到新的代理策略。
由于此更改主要影响小型项目和爱好者,因此唯一的答案分散在论坛中(很少有示例 1个和 2个和 3个) 有很好的提示和建议,但没有明确的证据。 直到 2022 年 11 月 .NET 7 可用后,才出现了清晰的迁移指南。
新模板改变了 SPA 前端和后端 .NET API 之间的通信方式。 较旧的模板(从 .NET Core 到 .NET 5)使用专门的中间件启动前端框架的开发服务器,然后将请求从 .NET 服务器传递到 Node.js 前端。
ASP.NET SPA 模板在开发和发布版本中的编译和运行方式不同。
在最终发布的版本中,Kestrel 服务于请求,出于开发目的,同时使用了 Node.js 和 Kestrel,因此可以更轻松地调试前后端代码。 更改只会影响开发环境。
[Click on the image to view full-size]
这种方法意味着操作码必须特定于每个前端框架,这导致微软团队想要支持的每个前端框架的代码都难以维护。
[Click on the image to view full-size]
从 .NET 6 开始,Angular 和 React 的新模板切换了前端和后端的通信方式。 他们使用前端代理解决方案将请求发送到后端。 常见的前端框架已经内置了对开发服务器代理的支持,但每个框架也必须针对所使用的框架进行单独配置。 ASP.NET 应用程序仍在运行前端开发服务器,但请求来自该服务器。
这种新方法的优点包括:
- 后端文件中的配置更简单
- 后端比前端框架更独立,但不是完全独立的,因为运行命令和URL仍然是定义的
- 后端 Startup.cs 或 Program.cs 文件中不再有框架代码
- 可扩展到模板中未包含的其他前框架
前端开发服务器启动逻辑是在开发过程中使用Microsoft.AspNetCore.SpaProxy包,设置前端URL和运行命令实现的。
<SpaProxyServerUrl>https://localhost:44435</SpaProxyServerUrl>
<SpaProxyLaunchCommand>npm start</SpaProxyLaunchCommand>
对于前侧,每个轮胎都有自己的应用。 比如 React 使用第三方包 http-proxy-middleware
就像下面这样
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:5000',
changeOrigin: true,
})
);
};
Angular 相反有一个更全面的配置解决方案。 他们使用一个名为 proxy.conf.json
,开发人员可以在其中选择代理,然后将其添加到选项中 angular.json
.
proxy.conf.json
{
"/api": {
"target": "http://localhost:3000",
"secure": false
}
}
angular.json
…
"architect": {
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "your-application-name:build",
"proxyConfig": "src/proxy.conf.json"
},
…
这个模板的一大特点是应用程序是一个单一的项目:最终发布的版本有一个单一的入口点,前端代码作为来自 ASP.NET 进程的静态文件。 这使得部署更简单,因为它只是一个需要部署和维护的服务。 它非常适合小型解决方案、副项目,或者只是快速尝试一些东西。
表格附带:
- 已创建带有客户端导航的页面
- 开发服务器集成
- 建设高效生产
- 计数器和数据获取的例子
根据团队的规模,它还可以处理更大的项目,因为在 ClientApp 文件夹中,它可以像单独的前端解决方案一样工作。 但是从长远来看,创建一个完全独立的前端会更干净。 创建定义明确的项目有助于维护代码。 这也有助于专业开发人员(例如 React 或 .NET)只使用他们熟悉的项目。
在开发期间使用代理对开发人员有多种好处:
- 将请求重定向到特定路由,例如
awesomeweb.com/about
到localhost:3210/about
- 使用相对路径
- 改写曲目
- 解决因证书可能出现的 HTTP/HTTPS 问题
- 解决 CORS(跨源资源共享)错误,无需为本地 devServers 添加显式允许规则
- 前后端代码均可读取和调试
这些好处可以大大加快开发过程,使开发人员使用 asp.net 的体验更加愉快。
假设您不喜欢使用前端代理到后端的方法,因为在开发和生产中提供页面时存在差异,或者您不喜欢前端代理设置。 在这种情况下,可以使用 Microsoft Yarp 的反向代理解决方案作为替代方案。 该包被称为 温泉,并且该方法是在不反转代理连接的情况下为后端使用新模板配置的组合。
如果您想将当前解决方案升级到 .NET 6 (LTS) 或更高版本,以下步骤将总结需要在 .NET 和 Angular 端更新的文件(使用解释 a
) 或 React(注释为 r
) 边。
移民
要迁移 ASP.NET Angular 或 React 模板,您必须更新以下文件:
后端:
- 项目文件
LaunchSettings.json
Startup.cs
或者Program.cs
(取决于设置)
介绍结束:
● package.json
● angular.json
(仅限角度)
● 您必须添加文件来设置代理和 HTTPS
后端
1. 在项目的 Nuget Packages 部分,删除对 Microsoft.AspNetCore.SpaServices.Extensions
并添加名为 Microsoft.AspNetCore.SpaProxy
.
2. 在里面 csproj
项目文件,在第一个添加 PropertyGroup
<SpaProxyServerUrl>https://localhost:<<Insert Frontend Port>></SpaProxyServerUrl>
<SpaProxyLaunchCommand>npm start</SpaProxyLaunchCommand>
最后,有一个有名字的目标 PublishRunWebpack
. 在该组中,更新一个文件 ResolvedFileToPublish->RelativePath
到
<RelativePath>wwwroot\%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
3. 在里面 launchSettings.json
更新每个专有配置文件:
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
它应该类似于以下几行:
"profiles": {
"TestProject": {
...
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
}
...
},
"IIS Express": {
...
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
}
...
}
}
4. 在里面 Startup.cs
或者 Program.cs
您需要删除:
● AddSpaStaticFiles()
:
services.AddSpaStaticFiles(configuration =>
{
... //All the configs/code inside
});
● UseSpaStaticFiles()
:
app.UseSpaStaticFiles();
● UseSpa
:
app.UseSpa(spa =>
{
... //All the configs/code inside
});
5. 您必须在“index.html
在端点部分,例如:
app.UseEndpoints(endpoints => {
...
endpoints.MapFallbackToFile("index.html");
...
});
介绍结束
即使模板前端、Angular 和 React 有不同的实现,它们都使用相同的 HTTPS 设置文件。
1. 添加 aspnetcore-https.js
旁边 package.json
文件
// This script sets up HTTPS for the application using the ASP.NET Core HTTPS certificate
const fs = require('fs');
const spawn = require('child_process').spawn;
const path = require('path');
const baseFolder =
process.env.APPDATA !== undefined && process.env.APPDATA !== ''
? `${process.env.APPDATA}/ASP.NET/https`
: `${process.env.HOME}/.aspnet/https`;
const certificateArg = process.argv.map(arg => arg.match(/--name=(?<value>.+)/i)).filter(Boolean)[0];
const certificateName = certificateArg ? certificateArg.groups.value : process.env.npm_package_name;
if (!certificateName) {
console.error('Invalid certificate name. Run this script in the context of an npm/yarn script or pass --name=<<app>> explicitly.')
process.exit(-1);
}
const certFilePath = path.join(baseFolder, `${certificateName}.pem`);
const keyFilePath = path.join(baseFolder, `${certificateName}.key`);
if (!fs.existsSync(certFilePath) || !fs.existsSync(keyFilePath)) {
spawn('dotnet', [
'dev-certs',
'https',
'--export-path',
certFilePath,
'--format',
'Pem',
'--no-password',
], { stdio: 'inherit', })
.on('exit', (code) => process.exit(code));
}
对于角度成型:
2a. 更新 package.json
"start": "run-script-os",
"start:windows": "ng serve --port <<Insert Frontend Port>> --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 <<Insert Frontend Port>> --ssl --ssl-cert $HOME/.aspnet/https/${npm_package_name}.pem --ssl-key $HOME/.aspnet/https/${npm_package_name}.key",
注意:使用与后端文件相同的端口。
3a. 安装 run-script-os
用命令
npm install --save-dev run-script-os
这个包增加了在 npm 脚本中使用操作系统特定进程的能力,正如你在上面看到的那样 :windows
适应症。
4A. 在里面 angular.json
开发文件,添加文件 proxyConfig
该属性指向新代理的配置文件:
"serve": {
...
"configurations": {
...
"development": {
...
"proxyConfig": "proxy.conf.js"
}
},
...
},
5a. 添加 proxy.conf.js
文件内容:
const { env } = require('process');
const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:[IIS-HTTP-PORT]';
const PROXY_CONFIG = [
{
context: [
"/weatherforecast",
],
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
}
}
]
module.exports = PROXY_CONFIG;
对于交互模型:
2r。 更新文件 package.json
"prestart": "node aspnetcore-https && node aspnetcore-react",
"start": "rimraf ./build && react-scripts start",
3r。 添加 .env.development
环境文件:
PORT=<<Insert Frontend Port>>
HTTPS=true
注意:使用与后端文件相同的端口。
4 页 添加文件 aspnetcore-react.js
// This script configures the .env.development.local file with additional environment variables to configure HTTPS using the ASP.NET Core
// development certificate in the webpack development proxy.
const fs = require('fs');
const path = require('path');
const baseFolder =
process.env.APPDATA !== undefined && process.env.APPDATA !== ''
? `${process.env.APPDATA}/ASP.NET/https`
: `${process.env.HOME}/.aspnet/https`;
const certificateArg = process.argv.map(arg => arg.match(/--name=(?<value>.+)/i)).filter(Boolean)[0];
const certificateName = certificateArg ? certificateArg.groups.value : process.env.npm_package_name;
if (!certificateName) {
console.error('Invalid certificate name. Run this script in the context of an npm/yarn script or pass --name=<<app>> explicitly.')
process.exit(-1);
}
const certFilePath = path.join(baseFolder, `${certificateName}.pem`);
const keyFilePath = path.join(baseFolder, `${certificateName}.key`);
if (!fs.existsSync('.env.development.local')) {
fs.writeFileSync(
'.env.development.local',
`SSL_CRT_FILE=${certFilePath}
SSL_KEY_FILE=${keyFilePath}`
);
} else {
let lines = fs.readFileSync('.env.development.local')
.toString()
.split('\n');
let hasCert, hasCertKey = false;
for (const line of lines) {
if (/SSL_CRT_FILE=.*/i.test(line)) {
hasCert = true;
}
if (/SSL_KEY_FILE=.*/i.test(line)) {
hasCertKey = true;
}
}
if (!hasCert) {
fs.appendFileSync(
'.env.development.local',
`\nSSL_CRT_FILE=${certFilePath}`
);
}
if (!hasCertKey) {
fs.appendFileSync(
'.env.development.local',
`\nSSL_KEY_FILE=${keyFilePath}`
);
}
}
5r。 最后,在 ClientApp\src
新增文件夹 setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
const { env } = require('process');
const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:[IIS-HTTP-PORT]';
const context = [
"/weatherforecast",
];
module.exports = function(app) {
const appProxy = createProxyMiddleware(context, {
target: target,
secure: false,
headers: {
Connection: 'Keep-Alive'
}
});
app.use(appProxy);
};
完成所有更改后,构建并运行应用程序。 Angular 和 React 版本都必须遵循新的代理策略并支持 HTTPS。
结论
这种在模板中使用代理的新方法似乎更可靠,并且可能会继续用于未来版本的 ASP.NET SPA 模板。
轻松获取SpaProxyLaunchCommand中指定的前端启动命令和前端框架关联的URL SpaProxyServerUrl
比 Microsoft 提供的界面框架更容易使用其他界面框架。
React 和 Angular 连接示例展示了在调试模式下利用前端代理向后端发送请求的可能场景。 使用此解决方案,您可以在同一台机器/实例上部署前端 SPA 应用程序,因为它只是一个进程,从而提供了一种快速简便的方法来开始使用 SPA 作为前端的应用程序开发。
More Stories
《东京恶习》制片人详述日本走向全球制作中心之路
康拉德·科尔曼仅使用可再生能源再次改变了世界
新款 MacBook Pro 为苹果一周的重大新闻画上了句号