How do i get Angular 19 SSR to work with .net 6

Discussão em 'Angular' iniciado por Josh Hick, Janeiro 6, 2025.

  1. Josh Hick

    Josh Hick Guest

    I'm converting my CSR angular app to SSR using .net 6. Now the .net backend is having trouble serving the files for the frontend. All the files build and are correctly placed in the proper directories. See snippets for setup files below.

    angular.json snippet

    "server": {
    "builder": "@angular-devkit/build-angular:server",
    "options": {
    "outputPath": "dist/my-app/server",
    "main": "src/main.server.ts",
    "tsConfig": "tsconfig.server.json"
    },
    "configurations": {
    ...
    }
    },

    "serve-ssr": {
    "builder": "@angular-devkit/build-angular:ssr-dev-server",
    "options": {
    "host": "localhost",
    "port": 44492,
    "ssl": true,
    "sslCert": "%APPDATA%\\ASP.NET\\https\\%npm_package_name%.pem",
    "sslKey": "%APPDATA%\\ASP.NET\\https\\%npm_package_name%.key",
    "proxyConfig": "proxy.conf.js"
    },
    "configurations": {
    "development": {
    "browserTarget": "my-app:build:development",
    "serverTarget": "my-app:server:development"
    },
    "production": {
    "browserTarget": "my-app:build:production",
    "serverTarget": "my-app:server:production"
    }
    },
    "defaultConfiguration": "development"
    },


    tsconfig.server.json snippet

    {
    "extends": "./tsconfig.json",
    "compilerOptions": {
    "outDir": "./dist/my-app/server",
    "target": "es2022",
    "module": "es2022",
    "moduleResolution": "node",
    "types": ["node"],
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
    },
    "files": [
    "src/main.server.ts"
    ],
    "angularCompilerOptions": {
    "enableIvy": true
    }
    }


    main.server.ts

    import { bootstrapApplication } from '@angular/platform-browser';
    import { AppComponent } from './app/app.component';
    import { serverAppConfig } from './app/app.config.server';

    const bootstrap = () => bootstrapApplication(AppComponent, serverAppConfig);

    export default bootstrap;


    server.ts

    import 'zone.js/node';

    import { APP_BASE_HREF } from '@angular/common';
    import { CommonEngine } from '@angular/ssr/node';
    import * as express from 'express';
    import { existsSync } from 'node:fs';
    import { join } from 'node:path';
    import AppServerModule from './src/main.server';

    // Define paths
    const distFolder = join(process.cwd(), 'dist', 'my-app', 'browser');
    const serverFolder = join(process.cwd(), 'dist', 'my-app', 'server');
    const indexHtml = existsSync(join(distFolder, 'index.original.html'))
    ? join(distFolder, 'index.original.html')
    : join(distFolder, 'index.html');

    // Initialize CommonEngine for SSR
    const commonEngine = new CommonEngine();

    export function app(): express.Express {
    const server = express();

    server.set('view engine', 'html');
    server.set('views', distFolder);

    // Static Files Serving
    server.get('*.*', express.static(distFolder, {
    maxAge: '1y'
    }));

    // Angular SSR Rendering
    server.get('*', (req, res, next) => {
    const { protocol, originalUrl, baseUrl, headers } = req;

    commonEngine
    .render({
    bootstrap: AppServerModule,
    documentFilePath: indexHtml,
    url: `${protocol}://${headers.host}${originalUrl}`,
    publicPath: distFolder,
    providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
    })
    .then((html) => res.send(html))
    .catch((err) => {
    console.error('SSR Error:', err);
    next(err);
    });
    });

    return server;
    }

    // Start the Server
    function run(): void {
    const port = process.env['PORT'] || 44492;

    const server = app();
    server.listen(port, () => {
    console.log(`✅ Node Express server listening on http://localhost:${port}`);
    });
    }

    // Ensure Server Only Runs in Standalone Mode
    declare const __non_webpack_require__: NodeRequire;
    const mainModule = __non_webpack_require__.main;
    const moduleFilename = mainModule && mainModule.filename || '';
    console.log('✅ Module Filename:', moduleFilename);
    console.log('✅ Current Filename:', __filename);
    if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
    console.log('✅ Running the SSR server...');
    run();
    }

    export default AppServerModule;


    Program.cs snippet (this was working with CSR)

    app.UseStaticFiles(new StaticFileOptions
    {
    OnPrepareResponse = ctx =>
    {
    ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=31536000");
    }
    });

    app.UseRouting();
    app.UseAuthorization();

    app.MapControllerRoute(
    name: "default",
    pattern: "{controller}/{action=Index}/{id?}");

    app.MapFallbackToFile("index.html");


    csproj snippet

    <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <IsPackable>false</IsPackable>
    <SpaRoot>ClientApp\</SpaRoot>
    <SpaProxyServerUrl>https://localhost:44492</SpaProxyServerUrl>
    <SpaProxyLaunchCommand>npm run dev:ssr</SpaProxyLaunchCommand>
    <ImplicitUsings>enable</ImplicitUsings>
    </PropertyGroup>

    ...

    <!-- Copy Browser and Server Builds to wwwroot -->
    <Target Name="CopySpaFiles" AfterTargets="PublishRunWebpack">
    <ItemGroup>
    <DistBrowserFiles Include="$(SpaRoot)dist\my-app\browser\**" />
    <DistServerFiles Include="$(SpaRoot)dist\my-app\server\**" />
    </ItemGroup>

    <Message Importance="high" Text="Copying Angular SSR files to wwwroot..." />
    <Copy SourceFiles="@(DistBrowserFiles)" DestinationFolder="wwwroot\%(RecursiveDir)" />
    <Copy SourceFiles="@(DistServerFiles)" DestinationFolder="wwwroot\%(RecursiveDir)" />
    </Target>

    Continue reading...

Compartilhe esta Página