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

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();
}
}

Related

I am not getting any CORS error on NodeJS (Works Fine with Node) but I am getting the error on React and Javascript while fetching API

NodeJs Code:
const express = require('express');
const port = 3000;
const router = express();
router.get('/', (req, res) => {
res.send('Hi');
})
var request = require('request');
var options = {
'method': 'GET',
'url': 'URL',
'headers': {
'Authorization': 'API_KEY'
}
};
request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body);
});
router.listen(port, function(err) {
if(err) return;
console.log('Server Up');
})
JavaScript Code:
const options = {
method: 'GET',
headers: {
'Authorization': 'API_KEY'
}
};
fetch('URL', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
Error:
has been blocked by CORS policy: Response to preflight request doesn't
pass access control check: No 'Access-Control-Allow-Origin' header is
present on the requested resource. If an opaque response serves your
needs, set the request's mode to 'no-cors' to fetch the resource with
CORS disabled.
Am I missing a Header in JS or is the syntax wrong?
Note: The API I call to Get request is not my own.
This may solve your problem:
Install cors and add the following line to the node server file:
router.use(cors())
If it doesn't work, remove any headers config and try again.

SyntaxError: Unexpected token " in JSON at position 0

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);
})

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;
...
...
});

HTTP Post request not sending body or param data from ionic

HTTP post request is not sending body or param data to the server
Forgive me if this turns out to be a duplicate question. I've looked at several similar questions on stack overflow, but none of them have solved my problem. Also tried using a GET request instead of a POST request, but body data is still not sending.
Client side code:
// ionic code
homeUrl: string = 'http://localhost:80';
let obj = {"name": "Guest"};
let response = this.httpClient.post(this.homeUrl + '/admin-signup', JSON.stringify(obj));
response.subscribe(data => {
console.log('response: ', data);
//TODO: handle HTTP errors
});
Server side code:
server.post('/admin-signup', (req, res) => {
console.log('sign')
console.log(req.body);
// TODO: Process request
res
.status(200)
.send(JSON.parse('{"message": "Hello, signup!"}'))
.end();
});
First of all, import http client
import { HttpClient, HttpHeaders } from '#angular/common/http';
Then do the following
const header = new HttpHeaders({
'Content-Type': 'application/json',
Accept: 'application/json'
//api token (if need)
});
const options = {
headers: header
}
let response = this.httpClient.post(this.homeUrl + '/admin-signup', obj, options);
response.toPromise().then(data => {
console.log('response: ', data);
//TODO: handle HTTP errors
}).catch((err) =>{
console.log('error', err);
});
Hope it solve your problem.
I'm not familiar with ionic
but I'm guessing its a cors issue
can you try use cors?
const cors = require('cors');
app.use(cors());

Axios HTTP requests returns into an error (Access-Control-Allow-Origin)

I'm trying to make http post requests with Axios in JavaScript. The request was working fine, but then I tried to use cookies. As my backend I'm using an Express/Nodejs Server on http://localhost:8000, while my frontend is a react npm test server on http://localhost:3000.
My backend looks like this:
const express = require('express');
const cookieparser = require('cookie-parser');
const cors = require('cors');
const app = express();
app.use(cookieparser());
app.use(cors());
app.post("/request/status/check", (req, res) => {
if(req.cookies.gitEmployee != null){
res.status(200).send({res: 1, employeeName: req.cookies.gitEmployee.username, fullname: req.cookies.gitEmployee.fullname});
} else if(req.cookies.gitCompany != null){
res.status(200).send({res: 2, companyName: req.cookies.gitCompany.companyName, fullname: req.cookies.gitCompany.fullname});
}else{
res.status(200).send({res: 0});
}
});
app.post("/request/testcookie", (req, res) => {
res.cookie("gitEmployee", null);
res.cookie("gitEmployee", {
username: "testusername",
fullname: "Test Username"
}).send({res: 1});
});
So, as a short description: I'm setting a test cookie by posting a request to http://localhost:8000/request/testcookie. The response should be an JSON object where res = 1. Also, I'm trying to get information out of the cookie by posting a request to http://localhost:8000/request/status/check. In this case the response should be the object {res:1 , employeeName: "testusername", fullname: "Test Username"}.
I tried this concept with a REST Client called Insomnia (something like Postman) and it worked perfectly.
Then I wrote a helper-class for my React Application and for the Http request I'm using Axios.
import axios from 'axios';
class manageMongo {
authstate(){
return new Promise((resolve, reject) => {
axios("http://localhost:8000/request/status/check", {
method: "post",
data: null,
headers: {
"Access-Control-Allow-Origin": "*"
},
withCredentials: true
})
.then(res => {
console.log(res.data);
if(res.data.res === 0){
resolve(false);
}
if(res.data.res === 1){
resolve(true);
}
if(res.data.res === 2){
resolve(true);
}
});
});
}
setTestCookie(){
axios("http://localhost:8000/request/testcookie", {
method: "post",
data: null,
headers: {"Access-Control-Allow-Origin": "*"},
withCredentials: true
})
.then(res => { console.log(res)});
}
}
export default manageMongo.prototype;
When I execute these functions, I'm getting the same error of both of them (of course with different urls):
Failed to load http://localhost:8000/request/testcookie: Response to
preflight request doesn't pass access control check: The value of the
'Access-Control-Allow-Origin' header in the response must not be the
wildcard '*' when the request's credentials mode is 'include'
I already know that it's because of the withCredentials setting in the requests. I added these settings because I want to pass cookies through these requests and if I don't add withCredentials, the /request/status/check request always returns {res: 0} even if I set a cookie before.
I don't know, if this will change if the I set withCredentials = true but i read that in multiple threads. If you know an other working method to pass cookies through these requests even without axios please share it here! Because that is, what I want to achieve.
The problem seems to be you have set
'Access-Control-Allow-Origin': *
Try setting it to your actual origin, for example
'Access-Control-Allow-Origin': 'http://localhost:8000'
or
'Access-Control-Allow-Origin': 'http://localhost:3000'
Whichever the request originates from.

Categories