oMid dehghani
oMid dehghani

Reputation: 183

How to handle multiple SPA application in ASP.NET Core

I have an ASP.NET Core Web API application, I want to serve two Angular apps, one for admin and one for users.

In production, I don't use angular CLI tools so there is only some static files of two angular web applications.

Admin files are in /angular/admin and user files are in /angular/user. So, how can I serve them?

I tried multiple calls to IServiceCollection.AddSpaStaticFiles but they override each other. [From source repository I found this is Singleton services]

Upvotes: 12

Views: 13121

Answers (3)

Sike Mullivan
Sike Mullivan

Reputation: 310

I wanted to serve up an Outlook Addin from within my dotnet React based application. This is how I was able to get it to work for both development and production.

    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");
                    }
                });
            });
  }

Upvotes: 1

Mason Zhang
Mason Zhang

Reputation: 3459

another tip: I'm using react and the react-router cannot handle path in URL correctly. So I use a different port for different spa. Use "app.MapWhen(o => o.Request.Host == 6000, ...)" to handle this case.

In production, should be something like : MapWhen(o => o.Request.Host.Host == "a.com"...

Upvotes: 1

Avin Kavish
Avin Kavish

Reputation: 8947

You have to branch the application middleware pipeline into two and register the SPAs after setting up MVC

 ...
 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");
      });
  });  

                ```

Upvotes: 25

Related Questions