为什么如果UseSpaStaticFiles()应该为Angular页面提供服务,那么仍然需要UseSpa()



我在这里阅读了一些文章/博客文章和问题,但我仍然对ASP.NET Core 2.1中UseSpaStaticFiles()UseSpa()中间件的使用感到困惑。(参考文献:ASP.NET Core 2.1中的UseStaticFiles、UseSpaStaticFiles和UseSpa之间有什么区别?https://blog.steadycoding.com/usedefaultfiles-usestaticfiles-usespastaticfiles-usespa/)

我使用的是带有ASP.NET Core 2.1的Angular项目模板,它附带了一组预定义的中间件,这些中间件添加到Startup.cs文件中的管道中:

public void ConfigureServices(IServiceCollection services)
{
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});      
}
public void Configure(IApplicationBuilder app)
{   
app.UseStaticFiles();
app.UseSpaStaticFiles();

app.UseSpa(spa =>
{
if (env.IsDevelopment())
{
spa.UseProxyToSpaDevelopmentServer("http://localhost:4000");
}
});

}

我的困惑如下:Angular项目是外部编译的,编译的js文件位于ClientApp/dist文件夹中,环境也设置为生产环境,所以我假设UseSpaStaticFiles()将为Angular页面提供服务,因为ClientApp/dist文件夹具有适当的内容。此外,UseSpa()中间件在这种情况下是无用的,所以我可以删除它。

然而,如果UseSpa()被移除,则Angular页面根本不被加载,尽管UseSpaStaticFiles()应当基于ClientApp/dist的内容来服务页面。(如果UseSpaStaticFiles()UseSpa()都添加到中间件管道中,则Angular页面从ClientApp/dist文件夹成功服务,这意味着UseSpaStaticFiles()为其服务。)

如果环境设置为生产环境,则根本不配置UseSpa(),它只是添加到中间件管道中。

你能帮我澄清一下为什么UseSpaStaticFiles()不能在没有UseSpa()的情况下为页面提供服务,以及为什么UseSpa()应该总是添加到管道中,即使它不提供任何服务?我的配置中是否遗漏了导致这种行为的内容?

UseSpa本质上是一个包罗万象的路由处理程序,它将所有以前未处理的路由转发到单页应用程序。这样,您的SPA就可以执行客户端路由之类的操作,而无需服务器应用程序知道客户端应用程序所了解的所有路由。

相反,UseSpaStaticFiles是一个静态文件处理程序,它只为编译后的应用程序文件提供服务,例如,在它们各自的名称下的应用程序包,如app.jsvendor.js,就像UseStaticFileswwwroot文件夹中的静态文件工作一样。

使用"UseSpa">

这个答案可能有点长,但关键在于细节:)

对于初学者来说,意识到Angular有两种主要模式开发生产

  1. 开发-在这种模式下,您的代码不会将Angular的构建编译为"dist";文件夹作为Angular CLI,并且开发服务器在内存中仅运行构建。因此;dist";(发行版)或JavaScript构建文件夹在开发中运行时不存在!

  2. 生产-在这种模式下,它将把Angular构建编译为";dist";文件夹并从";dist";在该文件夹中找到的文件夹的原始JavaScript静态文件。

因此,这两种在Angular中工作的方式彼此完全不同。这就是让开发人员感到困惑的地方,而且从来没有在网上解释清楚。

ASP.NET内部的角度

ASP.NET Core网站项目中,当您将Angular添加到ASP.NET Core项目并尝试运行网站时,Angular不遵循与ASP.NET相同的规则,并且如果没有特殊的命令行或其他脚本,Angular将不会构建或加载。在.NET的开发环境中,它不使用ASP.NET加载;构建";进程,不会出现在本地IIS/IIsExpress/Kestrel测试服务器中,甚至不会在没有特殊帮助的情况下显示为物理文件

大多数Angular开发人员都运行命令行进程(Angular CLI)来在Angular中为您完成所有这些操作,但UseSpa工具和扩展允许您强制C#调用Angular的特殊构建和服务器例程,然后将它们加载到开发测试环境/浏览器中,而无需单独的命令行技巧。

对于初学者来说,Angular在开发中必须在自己的web服务器("Angular development server")内运行,也必须构建自己,并在自己的内存空间内将自己特殊的TypeScript加载到JavaScript编译/转译文件。除非您尝试使用与ASP.NET和Visual Studio不同的命令提示符通过CLI编译Angular,否则不会看到或观察到这些情况。

微软一直在努力尝试将Angular及其奇怪的命令行构建过程合并到各种模板中,你会注意到这些模板每隔几年就会不断变化和重写。当前的迭代将项目分为两个独立的项目,我很喜欢,但您仍然无法将Angular集成到一个网站中,也无法从ASP测试其生产构建。NET。

过去的版本使用精心设计的";"代理";调用以保持Angular dev服务器与任何包含数据的WebAPI或MVC项目分离;dist";Angular的构建用于生产。这样做的优点是,您有两个独立的浏览器窗口和服务器,但很难从项目中解开SpaProxy调用和代码。

单个ASP中的program.cs文件。NET是管理Angular与ASP。NET,因为您不受微软模板或Angular专有构建系统的限制,可以在浏览器中运行它。UseSpaAddSpaStaticFilesUseSpaStaticFiles代码块在中。NET Core可以组合在一个ASP中运行开发(在内存中)和生产(dist文件)。NET核心web应用程序。你只需要修改程序文件并遵守这些规则。。。

  1. 在本地PC上使用Angular进行开发时,您只需要运行UserSpa代码,该代码将在内存中加载Angular进行测试。就是这样!

  2. 在生产中,您需要打开AddSpaStaticFilesUseSpaStaticFilesUseSpa(全部三个),因为您需要告诉ASP。NET的单页应用程序的优先级;dist";并在每次浏览器访问时加载它们。

为什么?CCD_ 36、CCD_;dist";构建,指向包含Angular编译的JavaScript链接的index.html文件。

在发展中,它是不同的。在开发过程中,您只需要调用CCD_;"开始";Angular CLI构建,因为在开发过程中,您只需要在内存中运行Angular。程序.cs文件末尾的UseSpa调用将确保您的网站路由到Angular应用程序(在内存或静态文件中)。

使用此策略,您现在可以从一个ASP运行开发Angular和静态生产。NET网站和测试两者。执行此操作的代码如下。

魔术密码

首先创建您的ASP。NET网站。我选择了一个WebAPI。然后继续,将文件的Angular文件夹拖动到ASP的根文件夹中。NET网站不要为ASP安装任何MICROSOFT模板。NET核心与Angular模板,因为我们将手动连接ASP。NET来Angular我们自己!

请确保首先在Visual Studio中的项目中安装这两个Nuget Packages

Microsoft.AspNetCore.SpaServices.Extensions
Microsoft.AspNetCore.SpaServices

如果你想继续构建你的Angular产品";dist";或者使用您选择的命令行工具和下面的脚本构建文件夹,继续。但是下面的代码允许您只更改environmentC#调用,并在ASP期间动态创建它。NET构建。

为此,打开一个command prompt,将目录更改为Angular根文件夹(请参阅下文),然后运行构建脚本(请参见下文)。这创建了";dist";仅用于生产测试的物理文件文件夹:

cd c:{path to your Angular root folder}
ng build

现在在您的ASP。NET项目,按如下方式修改您的program.cs文件。这段代码将允许您在localhost浏览器中测试Angular Development和Angular Production代码,但只需更改顶部的环境变量。(多酷!):

using Microsoft.AspNetCore.SpaServices.AngularCli;
// Add a way to quickly change Development and Production
// Just comment out one and enable the other to
// run either of the Angular builds locally.
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
EnvironmentName = Environments.Development
//EnvironmentName = Environments.Production
}); 
builder.Services.AddControllers();
// PRODUCTION ONLY
if (builder.Environment.IsProduction())
{
builder.Services.AddSpaStaticFiles(configuration =>
{
// PRODUCTION
configuration.RootPath = "AngularRootFolder/dist/YourProjectName";
});
} else {
// You do not need to serve static Angular
// in development or the SPA root path!
}
// Turn off all Swagger calls!!
//builder.Services.AddEndpointsApiExplorer();
//builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
// Turn off all Swagger calls!!
//app.UseSwagger();
//app.UseSwaggerUI();
}
// PRODUCTION ONLY
if (builder.Environment.IsProduction())
{
// You do not need access to static Angular files
// in Development as you are only triggering
// the "ng start" call below which runs Angular
// builds in-memory.
app.UseSpaStaticFiles();
}
app.UseHttpsRedirection();
// Be sure to call the Routing and Endpoint Middleware!
// This allows your WebApi to route to endpoints for
// data Angular calls inside the same project, but avoid
// ASP.NET setting a default homepage in the WebAPI.
// This is the key to letting the Single Page Application
// be the default route in the pipeline.
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
// At the end of the pipeline you always
// need some Single Page Application mapping
// to occur so in the browser your Angular app
// pops up in both environments.
// That is what "UserSpa" does. In development
// however it also calls the Angular CLI via the
// "ng start" command inside "packages.json"
// in your Angular folder.
app.UseSpa(spa =>
{
if (builder.Environment.IsDevelopment())
{
// DEVELOPMENT
// This gets the web root, and adds your path to your Angular folder.
spa.Options.SourcePath = System.IO.Path.Combine(builder.Environment.ContentRootPath, "AngularRootFolder");
// This calls the Angular CLI to build your Angular
// project and call it via its server. This will
// Load the Angular app into the browser, too,
// And call your WebAPI for data.
spa.UseAngularCliServer(npmScript: "start");
} else {
// PRODUCTION
// DO NOT CALL THE Angular CLI and dev server in-memory
// when in Production as will call only the static files!
}
});
app.Run();

您应该能够从一个ASP测试Angular的开发和生产版本。NET网站,并从Angular!

最新更新