XMLHttpRequest successful while fetch is blocked - javascript

I am trying to access an API from the front-end and i tried both xhr and fetch api requests.
When using fetch, i received the error "Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:5500' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."
However, when using XHR, I do not receive a headers not present warning and it sucessfully fetches the JSON from the api.
I don't quite understand CORS, but my understanding is that there was no header present on the api I was requesting and therefore could not fetch the API. BUT, how was xhr able to fetch the api with the assumed lack of headers on the API? How was the request possible in an XMLHTTPRequest but not a fetch request? How would I use fetch to fetch this API? I have included both my fetch and XHR code below for reference.
Fetch Code:
fetch(requestURL, {
headers: {
"Content-Type": "application/json",
'Authorisation': 'Basic' + apiKeySecured,
"Access-Control-Allow-Origin": "*", // Required for CORS support to work
"Access-Control-Allow-Credentials": false,
"api-key": apiKeySecured,
}
}).then(function (response) {
return response.json();
})
.then(function (myJson) {
console.log(JSON.stringify(myJson));
})
.catch(error => console.error(error));
XHR code:
var xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === this.DONE) {
console.log(this.responseText);
}
});
xhr.open("GET", requestURL);
xhr.setRequestHeader(`api-key`, apiKeySecured);
xhr.send();

You need to set the mode option for fetch.
From the docs:
// Example POST method implementation:
postData(`http://example.com/answer`, {answer: 42})
.then(data => console.log(JSON.stringify(data))) // JSON-string from `response.json()` call
.catch(error => console.error(error));
function postData(url = ``, data = {}) {
// Default options are marked with *
return 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; charset=utf-8",
// "Content-Type": "application/x-www-form-urlencoded",
},
redirect: "follow", // manual, *follow, error
referrer: "no-referrer", // no-referrer, *client
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
.then(response => response.json()); // parses response to JSON
}

What is triggering the pre-flight in your original code?
Every single one of those extra headers you added
"Content-Type": "application/json",
'Authorisation': 'Basic' + apiKeySecured,
"Access-Control-Allow-Origin": "*", // Required for CORS support to work
"Access-Control-Allow-Credentials": false,
Not one of these was added to XHR, so don't add it to fetch
Your fetch should be
fetch(requestURL, {
headers: {
// remove all those random headers you added
"api-key": apiKeySecured
},
mode: 'cors' // add this
}).then(response => response.json())
.then(function (myJson) {
console.log(JSON.stringify(myJson));
})
.catch(error => console.error(error));
Now it is equivalent to your XHR code

Related

Content type 'text/plain;charset=UTF-8' not supported in fetch js ti api spring boot

I'm trying to send an http request using fetch to my backend but it's returning this error even though I'm sending an application/json header,
the content that needs to reach the api is a json
front-end code
let user_ = 'teste';
let email_ = 'teste#email.com';
let pass_ = 'teste';
let button_submit = document.getElementById('mySubmit_signup');
let headers = new Headers();
headers.append('Accept', 'application/json');
headers.append('Content-Type', 'application/json');
headers.append('Access-Control-Allow-Origin', '*');
headers.append('Access-Control-Allow-Credentials', 'true');
button_submit.addEventListener('click', async function(){
try {
await fetch('http://localhost:8080/users', {
mode: 'cors',
method: 'POST',
body: JSON.stringify({
name: user_,
email: email_,
password: pass_
}),
})
.then(
response => response.json()
)
.then(
data => console.log(data)
)
} catch (error) {
console.log(error);
}
});
ATT:
i add header but i receive "Access to fetch at 'http://localhost:8080/users' from origin 'http://localhost:7777' 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."
try {
await fetch('http://localhost:8080/users', {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({
name: "user_",
email: "mail_#email.com",
password: "pass_"
}),
})
.then(
response => response.json()
)
.then(
data => console.log(data)
)
} catch (error) {
console.log(error);
}
};
req();
You are passing a string to body. This means fetch will automatically add a Content-Type request header saying that you are sending plain text.
Your server side code is expecting JSON and is rejecting your so-called plain text with an error.
You need to explicitly add the correct header to say that you are sending JSON.
headers: {
'Content-Type': 'application/json',
},
Aside Do not add 'Access-Control-Allow-Origin': '*', to your request headers. This is a response header and has no business being on the request. Adding it will create additional problems.
Adding an application/json content type to the request will make it preflighted.
You quoted an error mentioning a preflight request. The error state may have been caused by your incorrect extra header (see the aside above) or it might be that your server isn’t set up to support any preflight request.
If the latter then you need to adjust the server-side code to response to the preflight request (which is OPTIONS rather than POST) to give permission to the browser to make the POST request you want to make.

API fetch is not responding

I was working on a React APP which fetches data from https://restcountries.com/v2/all and now I have an error.
useEffect(() => {
fetch(`https://restcountries.com/v2/all`)
.then((r) => r.json())
.then((data) => {
if (data !== undefined) {
setCountries(data);
} else {
alert('Can´t Load Data');
}
});
}, []);
**
use this format with header
** ##
// Example POST method implementation:
async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {`enter code here`
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
}
}, []);
You are getting a CORS error which means target domain of api (restcountries) does not allow other domains to fetch data.
The solution to this problem is a server side browser or headless browser. Like selenium and puppeteer
https://www.selenium.dev/
https://github.com/puppeteer
But i have tested and api is giving me data in browser with same fetch code. I cant reproduce the problem. Its an issue with something else
this is happening due to multiple reason like due to authentication or your are not sending token in request header second due to server down or may be your are passing wrong param to request third one my be this endpoint can me access by only specific domains url.

Sending GET data via fetch() in body instead of specifying it in the URL

I am trying to convert this jQuery call to native Javascript using fetch() as mentioned in MDN (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Supplying_request_options).
$.ajax(
{
method: "GET",
url: CB_ABS_URI + "ajax/get-checkin.php",
dataType: "json",
data: { DwellingUnitID:DwellingUnitID },
})
to
// Example POST method implementation:
async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: 'GET', // *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, *client
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return await response.json(); // parses JSON response into native JavaScript objects
}
postData(CB_ABS_URI + "ajax/get-checkin.php", { DwellingUnitID: DwellingUnitID })
.then((data) => {
console.log(data); // JSON data parsed by `response.json()` call
});
But I can't seem to send GET data in the body. Is adding the query to ajax/get-checkin.php the only way ?
But I can't seem to send GET data in the body
fetch makes a clear distinction between the query string in the URL and the data in the request body (unlike jQuery which switches between them depending on the request method).
Is adding the query to ajax/get-checkin.php the only way ?
Yes, see the documentation:
If you want to work with URL query parameters:
var url = new URL("https://geo.example.org/api"),
params = {lat:35.696233, long:139.570431}
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
fetch(url).then(/* … */)

How to use fetch with Authorization Header

I'm quite new to using fetch and I dont know if I'm doing this right since it works fairly well with POST. If I use the normal fetch method, and not the function I made, the server responds with the data. But if I use this, the data becomes undefined. Any ideas on how I can fix this?
network_requests.js
export const getData = (url, data) => {
return fetch(url, {
body: JSON.stringify(data), // must match 'Content-Type' header
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'include', // include, same-origin, *omit
headers: {
'user-agent': 'Mozilla/4.0 MDN Example',
'content-type': 'application/json',
'Authorization': 'Token '+ localStorage.token
},
method: 'GET', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, cors, *same-origin
redirect: 'follow', // manual, *follow, error
referrer: 'no-referrer', // *client, no-referrer
})
.then(response => response.json()); // parses response to JSON
};
sample usage: (doesnt work)
fetchDrivers() {
return getData('/members/drivers').then(data => {
if (!data["error"]) {
//for each entry in drivers data, append data as a dictionary in tableData
//ant tables accept values {"key": value, "column_name" : "value" } format
//I cant just pass the raw array since its a collection of objects
const tableData = [];
//append drivers with their ids as key
data["drivers"].forEach(item => tableData.push({
"key": item.id,
"name": item.name
}));
this.setState({drivers: tableData});
} else {
console.log(data["error"]);
}
});
}

javascript fetch is not posting to rest api endpoint while postman is doing

I have a rest api endpoint and I am checking it using POSTMAN which is posting correctly. But, when I am doing it using JAVASCRIPT FETCH, I am not able to post it. Below is my code for fetch:
const { inputAOI, wktForCreation } = this.state
fetch('http://192.168.1.127:8080/aoi/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ userName: 'fuseim', aoiName: inputAOI, wkt: wktForCreation }),
mode: 'no-cors'
}).then(function (response) {
if (response.ok) {
return response.json()
} else {
throw new Error('Could not reach the API: ' + response.statusText)
}
}).then(function (data) {
console.log({ data })
}).catch(function (error) {
console.log({ error })
})
Below is the image for the request headers.
It is seen in the above image that in Request Headers, the Content-Type is still text/plain but I am sending application/json as shown in above fetch code.
Check the response preview in console.
Below is correct POSTMAN request:
As hinted in the comments, the problem is with the mode:"no-cors"
Content-Type is considered a simple header, and should be allowed without cors, but only with the following values:
application/x-www-form-urlencoded
multipart/form-data
text/plain
See: https://fetch.spec.whatwg.org/#simple-header
If you are running the API on the same host/port as the script, you should use mode: "same-origin" alternatively add the host/port that the script is running on as an allowed origin on the API.
For more information about CORS: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Instead of
headers: {
'Content-Type': 'application/json'
}
you could try:
headers: new Headers({
'Content-Type': 'application/json'
})

Categories