You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express集成Nunjucks模板时抛出ERR_HTTP_HEADERS_SENT错误求助

Nunjucks集成Inversify+Express时ERR_HTTP_HEADERS_SENT错误的原因与解决办法

问题场景

基于ESModule TypeScript的Node.js项目,使用Express.js和Inversify框架,集成Nunjucks作为模板引擎时,访问渲染njk模板的接口会抛出ERR_HTTP_HEADERS_SENT错误。切换为Pug模板或返回纯字符串时一切正常,且此前类似配置曾成功运行。

错误栈

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (node:_http_outgoing:659:11)
    at ServerResponse.header (/home/bailando/projects/learning-platform/node_modules/express/lib/response.js:794:10)
    at ServerResponse.contentType (/home/bailando/projects/learning-platform/node_modules/express/lib/response.js:624:15)
    at ServerResponse.send (/home/bailando/projects/learning-platform/node_modules/express/lib/response.js:149:14)
    at done (/home/bailando/projects/learning-platform/node_modules/express/lib/response.js:1045:10)
    at /home/bailando/projects/learning-platform/node_modules/nunjucks/src/environment.js:28:5
    at RawTask.call (/home/bailando/projects/learning-platform/node_modules/asap/asap.js:40:19)
    at flush (/home/bailando/projects/learning-platform/node_modules/asap/raw.js:50:29)
    at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
  code: 'ERR_HTTP_HEADERS_SENT'
}

相关代码

项目配置文件index.ts

import 'dotenv/config';
import 'reflect-metadata';
import container from './ioc.config.js';
import nunjucks from 'nunjucks';
import { InversifyExpressServer } from 'inversify-express-utils';

const server = new InversifyExpressServer(container);

server.setConfig((app) => {
  nunjucks.configure('views', {
    autoescape: true,
    express: app
  });

  app.set('view engine', 'njk');
});

const app = server.build();

app.listen(3000, () => {
  console.log('Server started on http://localhost:3000');
});

控制器文件home.controller.ts

import { Request, Response } from 'express';
import { controller, httpGet } from 'inversify-express-utils';

@controller('/')
export class HomeController {
  constructor() {}

  @httpGet('/')
  public async getUsers(_: Request, res: Response) {
    res.render('home');
  }
}

模板文件home.njk

内容为纯文本:Hello world

原因分析

核心问题在于控制器的getUsers方法被标记为async。Inversify-express-utils会将异步方法的返回值(一个Promise)视为未完成的响应处理,当Nunjucks通过res.render完成响应发送后,框架会在Promise resolve时尝试再次处理响应,导致重复设置HTTP头,触发ERR_HTTP_HEADERS_SENT错误。

而Pug模板或纯字符串返回时未触发该错误,是因为它们的响应处理逻辑与Nunjucks的异步调度机制存在差异,或者此前的配置中控制器方法并未使用async关键字。

解决办法

方案1:移除async关键字

由于res.render本身是基于回调的异步操作,不需要用async包裹,直接移除即可:

import { Request, Response } from 'express';
import { controller, httpGet } from 'inversify-express-utils';

@controller('/')
export class HomeController {
  constructor() {}

  @httpGet('/')
  public getUsers(_: Request, res: Response) {
    res.render('home');
  }
}

方案2:保留async并手动控制Promise完成时机

如果业务逻辑需要保留async,可以将res.render包装为Promise,确保在渲染完成后再resolve,避免框架重复处理:

import { Request, Response } from 'express';
import { controller, httpGet } from 'inversify-express-utils';

@controller('/')
export class HomeController {
  constructor() {}

  @httpGet('/')
  public async getUsers(_: Request, res: Response) {
    await new Promise<void>((resolve) => {
      res.render('home', resolve);
    });
  }
}

内容的提问来源于stack exchange,提问作者Pan Wolodyjowsky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 09:25:04