Hosting Multiple (React) SPAs in Asp.Net Core for development.
See original GitHub issueThis might start off sounding like a general usage question; however there have been multiple SO posts that mention this topic in one way or another.
I also realize the tremendous amount of work it takes to handle more serious issues, so thank you for reading.
I am simply attempting to figure out how to host multiple (in this case, React) SPA’s using either App.UseProxyToSpaDevelopmentServer
or App.UseReactDevelopmentServer
for development.
Previously, I worked together a solution of building (npm run build
) each SPA and constructing a custom StaticFilesProvider that would serve them up. Obviously this build process takes quite a while and I would like to be able to use something like App.UseProxyToSpaDevelopmentServer
to take advantage of hot-reloading, etc.
I have put together a basic demo repo that illustrates the problem I am experiencing. It is linked at the bottom of this ticket.
For clarity, here is my Startup.cs class:
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseRouting();
app.Map("/spa1", spa1 =>
{
if (env.IsDevelopment())
{
spa1.UseSpa(spa =>
{
spa.Options.SourcePath = Path.Join(env.ContentRootPath, "spa1");
spa.UseProxyToSpaDevelopmentServer("http://localhost:3001/");
});
}
});
app.Map("/spa2", spa2 =>
{
if (env.IsDevelopment())
{
spa2.UseSpa(spa =>
{
spa.Options.SourcePath = Path.Join(env.ContentRootPath, "spa2");
spa.UseProxyToSpaDevelopmentServer("http://localhost:3002/");
});
}
});
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
}
When I use UseProxyToSpaDevelopmentServer
, I start my SPAs separately (on port 3001 and 3002 for /spa1 and /spa2 respectively). I then start my .NET app, which boots as normal. I then navigate to the desired SPA endpoint (say http://localhost:5001/spa1
). When inspecting the browser, I see that my static content (I.e.,https://localhost:5001/spa1/static/js/bundle.js
) all have 200 status codes, but there are MIME type errors printed out to the console:
The script from “https://localhost:5001/spa1/static/js/bundle.js” was loaded even though its MIME type (“text/html”) is not a valid JavaScript MIME type.
Here is a link to the reproduce repo
It feels like I am just overlooking something; however, if I am not, perhaps this is something that could be further documented? (I wouldn’t mind at all helping with that process if needed).
Thanks again for all of the work making awesome open source software!
Issue Analytics
- State:
- Created 2 years ago
- Comments:6 (5 by maintainers)
@spencer741 if you reverse the way the proxying works it’s actually closer to your E2E scenario. I haven’t been around that area of the codebase in quite a while, but if I recall it correctly the only thing the SPA extensions do in a production environment is serve the spa app from a different folder.
We have encountered the same issue and it turned out that the proxy does not create the proper URL when calling the development servers. In these scenarios, the React applications need to be configured to be hosted under /spa1 and /spa2 paths respectively so that it tries to load the resources from the proper URL (which seems to be missing from this example). Therefore the static files on the instances are similar to /spa1/static/js/bundle.js and /spa2/static/js/bundle.js.
When these requests arrive on the server, due to the Map call, the request context will look like this: Request.Path = “/static/js/bundle.js” and Request.PathBase = “/spa1”. If you look at the proxy code, you can realize that even though we set the target URL of the proxy to http://localhost:3001/spa1 and http://localhost:3002/spa2, the logic of the Uri concatenation will strip down the important /spa1 and /spa2 segments because it is ignoring the PathBase property.
I understand that just adding the PathBase property here might break some things, but configuring the proxy path based on the incoming request might be a solution for this.