Is there a way to hook into all of the fetch API (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) ajax call errors similar to jquery's $(document).ajaxError()?
We're using $(document).ajaxError() to detect Session Timeout/failures and present a Login dialog. I would like to detect timeouts as soon as I get a failed response.
Here's some code:
$(document).ajaxError((event, jqxhr) => {
if (jqxhr.statusText === 'abort') {
return;
}
// Sometimes (even with /restapi/session), we get no jqxhr.responseJSON, but we do have jqxhr.responseText
let responseJSON = jqxhr.responseJSON;
if (!responseJSON && jqxhr.responseText) {
responseJSON = JSON.parse(jqxhr.responseText);
}
if (jqxhr.status === 401) {
if (!responseJSON) {
// https://sentry.zetta.net//zetta/prod-frontend/group/13631/
alert('API call\'s responseJSON is empty, and so is jqxhr.responseText. Check the Console logs for more info.');
console.error('jqxhr:', jqxhr, 'event:', event);
}
if (responseJSON.code === 1900) { // session expired
this.setState({ showExpirationModal: true });
} else {
// TODO: consider removing handling of these errors locally
// Currently we'd be showing 2 popups in some places: one with a "permission denied", and another with this error handler
// alert('Unauthorized');
}
} else if (jqxhr.status === 500) { // TODO: see if we should to all 500 errors in this global handler
if (responseJSON.code === 8017) { // maintenance mode
alert(responseJSON.message);
}
} else if (!navigator.onLine) {
this.setState({ showConnectionModal: true });
} else if (responseJSON === undefined) {
console.error(event, jqxhr);
}
}
I'm not sure if the Fetch API supports this.
From the document linked at OP
Note that the fetch specification differs from jQuery.ajax() in
mainly two ways that bear keeping in mind:
The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will
resolve normally (with ok status set to false), and it will only
reject on network failure or if anything prevented the request from
completing.
To get the status code, return Response.status
fetch("/path/to/server"/*, options object*/)
.then(response => ({headers: response.headers, code: response.status}))
.then(({headers, code}) => {
for (let [key, value] of headers) {
console.log(key, value)
}
console.log(code); // `403` at linked jsfiddle
// if (code === /* code */)
// do stuff
})
.catch(err => console.log(err));
jsfiddle https://jsfiddle.net/wgwd9hk3/
Related
Why this url fetch isn't working?
Actually this GET method is passing some error message to be stored:
fetch('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', {
method: 'get'
}).then(function(response) {
}).catch(function(err) {
// Error :(
});
However if I typein same URL ( http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha ) in browser it works.
Some other places in WebExension it's working properly.
But not working in another place. Also when I enter in Firefox console too it does not work. It shows some "pending.."
This function too is showing the same behavior:
function ff_httpGetAsync(theUrl, callback, failed_cb) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
// console.log("Successfully downloaded the ajax page");
if (callback) {
if (xmlHttp.responseURL == theUrl) {
callback(xmlHttp.response);
} else {
console.log("diff response url received" + xmlHttp.responseURL);
}
}
} else {
// console.log("Got status =", xmlHttp.status);
}
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
console.log("Gettiy :" + theUrl);
xmlHttp.send(null);
}
ff_httpGetAsync('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', function() {
}, function() {});
I've checked the server. In this case backend pushlo90.php isn't getting called.
Not sure what is wrong with my URL?
That result tells you the promise isn't answered yet. It might work in some occasions when the promise is handled very quickly, before the page is rendered.
Using a promise you basically say 'promise me you will do this'. This promise is either resolved or rejected. Before it's resolved or rejected, it's always pending.
Adding some logging in your first function should explain.
fetch('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', {
method: 'get'
}).then(function(response) {
console.log(response) //do something with response data the promise gives as result
}).catch(function(err) {
console.log(err)// Error :(
});
If you don't want to use the .then(), use async/await.
const functionName = async () => {
const result = await fetch(
"http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha",
{
method: "get"
}
);
console.log(result); //this will only be done after the await section, since the function is defined as async
};
functionName();
The fetch function return a promise that when resolved returns a HTTP response. You then can access the HTTP response Example:
fetch(`https://baconipsum.com/api/?type=all-meat¶s=2&start-with-lorem=1`)
.then(response => {
// HTTP response which needs to be parsed
return response.json()
})
// accessing the json
.then(json =>console.log(json))
So the question you have to ask yourself is: what is returned from the call??
Also be aware that 404 and other HTML error codes wont lead to a reject of the promise so don't bother with catch.
For more details see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
So, the code block that is shown in your question is -
fetch('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', {
method: 'get'
}).then(function(response) {
}).catch(function(err) {
// Error :(
});
So, what it says is fetch module will send a GET request to the URL provided in the request and the response or the error will go into the respective chained functions.
The error could be 404(Not found) or 401(Unauthorized) etc.
To check the error put some logging into your HTTP request handlers.
fetch('http://www.govtschemes.in/pushlo90.php?msg=alert-bid:0.120148336001477231576473857578-Please%20enter%20correct%20captcha', {
method: 'get'
}).then(function(response) {
console.log(`Response is {response}`)
}).catch(function(err) {
console.log(`Error is {err}`)
})
And for your other code here is the screenshot of what is getting returned from your code -
Where it clearly states 404 (Not found), hence your code will go in the error handler.
I'm new to service-worker. I'm following a training of Mobile Web Specialist given by Udacity and I'm using google-chrome for that.
I want to fetch for a response from the network, and if it returns 404 as a status I fetch for another response from the network as well.
This is a code to fetch from the network only once. This code works perfectly:
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).then(function(response) {
if (response.status === 404) {
return new Response("Whoops, not found");
}
return response;
}).catch(function() {
return new Response("Uh oh, that totally failed!");
})
);
});
I did some updates on this code by throwing an error after getting response.status === 404 and manage it the same way in a try/catch. The updated code is below:
self.addEventListener('fetch', function(event) {
try {
event.respondWith(
fetch(event.request).then(function(response) {
if (response.status === 404) {
throw (Error);
}
return response;
}).catch(function() {
return new Response("Uh oh, that totally failed!");
})
);
} catch (Error) {
event.respondWith(
fetch('/imgs/dr-evil.gif').then(function(response) {
if (response.status === 404) {
return new Response('couldn\'t fetch twice');
}
return response;
}).catch(function() {
return new Response("Uh oh, that totally failed twice!");
})
);
}
});
I know there is a better way to do a nested fetch using the service-worker, but I want to know what I did wrong here.
I've not run this so it's possible it needs some adjustments, but try something like this. The problem with your current code is that the first fetch promise chain always resolves to a Response. Either in the first then or in the first catch, where you return a response of "Uh oh, that totally failed!". The event.respondWith takes that response and happily goes along it's way.
The outer try/catch exists in a synchronous space, where as the fetch kicks off an asynchronous chain, so there will be no way for your code to reach the outer catch since it's not in the execution context for the fetch.
If the compatability is the same for both service worker and async/await (I don't know) you might want to take a look at that as it would be a much friendlier way to structure your code.
self.addEventListener('fetch', function(event) {
event.respondWith(
fetch(event.request).then(function(response) {
if (response.status === 404) {
throw (Error);
}
return response;
}).catch(function() {
return fetch('/imgs/dr-evil.gif').then(function(response) {
if (response.status === 404) {
throw (Error);
}
return response;
})
}).catch(function() {
return new Response("Uh oh, that totally failed twice!");
})
);
});
In my ASP.NET Core API backend, I send Status 204 when there's no data but I noticed in the front end, my fetch call is still showing response.ok.
Two questions:
Is this normal behavior? I guess, it was a successful call so response could be OK, but it just threw me off.
What's the best way to check for Status 204?
My current code in my React/Redux app looks like this:
export const apiCall = () => {
return (dispatch) => fetch("/api/get", fetchOptions)
.then((response) => {
if(response.ok) {
// Do something
} else {
// Couldn't get data!
}
})
};
This is my standard code block in handling fetch calls. How should I modify it to handle Status 204 scenarios?
In addition to checking Response.ok, you can also check Response.status. Per MDN:
The status read-only property of the Response interface contains the status code of the response (e.g., 200 for a success).
Response.ok is only a check to see if the the status property is 200-299.
So, rather than just checking ok, you could do:
if (response.status === 200) {
// Do something
} else if (response.status === 204) {
// No data!
} else {
// Other problem!
}
I tried to handle the Network related issues in HTTP ajax call. So, I temporarily stopped the respective API's service in IIS and I tried to call the shut downed API - http://localhost:1000/GetData.
fetch("http://localhost:1000/GetData")
.then(handleErrors)
.then(function(response) {
return response.Json();
}).catch(function(error) {
console.log(error);
});
I tried the following code too
fetch("http://localhost:1000/GetData")
.then(response => {
if(response) {
if (response.status === 200) {
alert('Super');
return response.json();
} else {
alert('Hai');
return '';
}
} else {
alert('Oooops');
return '';
}
})
.catch(function(error) {
console.log(error);
});
But its failing and directly hitting the catch block without triggering any alert and its throwing an error. Moreover the response.json(); is in Success block, I don't know how its executed.
TypeError: response.json is not a function
Stack trace:
onFetchError/<#http://192.168.4.159:3000/app.0df2d27323cbbeada2cd.js:9946:13
Kindly assist me how to check the Status code and how to handle the Network error (i.e., Network Unavailable 404, etc.,)
Referred website: https://www.tjvantoll.com/2015/09/13/fetch-and-errors/
Based on this issue on Github, you can try to identify error types in catch block instead. So, something like this may work for your case:
fetch("http://localhost:1000/GetData")
.then(response => {
alert("Super");
return response.json();
})
.catch(err => {
const errStatus = err.response ? err.response.status : 500;
if (errStatus === 404){
// do something
} else {
// do another thing
}
});
Currently, I'm using fetch with redux-thunk to read code from an API -
my code reads like this:
export function getUsers() {
return (dispatch) => {
// I have some helper code that automatically resolves the json promise
return fetch(`/users`)
.then((resp, json) => {
if (resp.status === 200) {
dispatch(getUsersSuccess(json));
} else {
dispatch(getUsersFail(json));
}
}).catch((err) => {
// network error
dispatch(getUsersFail(err));
});
};
}
The problem here is the catch method, as it will catch any error thrown in the then block. This commonly means that if some React component's render function fails with a programmer error, that error gets swallowed up back into dispatch(getUsersFail(err)).
Ideally, I'd like to detect if err is a fetch error (and dispatch my own action), otherwise throw. However, fetch throws a generic TypeError. How can I reliably detect that the error caught was one thrown by fetch?
Don't use .catch() but install the error handler directly on the fetch promise, as the second - onreject - argument to .then():
return fetch(`/users`)
.then(([resp, json]) => {
if (resp.status === 200) {
dispatch(getUsersSuccess(json));
} else {
dispatch(getUsersFail(json));
}
}, (err) => {
// network error
dispatch(getUsersFail(err));
});
Check out the difference between .then(…).catch(…) and .then(…, …) for details.
Btw, I'd recommend to write
return fetch(`/users`)
.then(([resp, json]) => resp.status === 200 ? getUsersSuccess(json) : getUsersFail(json)
, getUsersFail)
.then(dispatch);