My web application was scaffolded from the dotnet core 3.0 Angular template. I want to deploy the first version into production but I am struggling to have the Angular app served. I am using Kestrel as the webserver (it's an intranet application).
I have published the application with dotnet publish -c Release -o ../app-dist
which build the Asp.net application and it triggered ng
to build a production release of the client. The resulting folder structure includes a app-dist/wwwroot
folder which has some assets like favicon and it has a app-dist/ClientApp/dist/app
folder with the index.html
and the compiled js
and css
assets. If I start the application by running dotnet app.dll
in app-dist
, Kestrel starts but it fails to serve the client application. I can access the minor assets from wwwroot
as well as the Razor pages that are used for user authentication.
In my docker deployment, I get the following error when trying to access https://host/
or https://host/index.html
(as well as http
equivalents):
System.InvalidOperationException: The SPA default page middleware could not return the default page '/index.html' because it was not found, and no other middleware handled the request. Your application is running in Production mode, so make sure it has been published, or that you have built your SPA manually. Alternatively you may wish to switch to the Development environment.
When I run the same command in app-dist
on my macOS development system I get the same error (after first setting export ASPNETCORE_ENVIRONMENT=production
).
In my Startup.cs
I have the following:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseStaticFiles();
if (!env.IsDevelopment())
{
app.UseSpaStaticFiles();
}
...
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
In my csproj:
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
<SpaRoot>ClientApp\</SpaRoot>
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
<BuildServerSideRenderer>false</BuildServerSideRenderer>
</PropertyGroup>
...
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build:ssr -- --prod" Condition=" '$(BuildServerSideRenderer)' == 'true' " />
<ItemGroup>
<DistFiles Include="$(SpaRoot)dist\**; $(SpaRoot)dist-server\**" />
<DistFiles Include="$(SpaRoot)node_modules\**" Condition="'$(BuildServerSideRenderer)' == 'true'" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
I have tried copying the Angular index.html
and compiled files to the wwwroot
as well as the ClientApp
folder, but neither made a difference. I also tried setting spa.Options.SourcePath = "ClientApp/dist/app";
. What seems incorrectly configured, or what should I investigate next?
If you want to change the directory of the served app in production you can do it in ConfigureServices
following way:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
Aborve the configuration.RootPath
should be changed to your wished path.