Server-side rendering in ASP.NET Core Angular (2022 version)
This is an updated version of following walkthrough.
Create the project
Start by creating a new ASP.NET Core project. Choose the Angular template:
If you wish to use the Visual Studio publish wizard, try putting your project as close to the drive root as possible (Yes, you’re right, Visual Studio 2022 still doesn’t support the LongPathApi…)
Off course we choose to use .NET 6
Make sure you have the latest version of angular installed. Run a cmd as administrator, and issue:
npm install --global @angular/cli@latest
ng version
> 13.1.2
Now remove the ClientApp folder from the project, and recreate it by running the following command from the project folder:
ng new ClientApp --style scss --routing --strict
.NET Setup
Now we need to revert the Startup file back to the “old-style” Startup class.
- Open the NuGet package manager, and install the latest version of
Microsoft.AspNetCore.SpaServices.Extensions
into your project. - Also install
MintPlayer.AspNetCore.Hsts
. This is an improved version of the ASP.NET Core HSTS Middleware, which works correctly for SPA’s. This middleware uses theOnStarting
callback, and effectively adds the header to the HTTP response when visiting any page in your angular application. - Also install the
MintPlayer.AspNetCore.SpaServices.Routing
package into your project. This package contains the RouteBuilder to parse visited urls in your backend. The dependency projectPrerendering
contains the SpaPrerendering middleware. - Remove
Microsoft.AspNetCore.SpaProxy
.
Add the following Startup class
Then the Program
file should look like this:
The only thing missing now is the SpaPrerenderingService
This service is registered as a scoped service, so you can inject repositories, DbContext through the constructor.
Modify your csproj file:
Items to remove:
- SpaProxyServerUrl
- SpaProxyLaunchCommand
Items to add:
- BuildServerSideRenderer
- npm run build:ssr
Items to change:
<!-- Old lines -->
<DistFiles Include="$(SpaRoot)dist\**; $(SpaRoot)dist-server\**" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" /><!-- New lines -->
<DistFiles Include="$(SpaRoot)dist\**" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" Condition=" '$(BuildServerSideRenderer)' == 'false' " />
My final csproj file:
Angular setup
Navigate to the ClientApp
folder and call the following schematic:
ng add @nguniversal/express-engine
Now the output
folder is still disconfigured, and the configuration won’t work when DOTNET_ENVIRONMENT = Production
. To fix this, open the angular.json
file and change the outputPath
from dist/ClientApp/browser
to dist
.
Install aspnet-prerendering
and the SERVER_SIDE
provider
npm install --save aspnet-prerendering
npm install --save @mintplayer/ng-server-side
Modify the main.ts:
And modify the main.server.ts:
In angular.json
, change the server main to src/main.server.ts
tsconfig.app.json
The result is here: https://github.com/PieterjanDeClippel/Ssr/tree/master
EDIT
Since the .NET template was updated by Microsoft, you need to make additional changes before starting the deployment to your live webserver.
Troubleshoot
- Important!!!!: Please check the output window. Half of the information needed to solve occasional problems with SSR is logged in the Visual Studio Output window, so the Visual Studio Output window is the most interesting source to diagnose issues here. If you’re experiencing problems, don’t hesitate to write back, but include the contents of your Output window.
- In previous walkthroughs I’ve written out some checks you can perform to solve several issues
Possible troubles
NodeInvocationException: Cannot destructure property ‘AppServerModule’ of ‘module2.exports’ as it is undefined.
This link explains everything.
So replacing
const renderPromise = AppServerModuleNgFactory
? /* AoT */ renderModuleFactory(AppServerModuleNgFactory, options)
: /* dev */ renderModule(AppServerModule, options);
with
const renderPromise = renderModule(AppServerModule, options);
should be fine.
dotnet templates
I’ve created a dotnet template containing this code in this repository. You can install these templates using the following command:
dotnet new --install MintPlayer.AspNetCore.IdentityServer.Templates
or update them if they’re already installed
dotnet new --update-apply
After this, you should see the templates in Visual Studio
And be able to create them using dotnet
dotnet new id-provider
dotnet new id-application
If you’re starting Visual Studio as Administrator, then you’re best off running
npm i
from the ClientApp
folder, before starting the application.