I'm using the native fetch library as specified here. It seems that whenever a response other than a 200 OK is returned it throws an exception with the string response Uncaught (in promise) TypeError: Failed to fetch.
Was there a way to catch and branch on specific HTTP response codes and still view the response data? For example a 401 response?
I have attached my request code I am using as a wrapper for fetch.
static request(url, data) {
let headers = {
"Authorization": window.localStorage.getItem("Authorization"),
"Content-Type": "application/json"
};
let options = {
method: "GET",
headers: headers,
mode: "no-cors",
cache: "no-cache",
};
if (data) {
options = {
method: "POST",
headers: headers,
mode: "no-cors",
cache: "no-cache",
body: JSON.stringify(data)
}
}
return new Promise(async (resolve, reject) => {
try {
let response = await fetch(url, options);
let jsonResponse = await response.json();
return resolve(jsonResponse);
} catch (error) {
// hashHistory.push("/login");
return reject(error);
}
})
}
"An accurate check for a successful fetch() would include checking that the promise resolved, then checking that the Response.ok property has a value of true. The code would look something like this (https://developer.mozilla.org/pt-BR/docs/Web/API/Fetch_API/Using_Fetch#Checking_that_the_fetch_was_successful):
fetch('flowers.jpg').then(function(response) {
if(response.ok) {
response.blob().then(function(myBlob) {
var objectURL = URL.createObjectURL(myBlob);
myImage.src = objectURL;
});
} else {
console.log('Network response was not ok.');
}
})
.catch(function(error) {
console.log('There has been a problem with your fetch operation: ' + error.message);
});
"
You can check Response Headers .status property, .text() to read Response. If Response is expected to be read more than once, you can use .clone()
let request = fetch("/path/to/resource");
request
.then(response => {
const status = response.headers.get("status");
console.log(status);
if (status == 401) {
// read 401 response
response.text().then(res = > console.log(res));
return "404.html"
}
if (status == 200) {
return "200.html"
}
})
.then(result => console.log(result))
.catch(err => // handle error);
Related
I am trying to send html table inside append of FormData object using fetch request.
Code is Correct for these
let formData = new FormData();
formData.append('sponsor', "My sponsor");
formData.append('rider', "xRider");
But When trying to send an html in it Fetch request fails
formData.append('delegation', "<b>Bold Text</b>");
My complete request
fetch(request_url, {
method: 'POST',
processData: false,
contentType: false,
body: formData
})
.then(response => {
if (response.ok) return response.json();
})
.then(response => {
let response_status = response['status'] == 'success' ? 'success' : 'failed';
// In case of OK, this sendResponse will be called
Console.log("Working correctly")
})
.catch(error => {
// In case of error, this sendResponse will be called
call_back_function({
status: "failed",
data: error
});
console.log('%c View Visa Details Catch Error: ', 'background: #000; color: #bada55', error)
});
So this is an example from an actual project I am making. I don't see all of your code, so I can't see you request, or headers. But try something like this! or post all of your code so we can properly see everything.
export const NewPost = async(endpoint, body) =>
{
const headers = new Headers();
headers.append('Content-Type', 'application/json');
const options = {
method:'POST',
body: JSON.stringify(body),
headers:headers,
};
const request = new Request(endpoint, options);
const response = await fetch(request).then((response) =>
{
if(response.ok)
return response.json();
throw new Error(`Response not received. Status: ${response.status}`);
})
.then((json) =>
{
console.log(`POST Request successful.`);
return json;
})
.catch(error => console.log(error));
return response;
};
I've the below JS code, that is returning status 201,
I'm working it with a local server, that is working correctly once I checked it at thunder client as shown:
My code is:
async function send(params) {
var path = document.querySelector('#path');
var skills = document.querySelector('#skills');
var skillsList = skills.value
skillsList = skillsList.replace(/\n\r?/g, '|');
const dataToSend = JSON.stringify({"path": path.value, "skills": skillsList});
console.log(dataToSend);
let dataReceived = "";
fetch("http://localhost:3000/getSkills", {
credentials: "same-origin",
mode: "cors",
method: "post",
headers: { "Content-Type": "application/json" },
body: dataToSend
}).then(resp => {
if (resp.status === 200) {
return resp.json();
} else {
console.log("Status: " + resp.status)
return Promise.reject("server")
}
}).then(dataJson => {
dataReceived = JSON.parse(dataJson)
console.log(`Received: ${dataReceived}`)
}).catch(err => {
if (err === "server") return
console.log(err)
})
}
Why I'm getting 201, not the returned JSON object as expected, and as shown at thunder client?
Your "thunder client" shows that a 201 response code is a success from your backend (and 2xx codes are Successful Responses according to the HTTP standard)
You explicitly state that if it's not a 200, it's a failure. Change this in your code to accept all 2xx. (window.fetch returns a Response instance, which sets the readonly attribute ok to true if the response code is 2xx MDN)
.then(resp => {
if (resp.ok) {
return resp.json();
} else {
console.log("Status: " + resp.status)
return Promise.reject("server")
}
})
201 means Created,
The request has succeeded and a new resource has been created as a result. This is typically the response sent after POST requests, or some PUT requests. MDN
What is the way to take data from getUserConnectRequestData function and pass it to getUserConnectResponseData function ?
as you can see so i try to use then and responseData to for save the data of the getUserConnectRequestData function and than i try pass it into the getUserConnectResponseData function but itd not works .
getUserConnectRequestData().then(() => {
responseData();
});
and this is getUserConnectResponseData function that i want to pass the data from getUserConnectRequestData
export const getUserConnectResponseData = (responseData) => {
return new Promise((resolve, reject) => {
// console.log('THIS IS MY RESPONSE ==============>>>>>>>>>>>', responseData);
try {
fetch(
'https://hghghgghghg3223223',
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
Req_Type: responseData.Req_Type,
Language_Code: responseData.Language_Code,
User_ID: responseData.User_ID,
Session_ID: responseData.Session_ID,
Session_Key: responseData.Session_Key,
Client_Type: responseData.Client_Type,
Req_Data: {
Bridge_ID: responseData.Bridge_ID,
},
}),
}
)
.then((response) => response.json())
.then((jsonResponse) => {
resolve(jsonResponse);
});
} catch (error) {
reject(error);
}
});
};
You need to accept the parameter and use it, and call the right function:
getUserConnectRequestData().then((responseData) => {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^
getUserConnectResponseData(responseData);
// −^^^^^^^^^^^^^^^^^^^^^^^^^^−^^^^^^^^^^^^
});
But since getUserConnectResponseData takes just that one parameter you know that the then callback will only be called with that one single argument:
getUserConnectRequestData().then(getUserConnectResponseData);
You also need to handle errors, so:
getUserConnectRequestData()
.then(getUserConnectResponseData)
.catch(error => {
// Handle/report error
});
There are a couple of other things to point out, though:
getUserConnectRequestData is falling prey to a promise anti-pattern: You don't need new Promise when you already have a promise (from fetch) to use.
You need to check for HTTP success before calling .json() on the response. Sadly, fetch only rejects on network errors, not HTTP errors.
Here's an updated version of getUserConnectRequestData:
export const getUserConnectResponseData = (responseData) => {
return fetch('https://hghghgghghg3223223', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
Req_Type: responseData.Req_Type,
Language_Code: responseData.Language_Code,
User_ID: responseData.User_ID,
Session_ID: responseData.Session_ID,
Session_Key: responseData.Session_Key,
Client_Type: responseData.Client_Type,
Req_Data: {
Bridge_ID: responseData.Bridge_ID,
},
}),
})
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
});
};
Because of that need for the check, I never use fetch directly, I have wrappers to do the check so I don't have to code it Every Single Time.
// General purpose
function fetchGeneral(...args) {
return fetch(...args)
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response;
});
}
// JSON
function fetchJSON(...args) {
return fetch(...args)
.then((response) => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
});
}
Those reject on both network and HTTP errors.
Background:
I'm trying the simple web push notifications example given in Google's documentation of the same (link: https://developers.google.com/web/fundamentals/push-notifications/subscribing-a-user)
I keep running into a SyntaxError: JSON.parse: unexpected character at line 2 column 1 of the JSON data error, which means I'm doing something fundamentally wrong. I'm a JS neophyte, can you help?
My simple function to subscribe user to push is simply:
function subscribeUserToPush() {
const pub_key = document.getElementById("notif_pub_key");
const service_worker_location = document.getElementById("sw_loc");
return navigator.serviceWorker.register(service_worker_location.value)
.then(function(registration) {
const subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(pub_key.value)
};
return registration.pushManager.subscribe(subscribeOptions);
})
.then(function(pushSubscription) {
sendSubscriptionToBackEnd(pushSubscription);
return pushSubscription;
});
}
And sendSubscriptionToBackEnd() essentially uses fetch like so:
function sendSubscriptionToBackEnd(subscription) {
const sub_obj = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'CSRFToken':get_cookie('csrftoken')
},
body: JSON.stringify(subscription)
}
return fetch('/subscription/save/', sub_obj)
.then(function(response) {
if (!response.ok) {
throw new Error('Bad status code from server.');
}
return response.json();
})
.then(function(responseData) {
if (!(responseData.data && responseData.data.success)) {
throw new Error('Bad response from server.');
}
});
}
This fails with the error SyntaxError: JSON.parse: unexpected character at line 2 column 1 of the JSON data.
Doing console.log(sub_obj) shows this object:
Object { method: "POST", headers: {…}, body: "{\"endpoint\":\"https://updates.push.services.mozilla.com/wpush/v2/gAAAAABcaErG3Zn6Urzn5Hfhpyjl0eJg_IVtcgZI-sQr5KGE0WEWt9mKjYb7YXU60wgJtj9gYusApIJnObN0Vvm7oJFRXhbehxtSFxqHLOhSt9MvbIg0tQancpNAcSZ3fWA89E-W6hu0x4dqzqnxqP9KeQ42MYZnelO_IK7Ao1cWlJ41w8wZSlc\",\"keys\":{\"auth\":\"AJfXcUMO3ciEZL1DdD2AbA\",\"p256dh\":\"BN84oKD3-vFqlJnLU4IY7qgmPeSG96un-DttKZnSJhrFMWwLrH2j1a0tTB_QLoq5oLCAQql6hLDJ1W4hgnFQQUs\"}}" }
Also doing console.log(response); right before return response.json(); displays:
Response { type: "basic", url: "http://127.0.0.1:8001/subscription/save/", redirected: false, status: 200, ok: true, statusText: "OK", headers: Headers, body: ReadableStream, bodyUsed: false }
What's the problem and how do I fix it?
Changing return response.json() to return response.text() and then doing a console log on responseData gives the entire HTML of the page. I end up with the error Error: Bad response from server.
The main issue was that CSRFToken was mislabeled when being set in sendSubscriptionToBackEnd. It should have been X-CSRFToken. I.e.
function sendSubscriptionToBackEnd(subscription) {
const sub_obj = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken':get_cookie('csrftoken'),
},
body: JSON.stringify(subscription)
}
return fetch('/subscription/save/', sub_obj)
.then(function(response) {
console.log(response);
if (!response.ok) {
throw new Error('Bad status code from server.');
}
return response.json();
})
.then(function(responseData) {
// response from the server
console.log(responseData)
if (!(responseData.data && responseData.data.success)) {
throw new Error('Bad response from server.');
}
});
}
So why was this leading to return response.json(); failing?
Because the project in question routes requests that fail csrf checks to a default view - one which doesn't return a json response at all. Mystery solved!
I am using the fetch API with a REST API, and i want to handle certain errors globally, errors like 500, 503 ... I tried doing something like this
function(url, method, data) {
return fetch(url, {
method: method || "GET",
body: JSON.stringify(data),
mode: "cors",
headers: {
"Content-Type": "application/json; charset=utf-8"
}
}).then(response => {
if (response.ok && response.status < 500) console.log("ouch");;
return response;
});
but it doesn't seem to be working. how do i catch 500, 503 ... in the fetch api?
Try this approach in this way you can handle all possible errors at one place and you also can generate custom response to return e.g if your all requests return JSON data then you can convert response to JSON before returning it.
function secureFetch(url, method, data) {
return new Promise((resolve, reject) => {
fetch(url, {
method: method || "GET",
body: JSON.stringify(data),
mode: "cors",
headers: {
"Content-Type": "application/json; charset=utf-8"
}
}).then(response => {
// response only can be ok in range of 2XX
if (response.ok) {
// you can call response.json() here too if you want to return json
resolve(response);
} else {
//handle errors in the way you want to
switch (response.status) {
case 404:
console.log('Object not found');
break;
case 500:
console.log('Internal server error');
break;
default:
console.log('Some error occured');
break;
}
//here you also can thorow custom error too
reject(response);
}
})
.catch(error => {
//it will be invoked mostly for network errors
//do what ever you want to do with error here
console.log(error);
reject(error);
});
});
}
secureFetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(json => console.log(json))
.catch(error => console.log(error));
secureFetch('https://jsonplaceholder.typicode.com/posts/100000000')
.then(response => response.json())
.then(json => console.log(json))
.catch(error => console.log(error));
Here you can handle with promise something like this:
function status(response) {
/*You can handle status here.*/
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response.statusText))
}
}
function json(response) {
return response.json()
}
function makeCall(url, method, data) {
return fetch(url, {
method: method || "GET",
body: JSON.stringify(data),
mode: "cors",
headers: {
"Content-Type": "application/json; charset=utf-8"
}
})
.then(status)
.then(json)
.then(function(data) {
console.log('Request succeeded with JSON response', data);
}).catch(function(error) {
console.log('Request failed', error);
});
}
var url = 'http://localhost:4200/profile';
var data = {username: 'example'};
makeCall(url, 'POST', data);