How to handle error status in ES6 Promises? - javascript

I am using ES6 and trying to handle error status of the fetch() result.
I know I can catch error like this:
fetch(`http://${this.serverAddress}/reset`)
.then((resp) => {
Logger.debug(JSON.stringify(resp));
})
.catch((err) => {
Logger.debug(JSON.stringify(err));
});
In catch block I take an Error with message "Failed to fetch". There is no status information.
I have found the following suggestion:
fetch('/some/url/')
.then(processResponse)
.then(response => {
// do something
})
.catch(response => {
// repsonses with status >= 400 get rejected. you can access response.status and response.data here too
if (response.status === 400) {
// handle form validation errors, response.data.errors...
} else if (response.status === 403) {
// handle permission errors
} // etc
});
But response.status is undefined in catch block.
How can I get status code of the error to handle it?
UPDATE:
Thank you for the responces. Please see my updates:
My actual token looks as http://api.service/v2/prices/latest?token=err. If I write my token instead of err, then it works. When I try this URL, my code go to the catch block, not then.
I noticed the following error in browser console:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 401. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
If I add {mode: "no-cors"}, code go to the then, but status is always 0. This is not that I want.
I tried to add
mode: "cors",
headers: {
"Access-Control-Allow-Credentials": true,
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json"
}
This is not help, I still have No 'Access-Control-Allow-Origin' header error.

You may try to handle the error status by throwing error during response processing right after the fetch is done with !ok:
fetch('/some/url/')
.then(response => {
if (!response.ok) {
throw Error({ status: response.status });
}
response.json();
})
.then(response => {
// do something
})
.catch(error => {
switch(error.status) {
// ...
}
});

You can't check the status in code.
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.
Basically, fetch() will only reject a promise if a networking error occurs.
The fetch API provides a simple ok flag that indicates whether an HTTP response’s status code is in the successful range or not. See the below example
fetch("http://httpstat.us/500")
.then(function(res) {
if (!res.ok) {
throw Error(res.statusText);
}
return res;
}).then(function(res) {
console.log("ok");
}).catch(function(error) {
console.log(error);
});

After the fetch request check if response is as expected. If not, throw new error with a custom message. In the catch check if this custom message is there for the thrown error. Handle accordingly.
You can also create custom Errors and check the same.

As seen in the docs the catch block will be reached only if the request didn't complete. When there is a status code available (and all the other data is received as well) fetch will resolve and therefore the then block will be reached.
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. - Source
So all you have to do is logging resp.status instead of resp.
fetch(`http://${this.serverAddress}/reset`)
.then((resp) => {
Logger.debug(JSON.stringify(resp.status));
})
.catch((err) => {
Logger.debug(JSON.stringify(err));
});
In catch block I take an Error with message "Failed to fetch". There is no status information.
So in case the catch block is getting called the request didn't even finish. This might be caused because you are not using options like method, headers,... If needed, which one and what values depends on the backend you are using.

Related

How can I get my fetch error to show an http status code?

I have a react component and I'm making a network call to set the state. Eventually I want to pass this down to other child components, but just getting the plumbing to work at the moment.
I'm trying to catch errors correctly when calling out to my backend (an express server in the app). I attempted to force an error by fetching data from an endpoint that doesn't exist. This should throw a 404 since it doesn't exist, right? How can I get that error surfaced in the catch statement? Right now my error is SyntaxError: Unexpected token < in JSON at position 0 at eval (app.js:61)
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
data: null
};
}
componentDidMount() {
fetch('/api/wrong_endpoint').then((data) => {
return data.json();
}).then((body) => {
this.setState({data: body})
}).catch(err => console.log(err));
}
render() {
console.log('logging the states');
console.log(this.state.data);
return (
<div>
<ContactList />
<ContactDetail />
<AddContactModal />
</div>
);
}
}
I'll try to go step by step
fetch method doesn't throw an error even if you get the 4xx or 5xx response codes. Please read about the Fetch API carefully, I believe you can find a lot of interesting you don't know about it.
You can easily check the response status as follows (please read about the Response object and its methods/properties):
fetch('/api/wrong_endpoint').then((response) => {
console.log('status code', response.status)
})
It's hard to say if your server really returns 404 code because I don't know your express setup. If you set some fallback handler like app.get('*', ...) then it might as well return 200 success code. You can check the response status and its body in devTools of the browser. But I believe it's better if you configure at least your /api router to return 404 error if the requested /api/... route isn't found.
What I'm really sure of is that your server returns some HTML layout in the response. And you try to parse it as JSON string via data.json() and of course you get the syntax error since it's not JSON (html layout starts with < symbol hence the error: SyntaxError: Unexpected token <)
Generally, if you are using the fetch API, errors 40x and 50x will not go into the subsequent blocks, as the promise from fetch only rejects network errors (not HTTP errors or anything else). Therefore, requesting for data from an 'incorrect' endpoint will be handled within the first then block.
I would recommend you to use check your http response body based on the Response.Ok property. Successful responses will be handled within that condition, whereas any other responses (ok: false) will be handled on the other statement.
fetch('/api/wrong_endpoint')
.then(response => {
console.log(response) // full response body
console.log(response.status); // get only the response.status
if (!response.ok) {
// http errors 40x and 50x will go into this statement
// do something to handle it
} else if (response.ok) {
// handles status code 200
}
})
.then(
// ...

HTTP request error code 429 cannot be caught

Using fetchApi I am issuing a POST request to an endpoint and catching errors like this:
fetch(new Request(url, {
method: 'POST',
headers: { ...defaultHeaders, ...headers } ,
credentials: 'include',
body: JSON.stringify(body)
})).then(
// handle correct reponse
).catch(
if (err.status === 401) {
// handle error nicely
} else if (err.status === 429) {
// handle error differently
} else {
// handle error yet another way
}
);
While 401 error is handled as expected, when the endpoint responds with 429 the code
else if (err.status === 429) {
// handle error differently
}
Is never executed.
EDIT1: the entire catch is never reached in case of 429
Are 401 and 429 status codes handled differently by javascript/browsers?
How Can I catch 429 error and handle it my way?
Looks mostly like wrong expectations. The promise will only be rejected (and hence catch invoked) when the response's type is "error", which only seems to be the case for very specific, limited cases.
Per MDN, the fetch() API only rejects a promise when a “network error is encountered, although this usually means permissions issues or similar.” Basically fetch() will only reject a promise if the user is offline, or some unlikely networking error occurs, such a DNS lookup failure.
In other words, a request with a 429 response is still a successfully executed request.
The good is news is fetch provides a simple ok flag that indicates whether an HTTP response’s status code is in the successful range or not.
fetch("http://httpstat.us/500")
.then(function(response) {
if (!response.ok) {
throw Error(response.statusText);
}
})
https://www.tjvantoll.com/2015/09/13/fetch-and-errors/

How to catch the server response, even if 400 bad request error occured?

Consider following code. It's just a simple http post request with axios library.
axios.post('http://localhost/users', this.state)
.then(function(response) {
if (response.status == 201) {
browserHistory.push('/features');
}
})
.catch(function(error) {
console.log(error);
})
If user enters a wrong data to an input, the response from server holds info, e.g.
password has to be longer then...
mail missing the # sign
etc...
but unfortunately, I don't know how to get into that response if there's a 400 bad request status. It just shows the error, but I'm unable to get the response.
If the response status is 201, it properly shows the response. But in case of 400, even if I change the condition and add else if (response.status == 400) { console.log(response) } it doesn't show up the response.
Any help highly appreciated.
Just looking at the axios documentation, it looks like the response should be exposed in the error object (i.e. console.log(error.response)).
More information about different info provided when the response code falls out of 2xx here: https://github.com/mzabriskie/axios#handling-errors
you need to log the data of the response:
axios.post('http://localhost/users', this.state)
.then(function(response) {
browserHistory.push('/features');
})
.catch(function(error) {
console.log(error.response.data); // this is the part you need that catches 400 request
});

Error bubbles to console even though it is handled

I am using isomorphic-fetch to perform AJAX requests from my react-redux application. In my api middleware I have the following function which calls the external resource:
import fetch from 'isomorphic-fetch';
function callApi({ endpoint, method, body, params = {} }) {
let route = generateRoute(endpoint, params);
return fetch(route, generateFetchOptions(method, body))
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
return response.json();
});
}
The above function is called by the following piece of code:
return callApi(callAPI).then(
response => next(actionWith({
response,
type: successType,
statusCode: 200
})),
error => error.json().then(errorObject => {
return next(actionWith({
type: failureType,
statusCode: errorObject.statusCode,
error: errorObject.message || 'Something bad happened'
}));
})
);
If I reject with Promise.reject(response) the error is being handled by the error handler, but for some reason the error also bubbles to the browser console (in my case Chrome).
Here is a screenshot from the console which shows what is happening (api.js:34 is the second line of the callApi method):
This is the usual behavior (in probably every browser?) when hitting an error during an HTTP request (no matter whether a linked image cannot be found, or an XHR fails). No matter if and how you handle those errors, they will always be logged to the console. There is no way to suppress this behavior.
References:
Provide a way not to display 404 XHR errors in console
How can I stop jQuery.ajax() from logging failures to the console?

Is there really no way to get the response body from a failed js fetch request?

The new js fetch API fails the promise if the request fails (400):
fetch(uri).catch(function(err) {
console.log(err);
});
Is there really no way to get the response body when this happens? e.g. to check an error code.
EDIT: I've created a js fiddle: https://jsfiddle.net/4x4xLwqo/ that calls this mockbin endpoint: http://mockbin.org/bin/d87acbb0-526e-4d66-aea4-b827d9c35031/view
EDIT 2: updated jsfiddle to use a better endpoint: https://jsfiddle.net/4x4xLwqo/2/
fetch won't go into catch if it encounters a HTTP error. You can handle that with a regular then.
From MDN:
A fetch() promise will reject with a TypeError when a network error is encountered, although this usually means permission issues or similar — a 404 does not constitute a network error, for example. 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.
And an accompanying example, from MDN as well:
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);
});

Categories