SyntaxError: Unexpected token " in JSON at position 0 - javascript

I have an error with request to express. I have this fetch:
fetch(`http://localhost:4200/dist/js/server.min.js`, {
method: "POST",
// mode: 'no-cors',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(`<html><body><div style="background-color: yellow;"><p>Hello World!</p></div></body></html>`),
}).then((response) => {
console.log(response)
})
And I have such a code for my express server:
const { exec } = require("child_process"),
express = require("express"),
bodyParser = require('body-parser'),
webshot = require('webshot'),
PORT = 4200,
app = express(),
cors = require('cors')
// app.use(cors())
app.use((req, res, next) => {
res.append('Access-Control-Allow-Origin', 'http://localhost:4242');
res.append('Access-Control-Allow-Methods', 'POST', 'GET', 'OPTIONS');
res.append('Access-Control-Allow-Headers', 'Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With');
res.append('Access-Control-Allow-Credentials', 'true');
next();
});
// app.use(express.json());
app.use(bodyParser.json());
app.get('/dist/js/server.min.js', (req, res) => {
res.send('<h1>Hello</h1>')
})
app.post('/', function(req, res) {
htmlData = req.body
screen(htmlData) // just my function
});
app.listen(PORT, () => {
console.log(`What's my age again? ${PORT}, I guess.`)
});
And I've got this error in browser:
POST http://localhost:4200/dist/js/server.min.js 400 (Bad Request)
And this in console:
SyntaxError: Unexpected token " in JSON at position 0
at JSON.parse (<anonymous>)
at createStrictSyntaxError (/home/joe/Documents/vscode-projects/html-projects/swipeskins/node_modules/body-parser/lib/types/json.js:158:10)
at parse (/home/joe/Documents/vscode-projects/html-projects/swipeskins/node_modules/body-parser/lib/types/json.js:83:15)
at /home/joe/Documents/vscode-projects/html-projects/swipeskins/node_modules/body-parser/lib/read.js:121:18
at invokeCallback (/home/joe/Documents/vscode-projects/html-projects/swipeskins/node_modules/body-parser/node_modules/raw-body/index.js:224:16)
at done (/home/joe/Documents/vscode-projects/html-projects/swipeskins/node_modules/body-parser/node_modules/raw-body/index.js:213:7)
at IncomingMessage.onEnd (/home/joe/Documents/vscode-projects/html-projects/swipeskins/node_modules/body-parser/node_modules/raw-body/index.js:273:7)
at IncomingMessage.emit (events.js:198:15)
at endReadableNT (_stream_readable.js:1139:12)
at processTicksAndRejections (internal/process/task_queues.js:81:17)
I guess, server has problems with parsing json data. But why? What is wrong with code?
Thanks a lot for your time, I would be very grateful to hear something from you if you have some thoughts about my situation.

Your JSON's top level data type is a string:
JSON.stringify(`<html>...</html>`);
The current version of the JSON specification allows the top level data type in the JSON text to be any JSON data type.
The original version only allowed an object or an array.
The error message says that having " as the first character is an error, which implies that it doesn't support strings as the top level data type (and thus implements the original specification).
Either change the structure of the JSON so it starts with an object or an array:
{
"html": "<html>...</html>"
}
or change the data format you are sending:
"Content-Type: text/html"
and
body: "<html>...</html>" // without JSON.stringify
Obviously, you'll need to change the server-side code to accept the changed format.

The problem is that the express JSON body parser by default only accepts inputs that can be parsed as JSON objects. However, you can change it to accept other types of valid JSON data (including strings, like in the OP) by disabling "strict" parsing:
app.use(express.json({strict: false}));
This is the workaround the worked for me.
Reference: https://github.com/expressjs/express/issues/1725#issuecomment-22844485

On request body you are not parsing a DOM element, so you can do the following:
const parser = new DOMParser();
const raw = '<html><body><div style="background-color: yellow;"><p>Hello World!</p></div></body></html>';
const body = parser.parseFromString(raw, 'text/html');
fetch(`http://localhost:4200/dist/js/server.min.js`, {
method: "POST",
// mode: 'no-cors', // If you use 'no-cors' you will not get response body and some headers
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
}).then((response) => {
console.log(response);
})

Related

How to send data from React to Node?

I am really new to node and wanted to know how to send some data from my frontend using react to my backend (Node JS).I want to send some string to my backend,is this the process or is it a completely different thing?
useEffect(() => {
fetch("/api")
.then((res) => res.json())
.then((data) => setData(data.message));
}, []);
index.js file
// server/index.js
const express = require("express");
const PORT = process.env.PORT || 3001;
const app = express();
app.get("/api", (req, res) => {
const tmp=req.body;
res.json({ message: tmp });
});
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});
Your /api route is listening to GET requests. GET requests can't contain body, therefore you won't be receiving anything inside the body.
If you want to pass data with get request, you can either use query parameters or URL parameters. Passing query params would be something like,
fetch('/api?' + new URLSearchParams({
message: 'message',
}))
To receive this from backend and use it as a response, you can access the query parameters like below using req.query,
app.get('/api', function(req, res) {
res.json({
message: req.query.message
});
});
You can also send data using URL parameters with GET request, instead of using query parameters.
I suggest taking a deeper look at HTTP requests.
you need to use post method, here is the client side using fetch api(from mdn docs):
// Example POST method implementation:
async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return response.json(); // parses JSON response into native JavaScript objects
}
postData('https://example.com/answer', { answer: 42 })
.then(data => {
console.log(data); // JSON data parsed by `data.json()` call
});
and for backend, you can handle it this way (from express docs):
const express = require("express");
const bodyParser = require("body-parser");
const router = express.Router();
const app = express();
//Here we are configuring express to use body-parser as middle-ware.
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
router.post(‘/handle’,(request,response) => {
//code to perform particular action.
//To access POST variable use req.body()methods.
const {answer} = request.body;
res.json({answer});
});
// add router in the Express app.
app.use("/", router);

Send data from JavaScript to node.js

Below is the JavaScript code. How can I send the players array to node.js?
let players = [];
for(var i=0; i<22; i++){
players.push($(".card > button").eq(i).attr("value"));
}
Below is the node.js code.
const express = require("express");
const bodyParser = require("body-parser");
const mySql = require("mySql");
const app = express();
app.use(express.static("public"));
app.use(bodyParser.urlencoded({extended: true}));
app.set('view engine', 'ejs');
app.get("/play", function(req, res){
res.render("PlayGame");
});
app.post("/play", function(req, res){
res.render("PlayGame");
});
I need to catch the players array at /play route in node.js. How can I do that?
Yes, you can send data from the browser Javascript to your node.js app. You would use an Ajax call and use either the XMLHttpRequest API or the more modern fetch() API to send the data. You would create a route in your nodejs server such as /play and then send the data with the request. Your server will then need to parse the incoming data (depending upon how it was sent) and can then act on it.
You will also have to decide if you're sending a GET, POST or PUT request (picking what is appropriate based on typical REST design and architecture). If this is starting a game and you're sending a bunch of data with it, then you would probably use a POST request and send the data as JSON in the body of the request.
In Express, here's how you'd receive that type of data:
app.use(express.json());
app.post("/play", (req, res) => {
console.log(req.body); // this would be the data sent with the request
res.send("game started");
});
In the browser, here's how you could send an array of players to your server.
fetch("/play", {
method: "POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(players)
}).then(response => {
// this line of code depends upon what type of response you're expecting
return response.text();
}).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
See the "Using Fetch" page on MDN for more info.
On the client side you would need something like this:
const postData = data => {
const body = JSON.stringify(data);
return fetch('https://your.url/play', {
method: 'POST', // GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, same-origin
cache: 'no-cache', // default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, same-origin, omit
headers: {
'Content-Type': 'application/json',
},
redirect: 'follow', // manual, follow, error
referrer: 'no-referrer', // no-referrer, client
body
})
.then(response => response.json()) // parses JSON response into native JavaScript objects
}
const players = ['a', 'b', 'c'];
postData({data: players})
.then(json => {
console.log(json);
})
.catch(e => console.log(e));
On the server side you would need something like this:
app.use(express.json());
app.post("/play", (req, res) => {
const players = req.body.data;
...
...
});

JSON is becoming empty upon hitting server code

I am sending a fetch request with a JSON payload from my webpage like this:
let raw = JSON.stringify({"name":"James","favourite":"books"})
var requestOptions = {
method: 'POST',
body: raw
};
let send = () => {
fetch("http://mywebsite.herokuapp.com/send", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
On the server side I am getting an empty body {}. Here is the code I use to monitor that:
app.post('/send', (req, res) => {
console.log(req.body)
})
When I send the exact same code generated with Postman to server — somehow everything works fine, and I get the correct JSON. Please help me understand why that is.
On the server, req.body will be empty until you have middleware that matches the content type in the POST and can then read the body from the response stream and populate req.body with the results.
// middleware to read and parse JSON bodies
app.use(express.json());
app.post('/send', (req, res) => {
console.log(req.body);
res.send("ok");
});
And, then on the client side, you have to set the matching content-type, so the server-side middleware can match the content-type and read and parse it:
const requestOptions = {
method: 'POST',
body: raw,
headers: {
'Content-Type': 'application/json'
},
};

Getting invalid JSON on API call

I'm trying to use GoToMeeting's API and making a POST request to create a meeting. At the moment, I'm just trying to hardcode the body of the meeting and send headers but I'm receiving and I'm invalid JSON error and not sure why. Here's the code for that route:
app.post('/new-meeting', (req, res) => {
const headers = {
'Content-Type': 'application/json',
Accept: 'application / json',
Authorization: 'OAuth oauth_token=' + originalToken
};
console.log('-----------------------------------------------------------')
console.log('Acess Token:');
console.log('OAuth oauth_token=' + originalToken);
console.log('-----------------------------------------------------------')
const meetingBody = {
subject: 'string',
starttime: '2018-03-20T08:15:30-05:00',
endtime: '2018-03-20T09:15:30-05:00',
passwordrequired: true,
conferencecallinfo: 'string',
timezonekey: 'string',
meetingtype: 'immediate'
};
return fetch('https://api.getgo.com/G2M/rest/meetings', {
method: 'POST',
body: meetingBody,
headers: headers
}).then(response => {
console.log('response:');
console.log(response);
response
.json()
.then(json => {
res.send(json);
console.log(req.headers);
})
.catch(err => {
console.log(err);
});
});
});
When I hit that router, I get the following error:
{
"error": {
"resource": "/rest/meetings",
"message": "invalid json"
}
}
Any advice would be appreciated!
tl;dr
You are passing fetch a value for the body represented by a JavaScript object. It is converting it to a string by (implicitly) calling its .toString() method. This doesn't give you JSON. The API you are calling then complains and tells you that it isn't JSON.
You need to convert your object to JSON using:
body: JSON.stringify(meetingBody),
Test case
This demonstrates the problem and the solution.
Server
This is designed to be a very primitive and incomplete mock of GoToMeeting's API. It just echos back the request body.
const express = require("express");
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.text({ type: "*/*" }));
app.post("/", (req, res) => {
console.log(req.body);
res.send(req.body)
});
app.listen(7070, () => console.log('Example app listening on port 7070!'))
Client
This represents your code, but with the Express server stripped out. Only the code relevant for sending the request to GoToMeeting's API is preserved.
const url = "http://localhost:7070/";
const fetch = require("node-fetch");
const headers = {
'Content-Type': 'application/json',
Accept: 'application / json',
Authorization: 'OAuth oauth_token=foobarbaz'
};
const meetingBody = {
subject: 'string',
starttime: '2018-03-20T08:15:30-05:00',
endtime: '2018-03-20T09:15:30-05:00',
passwordrequired: true,
conferencecallinfo: 'string',
timezonekey: 'string',
meetingtype: 'immediate'
};
fetch(url, {
method: 'POST',
body: meetingBody,
headers: headers
})
.then(res => res.text())
.then(body => console.log(body));
Results of running the test case
The logs of both server and client show:
[object Object]
This is what you get when you call meetingBody.toString().
If you change the code as described at the top of this answer, you get:
{"subject":"string","starttime":"2018-03-20T08:15:30-05:00","endtime":"2018-03-20T09:15:30-05:00","passwordrequired":true,"conferencecallinfo":"string","timezonekey":"string","meetingtype":"immediate"}
This is JSON, which is what the API is expecting.
Aside
MIME types do not have spaces in them. Accept: 'application / json', should be Accept: 'application/json',. This probably isn't causing you any problems though.
I believe the header is incorrect.
You need 'Accept: application/json' without space.

client (fetch) and server (nodejs http) don't understand each other?

I try to wrap my mind around nodejs at the moment.
So I've created a client:
let myHeaders = {
'Content-Type': 'application/json',
'Accept': 'application/json'
};
let myBody = {
aString: "Test"
};
fetch("http://localhost:8099/", {
method: 'post',
mode: 'no-cors',
headers: myHeaders,
body: JSON.stringify(myBody)
})
.then(result => {
return result.text();
})
.then(text => {
// do stuff with text from server
});
And I have created a server:
// request needed modules
const http = require('http');
// init server
let server = http.createServer(logic);
server.listen(8099);
// server logic
function logic (req, res) {
var body = req.body;
res.end("Hello");
}
Two problems:
1) The sever does not get the body (req.body is undefined).
UPDATE
See my answer below.
--
2) The client does not receive "Hello" (result.text() returns "").
UPDATE
2 is solved by:
Changing this on the client
fetch("http://localhost:8099/", {
method: 'post',
mode: 'no-cors', <-- CHANGE to: mode: 'cors'
...
Adding this on server
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
--
I don't get what I do wrong here...
Your Node.js code has nothing which would generate the HTML document containing the JS that calls fetch.
This means you must be making a cross-origin request (this is supported by the absolute URL you pass to fetch).
You also set mode: 'no-cors' which means "Don't throw a security exception for trying to access a cross-origin resource, and don't make the response available to JS".
Consequently, when you try to read the response: You can't.
Set the mode to "cors" and change the Node.js code to follow the CORS specification to grant permission to the page trying to read the data.
I try to wrap my mind around nodejs at the moment.
There is nothing particular to Node.js here. The problems are related to security restrictions on what JavaScript running in the browser can do unless granted permission by the HTTP server.
To not completely mess up my question, I post my solution for problem number one as separate answer:
SOLUTION 1) The sever does not get the body (req.body is undefined)
As request is a stream, I need to treat it like one (notice "req.on('data'...)
This is how the server works as expected:
// request needed modules
const http = require('http');
// init server
let server = http.createServer(handler);
server.listen(8099);
// server logic
function handler (req, res) {
// Set CORS headers
let headers = {
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods' : 'POST, OPTIONS',
'Access-Control-Allow-Headers' : 'Content-Type, Accept'
};
res.writeHead(200, headers);
if(req.method == 'POST'){
var body = '';
req.on('data', data => {
body += JSON.parse(data).aString;
});
req.on('end', () => {
res.end(body.toString().toUpperCase());
});
} else if (req.method == 'OPTIONS'){
res.end();
}
}

Categories