routing-controllers
routing-controllers copied to clipboard
'Can't set headers after they are sent' error when using Express middleware 'after' request.
import {Middleware, ExpressErrorMiddlewareInterface} from "routing-controllers";
@Middleware({ type: "after" })
export class CustomErrorHandler implements ExpressErrorMiddlewareInterface {
error(error: any, request: any, response: any, next: (err: any) => any) {
console.log("do something...");
next();
}
}
This example code from the documentation causes a 'Can't set headers after they are sent' error. Changing the type to 'before' fixes the issue.
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:504:11)
at ServerResponse.setHeader (_http_outgoing.js:511:3)
at dnsPrefetchControl (C:\Programming\pbc\server\auth-server\node_modules\dns-prefetch-control\index.js:9:11)
at call (C:\Programming\pbc\server\auth-server\node_modules\connect\index.js:239:7)
at next (C:\Programming\pbc\server\auth-server\node_modules\connect\index.js:183:5)
at Function.handle (C:\Programming\pbc\server\auth-server\node_modules\connect\index.js:186:3)
at app (C:\Programming\pbc\server\auth-server\node_modules\connect\index.js:51:37)
at Layer.handle [as handle_request] (C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\layer.js:95:5)
at trim_prefix (C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\index.js:317:13)
at C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\index.js:284:7
at Function.process_params (C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\index.js:335:12)
at next (C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\index.js:275:10)
at compression (C:\Programming\pbc\server\auth-server\node_modules\compression\index.js:220:5)
at Layer.handle [as handle_request] (C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\layer.js:95:5)
at trim_prefix (C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\index.js:317:13)
at C:\Programming\pbc\server\auth-server\node_modules\express\lib\router\index.js:284:7
But then setting type to 'before', the error handler won't catch the error! 😆
Disabling the default error handler and setting the type back to 'after' seems to be working combination so there's a reasonable workaround. But the way documentation is written, it looks like the original code I posted above should work without error even with the default error handler.
You simply needs to left out the next(); call and the error will go away.
We will rework this part, see progress in #256 & #144 and a few other.
Is that the case for just 'after' typed middleware? It makes sense as to why this issue happens now that I've played around with it and I've got working middleware with no issues. I don't think there's any bug here, but the documentation needs to be updated with a working example that won't throw an error 😀
I get this Can't set headers after they are sent even with no `next() method
@Middleware({ type: "after" })
export class CustomErrorHandler implements ExpressErrorMiddlewareInterface {
error(error: any, request: any, response: any, next: (err: any) => any) {
console.log("error...");
response.status(error.status || 500)
.json({
name : error.name,
message: error.message,
status : error.httpCode,
stack : error.stack,
})
}
}
What is sending the headers before response.status.json?
Default error handler.
For people coming here from the internet, from my research it seems like something like class-validators will set the headers for you, hence why you're getting this error.
I was able to work around it by checking for res.headersSent. Here's my full middleware:
import * as express from 'express';
import { ExpressErrorMiddlewareInterface, HttpError, Middleware } from 'routing-controllers';
import { Logger, ILoggerInterface } from '../../decorators/logger';
import { env } from '../../env';
@Middleware({ type: 'after' })
export class ErrorHandlerMiddleware implements ExpressErrorMiddlewareInterface {
isProduction = env.isProduction;
constructor(
@Logger(__filename) private log: ILoggerInterface
) { }
error(error: HttpError, req: express.Request, res: express.Response, next: express.NextFunction): void {
// It seems like some decorators handle setting the response (i.e. class-validators)
if (!res.headersSent) {
res.status(error.httpCode || 500);
res.json({
name: error.name,
message: error.message,
errors: error['errors'] || [],
});
}
if (this.isProduction) {
this.log.error(error.name, error.message);
} else {
this.log.error(error.name, error.stack);
}
}
}
Reopening to clarify this in the docs.
I realize this might be trivial for others, but for me (and, possibly, future Internet searchers), even after reading through this thread and the full intro page for the project, it took a while (and some code-digging) to realize that if you want to use your own error handler middleware, you're probably better off disabling the default one:
createExpressServer({
defaultErrorHandler: false,
// ...etc.
That way you can set your error headers and response bodies however you want, without having to worry about the headers being set before your middleware had a chance to do it.
Stale issue message