For some reason, if I'm the last piece of middleware and I try calling the next() function, I get the same error as this common SO issue.
So that I have a minimal example, I set it up as follows:
In my route, I have:
router.get('/add',(req,res,next) => {
res.render('ideas/add');
next();
});
And then I have a final piece of middleware:
app.use(function finalMiddleware(req,res,next){
console.log("We are here!");
console.log(app._router.stack);
next();
});
And the console logs the following as the last element in the stack:
Layer {
handle: [Function: finalMiddleware],
name: 'finalMiddleware',
params: {},
path: '',
keys: [],
regexp: { /^\/?(?=\/|$)/i fast_star: false, fast_slash: true },
route: undefined } ]
However, I still get:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:471:11)
at ServerResponse.header (../node_modules/express/lib/response.js:767:10)
at ServerResponse.send (../node_modules/express/lib/response.js:170:12)
at done (../node_modules/express/lib/response.js:1004:10)
at Immediate.<anonymous> (../node_modules/express-handlebars/lib/utils.js:26:13)
I'm wondering if this for some reason has to do with handlebars? I just don't understand why if I'm not writing headers with later middleware that this would possibly be an issue. I can't seem to figure out how to debug it further. Sure, I can remove the next() call from my final piece of middleware, but I want to understand if there's something deeper going on as I'm new to Express and want to make I'm understanding stuff correctly.
It turns out I was missing a key piece of information from both the similar answer and from my investigation: if next() is called and nothing is left to execute in regards to middlware, the server will send a message Cannot GET /(route name). For me, my callback was executing after this message was sent, since next() must call the function that writes this default response.
Hence, since Express already sent the message Cannot GET /(routename), as soon as the callback fired, which was after, it wasn't able to send a response.
Related
I'm trying to set up a method that is called with Shopify's webhook. I get the data and I'm able to store with a fresh server but I get "Error: Can't set headers after they are sent" returned in the console. I believe this is because I'm calling res twice. Any ideas on how to structure this better?
This is my method:
function createProductsWebHook(req,res,next) {
//if(req.headers){
// res.status(200).send('Got it')
// return next()
// }
res.sendStatus(200)
next()
const productResponse = req.body
console.log(productResponse)
const product = Product.build({
body_html: req.body.body_html,
title: req.body.title,
});
product.save()
.then(saveProduct => res.json(saveProduct))
.catch((e)=> {
console.log(e)
});
}
This occurs because the middleware, createProductsWebHook(), is called first when a request is received, which then sends a 200 status code response, res.sendStatus(200). Then in, in the same middleware function, product.save().then(...) is called. save()’s callback function attempts to send a response too – after one has already been sent by the very same middleware – using res.json(saveProduct).
Key Takeaway
Middleware should not send the response; this defeats the purpose of middleware. Middleware's job is to decorate (add or remove information, i.e, headers, renew some auth session asynchronously, perform side effects, and other tasks) from a request or response and pass it along, like a chain of responsibility, not transmit it – that's what your route handler is for (the one you registered your HTTP path and method with, e.g., app.post(my_path, some_middleware, route_handler).
I get a "can't set headers after they're sent" error, which I understand may be due to sending a response from my server more than once, however it doesn't seem to be the case with my code, at least on the surface. I make a query to dbpedia in order to obtain some resources, which I then send back to my client. Here's the code:
app.get("/",function(req,res,next){
if (req.query.titolo){
var response ;
let [ artist, title ] = getArtistTitle(req.query.titolo);
title = title.replace(" ","_");//for dbpedia resource names
const prefixes = {
dbo: "http://dbpedia.org/ontology/",
db: "http://dbpedia.org/resource/"
}
DbPediaClient.setOptions('application/sparql-results+json',prefixes);
DbPediaClient.query("SELECT ?abstract WHERE { db:"+title+
" dbo:abstract ?abstract. FILTER langMatches(lang(?abstract),'en') }")
.then((data)=>{
response = data["results"]["bindings"][0]["abstract"]["value"] ;
return res.json({abstract: response}) ;
})
.catch((error)=>{
console.log(error);
});
}
});
I use virtuoso-sparql-client to make a query to http://dbpedia.org/sparql (DbPediaClient is initialized outside of this function). I've already tried using res.send instead of res.json, and it still gives the same error. Which by the way it's the following:
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 (/home/lorenzo/Scrivania/ProgettoTechweb/AlphaTube/node_modules/express/lib/response.js:767:10)
at ServerResponse.send (/home/lorenzo/Scrivania/ProgettoTechweb/AlphaTube/node_modules/express/lib/response.js:170:12)
at DbPediaClient.query.then (/home/lorenzo/Scrivania/ProgettoTechweb/AlphaTube/server.js:43:15)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
If you need more info please ask.
Express is a framework that handles request/response cycles by iterating through routes that match the path specified in an HTTP request and letting your code send the response in each match or call next() if it didn't send one. The problem here is you have the following:
app.get("/",function(req,res,next){
res.sendFile(...);
next();
});
app.get("/",function(req,res,next){
if (req.query.titolo){
...
res.json(...);
}
});
This code means
Whenever I get an HTTP request to / then send some HTML, then if the request contains titolo in the query, send some JSON as well.
Express is telling you that you can't send anything else since you already sent the HTML.
One solution would be to move the API route above the HTML:
app.get("/",function(req,res,next){
if (req.query.titolo){
...
res.json(...);
} else {
next();
}
});
app.get("/",function(req,res){
res.sendFile(...);
});
which means
Whenever I get an HTTP request to /, if the request contains titolo in the query, send some JSON, otherwise send some HTML.
But my recommendation would be to move the API route to a different path that doesn't collide with the route for your static file, which is a lot more typical for HTTP servers.
I have function, that executes when 'dataCompiled' event fires, it looks like his:
eventEmitter.on('dataCompiled', function () {
json = JSON.stringify({
conversations: convs
});
res.json(json).end();
return;
});
But when i refreshing page, i getting error
Error: Can't set headers after they are sent.
Finally figured out, need send json directly to end function, so it'll look like that:
res.end(json);
You don't need to stringify and end a call to res.json if I'm not mistaken:
eventEmitter.on('dataCompiled', function () {
return res.json({
conversations: convs
});
});
Are you sure you're not sending more data after this call? You shouldn't have to use res.end(): http://expressjs.com/en/4x/api.html#res.end .
If there is another place in your code you're sending more data to res it will give the error message you are receiving, res.end is not fixing the underlying problem.
I am setting a cookie like below in a post function.
res.cookie('sessionID', req.sessionID, { path: '/' });
It sets it fine...but I cannot seem to delete it. I am deleting it in a put function, because I want to update some data and then clear the cookie.
app.put('/data/completed/:info', jsonParser, function(req, res){
if (!req.body) return res.sendStatus(400)
res.clearCookie('sessionID', { path: '/' });
console.log(req.cookies.sessionID);
});
Obviously I am doing something wrong I just can't figure out what.
You are clearing cookie of response. But checking for request. Try to check console.log(res.cookies.sessionID);
So the issue wasn't with the clearing of the cookie it was with me not ending the response process. As clarified on the Express documentation.
res.end([data] [, encoding])
Ends the response process. This method actually comes from Node core,
specifically the response.end() method of http.ServerResponse.
Use to quickly end the response without any data. If you need to
respond with data, instead use methods such as res.send() and
res.json().
So when I put the res.end() function in the code, everything works as expected and the cookie clears.
app.put('/reset/:completed',function(req,res){
res.clearCookie('sessionID', { path: '/' });
res.end();
});
I have been dealing with this problem now for quite a while, and I can't seem to figure out why it's happening.
I'm getting the Error: Can't set headers after they are sent.
I was able to track down the offending Function Call via the Stack Trace, which leads me to believe that the error lies within this function:
exports.getCardUser = function(req, res, next) {
if (req.user) {
User.cardUser(req.user.userId, function(responseValue) {
res.json(200, responseValue);
});
} else {
res.send(401);
}
};
However, if I hit the Endpoint via a regular REST Client, iex: Hitting the API Endpoint in an Isolated Environment, the Error is not being thrown.
Any ideas?
Edit: skypjack's brought me on the right track - Callback was called twice. Thanks!