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?
Related
Is this expected? I am testing the error checking of my code by forcing a GET error. That is, I turned off the endpoints on purpose. Oddly I am getting two errors for each single error.
I verified my fetch() request only occurs once by logging it to the console.
However I see two errors in the console. I wanted to make sure that for some reason the browser is not fetching the data twice.
Is this expected behavior? If so where is it documented?
GET http://localhost:3000/users/user 404 (Not Found)
It occurs twice for a single fetch();
Here is the fetch()
const options = {
credentials: 'include'
};
fetch('/users/user', options)
.then((response) => {
return response.json();
})
.then((user) => {
if(user) {
dispatch({type: 'initializeUser', current: user});
}
})
.catch((err) => {
console.error('DEBUG: F1Data: /users/user endpoint failed : ', err);
})
Similarly, I see my own error being thrown twice as well:
DEBUG: F1Data: /users/user endpoint failed : SyntaxError: Unexpected token < in JSON at position 0
I've encountered a unexpected behavior in my fetch code when the internet connection is slow.
Consider the following code (not my actual code):
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
fetch("http://url")
.then(handleErrors)
.then(response => console.log("success") )
.catch(error => console.log("error") );
// "error"
// "success" (why is this called?)
The only way I can explain this behaviour is that the response is somewhat of an async function itself (check out https://stackoverflow.com/a/33446005/3016745). Meaning that first the request is sent, immediately after the connection times out and throws an error, and last the response is coming and calls the .then.
I checked the backend and the request and response both were successful.
Does this make sense? How do I prevent this behaviour?
At this moment i'm using a response interceptor instance.interceptors.response.use for global error handling. In that function i'm redirecting in case of a 404, i'm clearing the storage in case of a 401 etc. Which works fine :)
Except now I've got a spot where i want to handle the 404 error differently, but a simple try/catch or a .catch won't work since the axios interceptor intercepts the response and redirects immediately.
Whats the best practice in this situation?
Not using the interceptor for global error handling? (but then what? call a handleError kind of function after every request except this one?)
Temporary turn it off somehow
Pass an ignore404 option or something with the axios request (although I use a function per request so i can't do this really simple)
What would be the best, but i'm not sure how is that axios is always doing some global error reporting except when you catch it yourself. And at that point at the bottom of the catch i could let the global error handler do it's job anyway if i want to
My interceptor:
function onResponseRejected(error) {
const { response } = error;
if (!response) return Promise.reject(error); // network error, not axios related
const { status } = response;
const { errors } = response.data;
if (status === 403) {
router.push({ name: '403' });
} else if (status === 404) {
router.push({ name: '404' });
}
....
....
My axios requests
export const fetch = (some) => {
return get(`some/${someId}/url/`)
}
My application usage of a request
const response = fetch(this.$route.params.connectorId, this.$route.params.mapperId).catch(() => {
console.log('At this moment the request will be redirected to another route...');
});
I use the following code to POST the users position to my own backend-service via the Fetch API:
window.onload = () => {
let getPosition = (options) => {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, options);
});
};
getPosition().then(pos => {
const data = new FormData();
data.append('latitude', String(pos.coords.latitude));
data.append('longitude', String(pos.coords.longitude));
fetch('/', { // <--- error is thrown in this line
method: 'POST',
body: data
}).then(response => {
if (!response.ok) {
throw Error('Data sent - Network response NOT OK');
} else {
console.log('Data sent - Network response OK')
}
});
});
};
This works flawlessly and the backend-service sends back a positive empty response. Data sent - Network response OK is logged in the browser's console and everything is fine except that immediately after, this is logged:
How come I get a Fetch failed loading: POST even though the POST succeeded, the response is OK and the status code in the Chrome network tab is 200 OK? Is there something wrong with my code?
I had the same behaviour that you were seeing.
My server responded to the POST request with a 204 (Empty Response) and an empty (Content-Length=0) response.
I tried changing that to just respond with a "ok" message instead (edit: actually just returning the created object now) and the fetch error log disappeared.
It seems to me that fetch (at least in Chrome) erroneously logs "Fetch Failed" when the response body is empty, even if the request was successful.
This happens because you don't read the empty response body. I noticed this, because Django triggers a broken pipe error on the server side. To fix this, just read the empty body out.
async function fetchSomething() {
const response = await fetch(url, {
method: 'POST'
})
await response.text()
return response.ok
}
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(
// ...