Angular 16 SSR with Koa custom status code for NotFound page

33 views Asked by At

I have an Angular 16 application with SSR (Server-Side Rendering) that uses Koa as the Node framework for the server. To make it work, I've created my own engine.

export function ngKoaEngine(setupOptions: Readonly<NgSetupOptions>) {
  const engine = new CommonEngine(setupOptions.bootstrap, setupOptions.providers);

  return async (filePath: string, ctx: Context, options: Readonly<Partial<CommonRenderOptions>> = {}) => {
    const moduleOrFactory = options.bootstrap || setupOptions.bootstrap;
    if (!moduleOrFactory) {
      throw new Error('You must pass in a NgModule to be bootstrapped');
    }
    const { request } = ctx;
    const renderOptions: CommonRenderOptions = Object.assign({ bootstrap: setupOptions.bootstrap }, options);

    renderOptions.url = renderOptions.url || `${request.protocol}://${request.get('host') || ''}${request.originalUrl}`;
    renderOptions.documentFilePath = renderOptions.documentFilePath || filePath;
    renderOptions.providers = (renderOptions.providers || []).concat(getReqResProviders(ctx));
    renderOptions.publicPath = renderOptions.publicPath ?? setupOptions.publicPath ?? (options as any).settings?.views;
    renderOptions.inlineCriticalCss = renderOptions.inlineCriticalCss ?? setupOptions.inlineCriticalCss;
    const html = await engine.render(renderOptions);

    ctx.body = html;
  };
}

/**
 * Get providers of the request and response
 */
function getReqResProviders(ctx: Context): StaticProvider[] {
  const providers: StaticProvider[] = [
    { provide: CONTEXT, useValue: ctx },
    { provide: REQUEST, useValue: ctx.request },
  ];

  if (ctx.response) {
    providers.push({ provide: RESPONSE, useValue: ctx.response });
  }
  return providers;
}

And in my main server file, I have something like this:

const render = ngKoaEngine({
    inlineCriticalCss: false,
    bootstrap: AppServerModule,
});
server.use(async (ctx) => {
    try {
      await render(indexPath, ctx);
    } catch (error) {
      ctx.status = httpStatusCodes.HTTP_INTERNAL_SERVER_ERROR;
      ctx.body = error.stack + error.message;
    }
});

and this main router

     {
        path: '**',
        redirectTo: 'error.html',
     },
     {
        path: 'error.html',
        loadChildren: () => import('./not-found/not-found.module').then((mod) => mod.NotFoundModule),
     },

I need that when navigating to the error.html route, a notFoundComponent is rendered, which is done correctly, but it returns a status 200 when I need a 404.

Following what was indicated here Angular 9 SSR 404 Not Found Page with Status code, I have tried modifying my notFound component as follows:


export class NotFoundComponent implements OnInit {

  constructor(
    @Optional() @Inject(RESPONSE) private response: Response,
    @Inject(PLATFORM_ID) private platformId,
  ) {}

  ngOnInit(): void {
    if (isPlatformServer(this.platformId)) {
      this.response.status = 404;
    }
  }
}

In this way, it returns a 404 but does not render the notFound page; instead, it returns

{
    code: "404",
    description: "Resource not found",
    level: "ERROR",
    message: "Route not found [ROUTE_NOT_FOUND_ERROR]"
}

If instead of setting this.response.status = 404`` , I set this.response.status = 400`, the notFound page is rendered, and it returns a 400 status. However, when I set it to 404, the page doesn't render. I have tried returning the status in the ngKoaEngine, but every time I set it to 404, it doesn't render. Is there a way to achieve both, rendering the notFound page and having the status set to 404?

0

There are 0 answers