React Native/Node API: Can't set headers after they are sent - node.js

I am having an issue with my react native app interacting with my Node API.
When I try to post data to update some values in the database through node (which then goes to a stored procedure), I get a 500 - Can't set headers after they are sent in Node.
Everything I can find says that this could be due to sending responses twice. I don't think this is the case here. I've tested in postman and things work fine, it returns a status of 200 with the correct returned data.
I am trying to post data to the API like so:
const myHeaders = new Headers();
myHeaders.append('Content-Type', 'application/json');
fetch(`http://localhost:3000/user/preferences`, {
method: 'POST',
headers: myHeaders,
mode: 'cors',
cache: 'default',
body: JSON.stringify({
id: '1',
minage: this.state.minageValue,
maxage: this.state.maxageValue
})
})
.then(response => response.json())
.then(body => console.log(body))
.catch(err => console.log(err));
I am receiving it on the API side and passing the data to a stored procedure with this:
function updatePreferences(req, res, next) {
console.log(req);
var userid = req.body[0].id;
var minage = req.body[0].minage;
var maxage = req.body[0].maxage;
db.func('___spsavepreferences', [userid, minage, maxage])
.then(function(data){
res.status(200)
.json({
status: 'success',
preferences: data
});
});
}
Any ideas?
EDIT:
Don't know if this tells me anything, but I have logged the error message to the console:
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 ServerResponse.header (/Users/user/Projects/Project_node/node_modules/express/lib/response.js:730:10)
POST /user/preferences 500 410.809 ms - 189
at ServerResponse.send (/Users/user/Projects/Project_node/node_modules/express/lib/response.js:170:12)
at done (/Users/user/Projects/Project_node/node_modules/express/lib/response.js:967:10)
at Object.exports.renderFile (/Users/user/Projects/Project_node/node_modules/jade/lib/index.js:374:12)
at View.exports.__express [as engine] (/Users/user/Projects/Project_node/node_modules/jade/lib/index.js:417:11)
at View.render (/Users/user/Projects/Project_node/node_modules/express/lib/view.js:128:8)
at tryRender (/Users/user/Projects/Project_node/node_modules/express/lib/application.js:640:10)
at Function.render (/Users/user/Projects/Project_node/node_modules/express/lib/application.js:592:3)
at ServerResponse.render (/Users/user/Projects/Project_node/node_modules/express/lib/response.js:971:7)
at /Users/user/Projects/Project_node/app.js:51:7
at Layer.handle_error (/Users/user/Projects/Project_node/node_modules/express/lib/router/layer.js:71:5)
at trim_prefix (/Users/user/Projects/Project_node/node_modules/express/lib/router/index.js:315:13)
at /Users/user/Projects/Project_node/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/Users/user/Projects/Project_node/node_modules/express/lib/router/index.js:335:12)
EDIT 2: Got it. I needed to recieve the values as req.body.id, req.body.minage, etc... not as req.body[0].id.
Weird to get an error like that but that solved the error.

If you’re seeing different behavior in postman, that’s because postman just does the POST directly while the browser instead does a CORS preflight OPTIONS request before trying the POST.
So it sounds like the request that’s causing the “Can't set headers after they are sent” you’re seeing is that OPTIONS request, not the POST request.
You can confirm that by sending a OPTIONS request with postman, and making sure to also supply an Origin request header as part of the request. With curl you can do that like this:
curl -X OPTIONS -i -H 'Origin: http://x.y' http://localhost:3000/user/preferences
That will likely produce the same 500 error you’re seeing from the browser request.
As far as how to fix it, I think there’s no way anybody else here could tell you without seeing a lot more of your server side code. But what it would seem to come down to is, somewhere in your code you have one or more places with logic for handling OPTIONS requests, and somewhere in that OPTIONS-handling code, it’s trying to send headers after they’ve already been sent.
To track down where that is, it seems like you want to look through the server logs on the server side and find any message that’s getting logged when that 500 failure occurs.
And by the way, in this case the thing that triggers the browser to do a COR preflight OPTIONS request to begin with is the myHeaders.append('Content-Type', 'application/json') part.

Related

How do I use the the post method with fetch and koa?

This is a function on my front-end that makes the request.
function postNewUser(){
fetch(`http://12.0.0.1:8080/users/test`, {
method: 'POST',
body: {nome: name, email: "test#test.com.br", idade: 20}
})
}
This is my back-end code to receive the request.
router.post('/users/:id', koaBody(), ctx => {
ctx.set('Access-Control-Allow-Origin', '*');
users.push(ctx.request.body)
ctx.status = 201
ctx.body = ctx.params
console.log(users)
})
For some unknown reason I receive nothing. Not even a single error message. The "console.log()" on the back-end is also not triggered, so my theory is that the problem is on the front-end.
Edit
As sugested by gnososphere, I tested with Postman, and it worked. So now i know the problem must be on the fron-end code.
You can try your backend functionality with Postman. It's a great service for testing.
the request would look something like this
If the problem is on the frontend, double check your fetch method by posting to a website that will return data and logging that in your app.

Log request that caused error in node modules

I run a NodeJS server with two new error types in the logs:
[2021-05-21T09:11:33.891Z] SyntaxError: Unexpected token h in JSON at position 0
at JSON.parse (<anonymous>)
at createStrictSyntaxError (~/server/node_modules/body-parser/lib/types/json.js:158:10)
at parse (~/server/node_modules/body-parser/lib/types/json.js:83:15)
at ~/server/node_modules/body-parser/lib/read.js:121:18
at invokeCallback (~/server/node_modules/raw-body/index.js:224:16)
at done (~/server/node_modules/raw-body/index.js:213:7)
at IncomingMessage.onEnd (~/server/node_modules/raw-body/index.js:273:7)
at IncomingMessage.emit (events.js:323:22)
at IncomingMessage.EventEmitter.emit (domain.js:482:12)
at endReadableNT (_stream_readable.js:1204:12)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
The stacktrace shows only node_modules paths, not where in my code this error may have started. The stdout logs do not show what could have originated this error around that time either.
The server code that handles JSON objects is:
// Use JSON parser (required to parse POST forms)
app.use((req, res, next) => {
bodyParser.json()(req, res, next);
});
app.use(bodyParser.urlencoded({extended: true}));
I added logging inside this function in case I have the same error in the future.
In general, how can I log information about the request that caused an error in the node modules?
update with client-side code
This error originated from a user and I am unable to replicate it. The client-side code sending JSON data is:
// `id` indicates the ID of the video
var body = {
percent: percent,
videoId: id,
eventLabel: eventLabel
}
async function view() {
return await fetch("/viewership", {
method: "POST",
credentials: "include",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(body)
});
};
The Network tab of the Chrome debugger shows this request payload:
{percent: 0, videoId: ..., eventLabel: "play"}
Well, here's what we know.
The code is processing an IncomingMessage (and incoming http request)
The error comes from the body-parser module
The error comes from JSON.parse() on what is apparently supposed to be a JSON body
The error appears to come from this particular section of code in the body-parser module.
That code is this:
if (strict) {
var first = firstchar(body)
if (first !== '{' && first !== '[') {
debug('strict violation')
throw createStrictSyntaxError(body, first)
}
}
So, it is apparently failing to find a leading { or [ on the JSON and is instead finding an h.
We can deduce from that information that an incoming http request (probably a POST) is supposed to have a JSON body, but the data is not legal JSON.
Your first point of debugging is to see exactly the JSON body data is in the request. If this request is coming from a browser, you can look in the Chrome network tab of the debugger and see exactly what the browser is sending your server.
So, this is most likely a client-caused error. Either the content-type is set wrongly to JSON when the data is not JSON or the client is supposed to be sending JSON, but is not sending proper JSON.
If you can show us the client-side code for this, we may be able to spot the error in that code.
Do you know a way to log any request that throws an error, e.g. for other bad requests in the future unrelated to JSON?
When the body-parser gets bad JSON, it calls the Express error handler with the exception. If you go the the "Writing Error Handlers" on this Express doc page, it will show you how to catch these errors and handle them with some error page back to the client and as much logging as you want.

Set response header along with a string

I am trying to send the token in the headers of an HTTP request from backend to the frontend along with sending my own defined string. However, I am getting an issue. The token is being printed as null on the client-side. I don't know why:
Here's my code:
Node/Express
if (bcrypt.compareSync(passcode, results[0].password))
{
const token = jwt.sign({id: email}, secret, {expiresIn: 86400 });
console.log(token);
if(results[0].userrights == 'full')
{
res.setHeader('x-access-token', token);
res.send("Full Authorization");
}
//rest of the code
}
Angular
this.http.post('http://localhost:3000/api/login', form.value, {responseType: "text", observe:
'response'})
.subscribe(responseData => {
console.log(responseData);
console.log(responseData.headers.get('x-access-token')); //prints null on the console
I have searched quite a bit and found different examples which is making it very confusing. I don't want to use response status rather my own defined string. I have tried different things to print the variable but it still is throwing as null.
If you are using a browser extension to allow CORS requests then Access-Control-Expose-Headers should be added to the headers on server side. Please try adding the following line: res.setHeader('Access-Control-Expose-Headers', '*')
Angular2 's Http.post is not returning headers in the response of a POST method invocation
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers

Json.parse throws: "SyntaxError: Unexpected end of JSON input"

Hello fellow stackoverflow members,
The Problem
as described in the titel I have a problem parsing an string to JSON.
The string I want to parse is send by the client to the server with a POST Method. The request is handled by this function on the server:
async function handleReservierung (req: Http.IncomingMessage, res: Http.ServerResponse): Promise<void> {
res.setHeader("Content-Type", "application/json");
res.setHeader("Access-Control-Allow-Headers", "content-type");
res.setHeader("Access-Control-Allow-Origin", "*");
let body: string = "";
req.on("data", chunk => {
body += chunk.toString();
});
req.on("end", () => {
console.log(JSON.parse(body));
res.end(body);
});
}
I've read about incoming POST requests and how to handle them and saw this solution with the incoming chunks added up in the variable body. So far so good.
I thought probably the chunks were not added correctly but as I console.log(body) it would give me a perfectly valid string:
{"ids":["600c4eff6c54c802cc1cac4f","600c4f576c54c802cc1cac51"],"name":"Someons Name"}
like so tested in a JSON validator.
I built an Interface
interface Reservierungen {
ids: number[];
name?: string;
}
which is the one used on the client Side to fill out all the data and then its getting send in the Body of the request with JSON.stringify(body)
When it now ty to JSON.parse(body) on the server it throws this error:
SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at IncomingMessage.<anonymous> (C:\Users\felix\Documents\GIS\GIS-WiSe2020_2021\server\index.js:72:26)
at IncomingMessage.emit (events.js:327:22)
at endReadableNT (_stream_readable.js:1221:12)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
Why do I need to Parse the JSON?
I want to get the data from the JSON Object to update my MongoDB. I thought I could just parse the JSON string send in the body and use it to update the DB. After being stuck for quite a while I am kindly asking for you advice now :)
Important to know
Due to the limitations of our uni project: not useing any library like express or jquery I am asking for nativ typescript solutions.
I assume something is wrong with the string but I can't figure out what it is. I already tried to trim of the whitespaces which did not help either.
I hope I gave you all the information needed.
Thank you in advance.
I figured it out.
The problem was that I did copy the fetch from MDN and enabled CORS on the fetch() statement .
With CORS enabled there comes a preflight Request (CORS Details) which has an OPTIONS header. My Typescript server wasent configured to accept CORS requests (so no way to handle OPTIONS header) and did try to use the udefined body of the preflight Request.
I disabled the CORS header on the fetch() statement. Which resulted in a working JSON.parse()

Another "can't set headers after they're sent"

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.

Resources