res.render(..) not working : error Can't set headers after they are sent
Express res.render is not working and throwing exception.
Expected Behavior
It should render render a view and let the view engine send a response.
Current Behavior
->res.send({'someKey': 'someValue'}) is working -> res.render('viewName', {title: 'Home'}) is throwing exception from handlebars view engine - error Can't set headers after they are sent
It is also working as soon as we remove inversifyjs.
Possible Solution
Let expressjs's view engine send the response. If InverseJs is sending the response after setting headers then there should be an option to define that inversejs is responsible for routing but not for sending responses. Although I'm not sure how it is working internally.
Steps to Reproduce (for bugs)
- Checkout any inversify express app seed
- In the controller, instead of res.send try rendering a view with res.render(..)
Context
Rendering a view is common is express app when our app is not single page app and we want to render the view from server side.
Your Environment
- Version used: npm -> 5.6.0, express -> 4.13.4, express-handlebars: 3.0.0
- Environment name and version (e.g. Chrome 39, node.js 5.4): Node 8.11.2
- Operating System and version (desktop or mobile): Windows 10 desktop
- Link to your project:
Code for simple HomeController:
import { Request, Response, NextFunction } from "express"; import { injectable, inject } from "inversify"; import { interfaces, controller, request, response, httpGet, next } from "inversify-express-utils";
@controller("/home") export class HomeController implements interfaces.Controller { @httpGet("/") private index(req: Request, res: Response) { // res.send({"message": "Success" }); res.render("home", { title: "Home" }); } }
Stack trace
_http_outgoing.js:491 throw new Error('Can't set headers after they are sent.'); ^
Error: Can't set headers after they are sent. at validateHeader (_http_outgoing.js:491:11) at ServerResponse.setHeader (_http_outgoing.js:498:3) at ServerResponse.header (F:\WitSpry\Source Code\Wisely\wisely\node_modules\express\lib\response.js:767:10) at ServerResponse.contentType (F:\WitSpry\Source Code\Wisely\wisely\node_modules\express\lib\response.js:595:15) at ServerResponse.send (F:\WitSpry\Source Code\Wisely\wisely\node_modules\express\lib\response.js:145:14) at F:\WitSpry\Source Code\Wisely\wisely\dist\controllers\home.js:31:17 at Immediate._onImmediate (F:\WitSpry\Source Code\Wisely\wisely\node_modules\express-handlebars\lib\utils.js:26:13) at runCallback (timers.js:810:20) at tryOnImmediate (timers.js:768:5) at processImmediate [as _immediateCallback] (timers.js:745:5)
+1
I had the same issue, wrapping the res.render() in a Promise worked, but not ideal.
Do you have an example in a controller of your solution? Or can we get this fix? This is currently stopping me from using inversify-express-utils at all! I need view engine support :/
Hi, in my solution all the controllers extend an abstract BaseController which has the custom render method. this is my code:
@injectable()
export abstract class BaseController {
public render(res: express.Response, template: string, options = {}): Promise<string> {
return new Promise<string>((resolve, reject) => {
res.render(template, options, (err, compiled) => {
if (err) {
console.log(err);
reject('500 when rendering the template');
}
resolve(compiled);
});
});
}
}
@controller('/')
export class ListingController extends BaseController {
constructor(@inject(TYPES.ApiService) private apiService: ApiService) {
super();
}
@httpGet('/')
public async get(
@request() req: express.Request,
@response() res: express.Response,
) {
// Do things...
const data: IListingData = await this.apiService.get(params);
return this.render(res, 'listing/listing', data);
}
}
I hope it helps!
This took me a while to find out. I was getting 204 responses with empty bodies even though I called res.render(). Even res.status(200).render() didn't help. @aescarcha Your solution still works, thanks for that