如何在ASP.NET Core中处理多个SPA应用程序

10
我有一个ASP.NET Core Web API应用程序,我想为管理员和用户分别提供两个Angular应用程序。
在生产环境中,我没有使用angular CLI工具,因此只有两个angular Web应用程序的一些静态文件。
管理员文件位于/angular/admin,用户文件位于/angular/user。那么,我该如何为它们提供服务?
我尝试多次调用IServiceCollection.AddSpaStaticFiles,但它们会相互覆盖。[从源代码库中发现这是Singleton服务]
3个回答

22

您需要将应用程序中间件管道分支成两个,并在设置MVC之后注册SPAs

 ...
 app.UseMvc(...)

 app.Map("/admin",
   adminApp =>
   {
     adminApp.UseSpa(spa =>
     {
       spa.Options.SourcePath = "angular/admin";
       spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions
       {
           FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "angular", "admin"))
       };

       if (env.IsDevelopment())
         spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");
      });
    });

  app.Map("/user",
    userApp =>
    {
      userApp.UseSpa(spa =>
      {
        spa.Options.SourcePath = "angular/user";
        spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions
        {
            FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "angular", "user"))
        };

        if (env.IsDevelopment())
          spa.UseProxyToSpaDevelopmentServer("http://localhost:4201");
      });
  });  

                ```

我按照你说的写了代码,但仍然遇到了“SPA默认页面中间件无法返回默认页面'/index.html'...”的错误。直到我使用app.MapIApplicationBuilder分成两个appUserappAdmin,然后使用appUser.UseSpa(spa => spa.Options.DefaultPageStaticFileOptions = staticFileOptionsUser);appAdmin.UseSpa(spa => spa.Options.DefaultPageStaticFileOptions = staticFileOptionsAdmin);才解决了问题。感谢您的回复。 - oMid dehghani
是的,那通常是我所做的,但我想给你这个解决方案并看看它是否有效。xD 无论如何,您都必须拆分管道,对吧?或者在开发期间您是否在 asp net core 外部使用 angular cli? - Avin Kavish
那么如果没有使用 UseSpa,它就无法工作 :). 是的,我应该将其拆分。 - oMid dehghani
还有一个关于这方面的小技巧,我使用 UseProxyToSpaDevelopmentServer 而不是 UseAngularCliServer 并手动启动 Angular CLI,否则 Angular CLI 将在每次更改 C# 代码时重新启动。https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.spaproxyingextensions.useproxytospadevelopmentserver?view=aspnetcore-2.2 - Avin Kavish
这对我有用,但我必须添加 spa.UseVueCli(npmScript: "serve", port: 4200);(以及一个端口为4201的条目) - A Petrov
显示剩余3条评论

1
另一个提示:我正在使用React和React-Router无法正确处理URL中的路径。因此,我为不同的spa使用不同的端口。使用“app.MapWhen(o => o.Request.Host == 6000, ...)”来处理这种情况。
在生产中,应该像这样:MapWhen(o => o.Request.Host.Host == "a.com" ...

-1

我想从我的dotnet React应用程序中提供Outlook Addin服务。这是我成功地在开发和生产环境中使用的方法。

    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddSpaStaticFiles(configuration =>
        {
            configuration.RootPath = "ClientApp/build";
        });
        ...
    }
    

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      ...          

      
        
        app.Map("/addin",
           addin =>
           {
               var sfo = new StaticFileOptions()
               {
                   OnPrepareResponse = ctx =>
                   {
                       var resp = ctx.Context.Response;
                       resp.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate";
                       resp.Headers[HeaderNames.Expires] = "0";
                       resp.Headers[HeaderNames.Pragma] = "no-cache";
                   }
               };
               if (!env.IsDevelopment())
               {
                   var dir = Path.Combine(Directory.GetCurrentDirectory(), "OutlookAddinApp/dist");
                   sfo.FileProvider = new PhysicalFileProvider(dir);
               }
               addin.UseSpaStaticFiles(sfo);
               addin.UseSpa(spa =>
               {
                   spa.Options.SourcePath = "OutlookAddinApp";
                   spa.Options.DefaultPage = "/taskpane.html";
                   spa.Options.DefaultPageStaticFileOptions = sfo;

                   if (env.IsDevelopment())
                   {
                       spa.Options.StartupTimeout = TimeSpan.FromMinutes(2);
                       spa.UseProxyToSpaDevelopmentServer("https://localhost:3000");
                   }
               });
           })
           .Map("",
            root =>
            {
                var sfo = new StaticFileOptions()
                {
                    OnPrepareResponse = ctx =>
                    {
                        var resp = ctx.Context.Response;
                        resp.Headers[HeaderNames.CacheControl] = "no-cache, no-store, must-revalidate";
                        resp.Headers[HeaderNames.Expires] = "0";
                        resp.Headers[HeaderNames.Pragma] = "no-cache";
                    }
                };
                if (!env.IsDevelopment())
                {
                    var dir = Path.Combine(Directory.GetCurrentDirectory(), "ClientApp/build");
                    sfo.FileProvider = new PhysicalFileProvider(dir);
                }
                root.UseSpaStaticFiles(sfo);
                root.UseSpa(spa =>
                {
                    spa.Options.SourcePath = "ClientApp";
                    spa.Options.DefaultPageStaticFileOptions = sfo;

                    if (env.IsDevelopment())
                    {
                        spa.Options.StartupTimeout = TimeSpan.FromMinutes(2);
                        spa.UseReactDevelopmentServer(npmScript: "start");
                    }
                });
            });
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接