I'm trying to raise an errorBoundary if an API call does not return a 200 but the raiseErrorBoundary is invoked immediately in catch block when the API call is called, how can I make it so it is invoked only when an error has been thrown?
export const downgradeAPICall = (raiseErrorBoundary) => {
return fetch('http://localhost:42005/Upgrade/Downgrade', {
method: 'POST'
}).then(res => {
if(res.status != 200){
throw new Error("An error has occured during API request!")
} else {
window.location.reload()
}
})
.catch((error)=>{
console.log(error)
raiseErrorBoundary()
})
}
200 is an "OK" status response. Right now you throw an error if it's successful so it will probably throw an error no matter what the way you've written it. Instead you probably want if(res.status !== 200)
(also "occurred" was misspelled as "occured")
export const downgradeAPICall = (raiseErrorBoundary) => {
return fetch('http://localhost:42005/Upgrade/Downgrade', {
method: 'POST'
}).then(res => {
if(res.status !== 200){
throw new Error("An error has occurred during API request!")
} else {
window.location.reload()
}
})
.catch((error)=>{
console.log(error)
raiseErrorBoundary()
})
}
Related
I would like to do some error handling on the response received from a call I am making and then move to the catch if the specific null check is hit. Something like this:
fetch('example.json')
.then(response => {
if (response.data === null) {
//go to catch
}
})
.catch(error => {
console.error("error happened", error);
})
What would be the best way to go about doing something like this? Any red flags with throwing an error inside a then block?
If you throw in a promise handler, that rejects the promise the handler returns. So:
fetch('example.json')
.then(response => {
if (response.data === null) {
throw new Error();
}
})
.catch(error => {
console.error("error happened", error);
})
What you throw will be the rejection reason the catch handler sees. It doesn't have to be an Error, but as with synchronous code, it's generally best if it is.
But, note that A) A fetch response doesn't have a data property, and B) You need to check for HTTP success and parse the JSON that was returned.
You probably want something like this:
fetch('example.json')
.then(response => {
if (!response.ok) {
// (I tend to use an specialized `Error` type here
// More on my anemic blog:
// http://blog.niftysnippets.org/2018/06/common-fetch-errors.html)
throw new Error("HTTP error " + response.status);
}
return response.json();
})
.then(data => {
if (data === null) {
throw new Error("The data is null");
})
// ...do something with `data`...
})
.catch(error => {
console.error("error happened", error);
});
In a comment on the question you've said:
i was hoping there was a way to check this response object without having to trigger the 'extreme' measure of throwing an exception
You do have an alternative which is basically identical in outcome: Return a rejected promise. Here's my second code block above adapted to do that:
fetch('example.json')
.then(response => {
if (!response.ok) {
// (I tend to use an specialized `Error` type here
// More on my anemic blog:
// http://blog.niftysnippets.org/2018/06/common-fetch-errors.html)
return Promise.reject(new Error("HTTP error " + response.status));
}
return response.json();
})
.then(data => {
if (data === null) {
return Promise.reject(new Error("The data is null"));
})
// ...do something with `data`...
})
.catch(error => {
console.error("error happened", error);
});
And as with the throw version, you don't have to use an Error, it's just best practice. It can be anything you want.
If you want, you can throw an Error object from within your promise handler.
fetch('example.json')
.then(response => {
if (response.data === null) {
throw new Error('oopsie');
}
})
.catch(error => {
console.error("error happened", error); // will show "Error: oopsie"
})
I have an app in Vue.js where user can post a review on a selected restaurant. To "POST" a review I have two functions linked to on #click event:
<button
class="btn btn-sm btn-primary mt-2 mr-2"
#click="addRestaurant(), addReview()"
>Add a Review</button>
Here is my two functions:
addRestaurant () {
let endpoint = `/api/restaurant/`;
let method = "POST";
apiService(endpoint, method, { maps: this.$route.params.maps, adress: this.$route.params.adress, name: this.$route.params.name })
},
addReview () {
let endpoint = `/api/restaurant_review/`;
let method = "POST";
apiService(endpoint, method, { maps: this.$route.params.maps, review_author: 1 })
},
It does work most of the time but I am facing two problems:
-If a restaurant instance allready exists, it throws a HTTP 400 error in my console. I've tried to catch it but with no success.
-It seems that sometimes addReview() is execued before addRestaurant() and I get an error because
this.$route.params.maps has a ForeignKey constraint.
So I tried to make only one function, with my two functions inside, trying to make addReview() executing as soon as addRestaurant() is done but I couldn't find a way. I also tried to make an if statement to check if my restaurant instance existed but I don't know if making so much API call in a row is good practice.
Any help is welcome I you think about the right solution to handle my problem
This my API service:
function handleResponse(response) {
if (response.status === 204) {
return '';
} else if (response.status === 404) {
return null;
} else if (response.status === 400) {
console.log("Restaurant allready added!")
return null;
} else {
return response.json();
}
}
function apiService(endpoint, method, data) {
const config = {
method: method || "GET",
body: data !== undefined ? JSON.stringify(data) : null,
headers: {
'content-type': 'application/json',
'X-CSRFTOKEN': CSRF_TOKEN
}
};
return fetch(endpoint, config)
.then(handleResponse)
.catch(error => console.log(error))
}
export { apiService };
First of all, in your handleResponse you can throw an error if something is wrong. That way, the error will end up in the catch method of the fetch call:
function handleResponse(response) {
if (response.status === 204) {
throw new Error("No data")
} else if (response.status === 404) {
throw new Error("Wrong URL")
} else if (response.status === 400) {
throw new Error("Restaurant already added!")
} else {
return response.json();
}
}
If handleresponse goes well, the fetch call will return a promise, but you're not handling that promise when you call apiService. I think it should look something like this:
addRestaurant () {
let endpoint = `/api/restaurant/`;
let method = "POST";
let config = { maps: this.$route.params.maps, adress: this.$route.params.adress, name: this.$route.params.name };
apiService(endpoint, method, config)
.then(data => {
console.log("restaurant added!" + data);
// now you can call the add review function
addReview();
})
.catch(error => console.log(error));
},
I'm calling an API that defines the statusCode from data instead of the response code:
{
data: {
statusCode: 422,
message: "User's not found"
},
status: 200
}
In my axios get request it's getting the status code from the status instead in data.
return axios.get(`${process.env.BASE_URL}/users`)
.then(response => {
console.log(response);
}).catch(err => {
console.log(err.message);
});
I'm getting the response but it should go to catch since it's 422.
How can I refer to the statusCode of the data response so that if it's not 200 it should go to catch statement
You can intercept the response, inspect the data and throw a custom error in this case:
// Add a response interceptor
axios.interceptors.response.use(function(response) {
if (response.data && response.data.statusCode && !(response.data.statusCode >= 200 && response.data.statusCode < 300)) throw new Error()
return response;
}, function(error) {
return Promise.reject(error);
});
// Make a GET request
axios.get(url)
.then((data) => {
console.log('data', data)
})
.catch((e) => {
console.log('error', e)
})
This way you configure your axios instance so you dont have to repeat yourself for every single request in your app
Also, you can override the status using following code. But since status validation has already executed, it will not throw errors on bad status codes
// Add a response interceptor
axios.interceptors.response.use(function(response) {
if (response.data && response.data.statusCode) response.status = response.data.statusCode
return response;
}, function(error) {
return Promise.reject(error);
});
You can handle with standard if statement inside the .then()
return axios.get(`${process.env.BASE_URL}/users`)
.then(response => {
if(response.data.statusCode===442){
...//custom error handling goes here
}else{
...//if statusCode is a success one
}
}).catch(err => {
console.log(err.message);
});
Check the response.data.statusCode value, if it is 442 then you should ideally throw an Error and let it be handled in the .catch callback.
return axios.get(`${process.env.BASE_URL}/users`)
.then(response => {
if(response.data.statusCode===442){
throw new Error(response.data.message); //using throw instead of Promise.reject() to break the control flow.
}else{
//return the data wrapped in promise
}
})
.catch((err) => {
console.log(err.message);
return Promise.reject(err.message);
});
Is it possible to throw an error on purpose inside the .then() block in axios? For instance, if the api responds with 204 status code, could I throw an error and run the catch block?
For example:
axios.post('link-to-my-post-service', {
json-input
}).then(response => {
if (response.status === 200) {
//proceed...
}
else {
// throw error and go to catch block
}
}).catch(error => {
//run this code always when status!==200
});
EDIT
I tried this, but it didn't work:
var instance = axios.create({
validateStatus: function (status)
{
return status == 200;
}
});
axios.post('link-to-my-post-service', {input: myInput}, instance)
.then(response => {
dispatch({
type: "FETCH_SUCCESS",
payload: response.data
});
}).catch(error => {
dispatch({
type: "FETCH_FAILED",
payload: error
});
});
When I get a status code 204, still the executed block is then() block instead of the catch block.
EDIT 2
The correct answer using Ilario's suggestion is this:
var instance = axios.create({
validateStatus: function (status)
{
return status == 200;
}
});
instance.post('link-to-my-post-service', {input: myInput})
.then(response => {
dispatch({
type: "FETCH_SUCCESS",
payload: response.data
});
}).catch(error => {
dispatch({
type: "FETCH_FAILED",
payload: error
});
});
Now when the status code is not equal to 200 the catch block code is executed.
If you give a look at the GitHub Project Page you will notice following option description.
/* `validateStatus` defines whether to resolve or reject the promise for a given
* HTTP response status code. If `validateStatus` returns `true` (or is set to `null`
* or `undefined`), the promise will be resolved; otherwise, the promise will be
*/ rejected.
validateStatus: function (status) {
return status >= 200 && status < 300; // default
},
So you could create an Instance with your own configuration.
var instance = axios.create({
validateStatus: function (status) {
return status == 200;
},
});
You could also set defaults. These will be applied to every request.
axios.defaults.validateStatus = () => {
return status == 200;
};
UPDATE 1
To set the config only on a specific operation you could replace "config" with your desired values or methods.
axios.post(url[, data[, config]])
UPDATE 2
I tried this, but it didn't work.
You cannot pass the instance to axios.post(). You must call post on the new instance.
var instance = axios.create({
validateStatus: function (status) {
return status == 200;
}
});
instance.post('url', data, config);
Thank you very much for your suggestions. The answer was simpler than I expected.
I didn't want to set any default options to change the behavior of axios, so I just tried something like the code below, and it worked. Every time the code throw new Error("Error"); is executed, the catch block code is executed after that.
axios.post('link-to-my-post-service', {
json-input
}).then(response => {
if (response.status === 200) {
//proceed...
}
else {
// throw error and go to catch block
throw new Error("Error");
}
}).catch(error => {
//when throw "Error" is executed it runs the catch block code
console.log(error)
});
I am trying to handle 500 internal server errors inside fetch. If an internal error occurs, the server responds with a message. I want to extract that message.
const req = new Request(url, {
method: node.method,
mode: 'cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
fetch(req)
.then((response) => {
if (response.status === 500) {
// res.json extracts the body from the response as a promise, chain
// .then on it and throw an error to be caught in the lower catch.
response.json()
.then((json) => {
const { message, stackTrace } = json;
throw new ServerException(message, stackTrace); // note 1
})
.catch((error) => {
return Promise.reject(RawException(error)); // note 2
});
} else {
return response.json();
}
})
.then((json) => { // note 3
dispatch(stopLoading());
dispatch(recieveResponse(typeOfRequest, json));
})
.catch((e) => {
dispatch(stopLoading());
dispatch(responseError());
dispatch(showError(e.message));
});
};
My issue is that extracting the body of the response creates a new promise, and I am unable to reject the outer promise from the inner one.
Note 1 triggers the catch method of the inner promise. Inside catch, I have tried throwing another error but it doesn't seem to work. If I throw new RawException(error) on the second noted line, nothing happens and the then method on the third noted line triggers. If I return a rejected promise as I have in the code provided, then still triggers but json is undefined.
How do I do this?
The solution is not to nest promises, but to resolve/return the .then of the outer promise with the conclusion of the inner promise.
if (response.status === 500) {
response.json() // response.json returns a promise, we chose to do nothing with its
.then((json) => { // conclusion
const { message, stackTrace } = json;
throw new ServerException(message, stackTrace); // note 1
})
.catch((error) => {
return Promise.reject(RawException(error)); // note 2
});
} else {
return response.json();
}
Should become
if (response.status === 500) {
return response.json() // return the result of the inner promise, which is an error
.then((json) => {
const { message, stackTrace } = json;
throw new ServerException(message, stackTrace);
});
} else {
return response.json();
}
The else clause can be removed as well if that syntax is preferred. ESLint complains about the else being wasteful, but I perfer the way it makes the code's branching explicit.