I have a stock aspnetcore
and reactjs
app, generated from the starter template (dotnet new react
). I would like the SPA app to be served from a subpath off the root url; e.g. instead of the sample app being https://localhost:5001/counter
I'm looking for it to instead be served from https://localhost:5001/myapp/counter
.
I changed the Startup.cs
from:
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
to this:
app.Map(new Microsoft.AspNetCore.Http.PathString("/myapp"), appMember =>
{
appMember.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
});
This sort of works. If I browse to https://localhost:5001/myapp/
it appears to load the index.html, but the static files are attempting to load from the root path and not the subpath.
What needs to be changed so that the react app uses the subpath as the root? I'd like this to work both in the interactive VS dev environment and when deployed, likely on IIS. It seems like it's close but I'm missing something.
Sample demo of the solution is available here: https://github.com/petertirrell/mvc-spa-demo/tree/master/mvc-spa-demo
Thanks!
Angular and React have dominated the Javascript framework space for some time now. Both are used widely and a great option for SPA's.
The ASP.NET Core with React project template provides a convenient starting point for ASP.NET Core apps using React and Create React App (CRA) to implement a rich, client-side user interface (UI).
Essentially, A SPA holds the markups/HTML and 'data fetchers that make calls to the server to fetch only data, when it arrives it mixes the data with some markup to create a nice UI.
Start with moving app to sub-path by adding this to top of package.json:
"homepage": "/myapp/",
When running npm start
inside ClientApp folder, app is now serving http://localhost:3000/myapp
Then change Startup.cs
like this:
First remove
app.UseSpaStaticFiles()
then add
const string spaPath = "/myapp";
if (env.IsDevelopment())
{
app.MapWhen(ctx => ctx.Request.Path.StartsWithSegments(spaPath)
|| ctx.Request.Path.StartsWithSegments("/sockjs-node"),
client =>
{
client.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
spa.UseReactDevelopmentServer(npmScript: "start");
});
});
}
else
{
app.Map(new PathString(spaPath), client =>
{
// `https://github.com/dotnet/aspnetcore/issues/3147`
client.UseSpaStaticFiles(new StaticFileOptions()
{
OnPrepareResponse = ctx =>
{
if (ctx.Context.Request.Path.StartsWithSegments($"{spaPath}/static"))
{
// Cache all static resources for 1 year (versioned file names)
var headers = ctx.Context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromDays(365)
};
}
else
{
// Do not cache explicit `/index.html` or any other files. See also: `DefaultPageStaticFileOptions` below for implicit "/index.html"
var headers = ctx.Context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromDays(0)
};
}
}
});
client.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions()
{
OnPrepareResponse = ctx => {
// Do not cache implicit `/index.html`. See also: `UseSpaStaticFiles` above
var headers = ctx.Context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromDays(0)
};
}
};
});
});
}
Don't forget to clear browser history before testing changes for the first time on e.g. Azure.
You can do so by having:
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
in your ConfigureServices
and:
string spaPath = "/myapp";
if (env.IsDevelopment())
{
app.MapWhen(y => y.Request.Path.StartsWithSegments(spaPath), client =>
{
client.UseSpa(spa =>
{
spa.UseReactDevelopmentServer(npmScript: "start");
});
});
}
else
{
app.Map(new PathString(spaPath), client =>
{
client.UseSpaStaticFiles();
client.UseSpa(spa => {});
});
}
It should be noted that in development we use .MapWhen
because .Map
would cause your static files to be available at /myapp/myapp/[file]
as opposed to /myapp/[file]
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With