Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to host multiple React SPA apps on a single ASP.NET Core site?

I'm trying to run multiple React SPA apps using ASP.NET Core 3.1 with the lastest SpaServices extension and have a problem serving static assets. Much of what I've built comes from a similar question at: How to configure ASP.net Core server routing for multiple SPAs hosted with SpaServices but that was related to Angular and an older version of SpaServices.

My code has two React apps, ClientApp and AdminApp and I have configured each using the following startup code:

app.Map("/client", clientApp =>
{
    clientApp.UseSpa(spa =>
    {
        spa.Options.SourcePath = "ClientApp";

        if (env.IsDevelopment())
        {
            spa.UseReactDevelopmentServer(npmScript: "start");
        }
    });
});

app.Map("/admin", adminApp =>
{
    adminApp.UseSpa(spa =>
    {
        spa.Options.SourcePath = "AdminApp";

        if (env.IsDevelopment())
        {
            spa.UseReactDevelopmentServer(npmScript: "start");
        }
    });
});

Going to the /client and /admin routes serves the correct React index.html file, but the linked bundled .js files do not load. Here's an example of the final index HTML for the ClientApp SPA:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <base href="/client" />
    <title>Client App</title>
  </head>
  <body>
    <h1>Client App</h1>
    <div id="root"></div>
    <script src="/static/js/bundle.js"></script>
    <script src="/static/js/0.chunk.js"></script>
    <script src="/static/js/main.chunk.js"></script>
  </body>
</html>

The problem is that the links are relative to the root of the site /static/js/bundle.js and need to be relative to the React app's path. The example file paths should be /client/static/js/bundle.js and /admin/static/js/bundle.js, respectively.

How do I get the system that writes the paths into the index.html file to use the correct root path for the static files? Thanks.

like image 938
Brian Vallelunga Avatar asked Sep 22 '20 16:09

Brian Vallelunga


1 Answers

Come up with a possible solution. The expectation is that the build SPA will have the contents of its build directory copied into a folder in the wwwroot folder of the .NET Core project that uses the same name as the route the SPA will use. In this case we have two apps, guestuser and payinguser. The mapping in the Configure function will redirect the user request to pull the static files out of the appropriate wwwroot folder.

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
   ...
   services.AddSpaStaticFiles();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...

   app.UseStaticFiles(new StaticFileOptions()
   {
       FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot"))
   });

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

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

Within the ReactJS project you will want to update/add the homepage property to the package.json file for each project to also use the same name as the folder the site will be hosted in under the wwwroot folder. This will update the paths of the generated code to use the pathname in their file references.

{
  "name": "guest-user",
  "homepage": "guestuser",
  ...
}
like image 143
cminus Avatar answered Oct 09 '22 23:10

cminus