I use axios to make call to the backend server. And with redux-saga I can control the side effect from the server easily.
import {call, put, takeEvery} from "redux-saga/effects";
import {REQUEST_FAILED, REQUEST_SUCCESS, ROOT_URL, SUBMIT_USERNAME_PASSWORD} from "../../constants";
import axios from "axios/index";
const shootApiTokenAuth = (values) => {
const {username, password} = values;
return axios.post(`${ROOT_URL}/api-token-auth/`,
{username, password});
};
function* shootAPI(action) {
try {
const res = yield call(shootApiTokenAuth, action.payload);
const {history} = action.payload;
yield put({
type: REQUEST_SUCCESS,
payload: res
});
history.push('/companies'); //push user to `/companies` page
} catch (err) {
yield put({
type: REQUEST_FAILED,
payload: err
});
}
}
export function* watchSubmitBtn() {
yield takeEvery(SUBMIT_USERNAME_PASSWORD, shootAPI);
}
Problem:
When the backend server is down. It does not return any error back to me. And browser will raises an error Failed to load resource: net::ERR_CONNECTION_REFUSED
I have seen previous answer on callback method, but I prefer axios and redux-saga not callback
I had tried console.log(err). And I still searching they way to grab the error message and differentiate it from server response error.
Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:87)
Question:
How can I use axios and redux-saga to catch the err?
If you put the try/catch around the axios request itself, then you can get a bit more granularity on the cause.
https://gist.github.com/fgilio/230ccd514e9381fafa51608fcf137253
You probably want to have a custom error format and an error reducer that handles the different types of errors appropriate. For example if you got a response you could parse it and add it to the error, else you know there is an application level error which you would handle with an 'Oops' page or something like that.
case REQUEST_FAILED:
//Probably it can failed by 2 reason
//1. 404 from server
//2. network is down
if (action.payload.response === undefined) {
return {
token: undefined,
message: 'Network is down',
isAuthenticated: false,
statusCode: 406
}
} else {
const tmp = action.payload.response.request.response;
const tmp2 = JSON.parse(tmp);
return {
token: undefined,
message: tmp2.non_field_errors[0],
isAuthenticated: false,
statusCode: action.payload.response.status
};
}
Related
I have a server that does the rendering of the component and returns an HTML when the request is made while rendering the server does a graphql call for a particular component which sometimes returns a 403 response.
Code:
const client = new ApolloClient({
link: new HttpLink({
uri: 'https://url/graphql',
fetch,
headers: {
'csrf-tokens': tokens,
Referer: header_referer,
},
}),
queryDeduplication: false
)}
export const getProperties = async () => {
try {
await client
.query({query, variables})
.then((response) => {
const data = response.data.properties;
if(response.error) {
throw new Error("Error encountered");
}
}
.catch((error) => {
console.log("gettProperites error")
})
} catch (err) {
console.log("Execution failed")
}
}
I'm making a graphql call inside the getProperties function and whenever I get a 403 error my pod crashes. I have wrapped the call inside try-catch block and added an additional if condition inside .then() to check for any error inside the response. Still, the 403 response is not caught and crashes the pod.
The above code is the overall structure of the code that I'm running, I have removed a few details that were not required to keep it small.
Try interceptors.
I can just tell for vue, but I think in react it's quite similar:
const link = createUploadLink({ uri: '/graphql' });
const errors = onError(({ networkError }) => {
if (networkError.statusCode === 403) {
// do something with 403 response code like:
router.push('/403');
}
else {
// do something with any other error response code
}
})
const apolloClient = new ApolloClient({
link: errors.concat(link),
...
})
I can't tell what is the optimal way to handle the status 500 sent by my API. Could you please help me?
In the case below, when my API responds with a status 500, I wish to set an error message in my redux store. When my API sent a successful status code, I wish to "Fetch the user decks" in my store.
My first attempt felt logical and dry to me, but it did not work:
const createDeck = async () => {
try {
const request = await fetch(`${back}/deck/`, options)
const response = await request.json()
store.dispatch({ type: FETCH_USER_DECKS })
} catch (error) {
store.dispatch({ type: SET_ERROR, message: error })
}
}
When the API send a 500 status, no exception seems to be thrown and the code in my catch block is ignored.
My second and third attempt worked as I excepted them too, but they feel convoluted and weird:
2nd attempt:
const createDeck = async () => {
try {
const request = await fetch(`${back}/deck/`, options)
const response = await request.json()
if (request.status === 201 || request.status === 200) {
store.dispatch({ type: FETCH_USER_DECKS })
} else {
store.dispatch({ type: SET_ERROR, message: error })
}
} catch (error) {
console.log('error')
}
}
This works, but it ignores completely the catch block, making me wonder: what's even the point of try...catch then?
Third attempt:
const createDeck = async () => {
try {
const request = await fetch(`${back}/deck/`, options)
const response = await request.json()
if (request.status === 201 || request.status === 200) {
store.dispatch({ type: FETCH_USER_DECKS })
} else {
throw response
}
} catch (error) {
store.dispatch({ type: SET_ERROR, message: error })
}
}
This third attempt make use of catch, but it feels weird to manually throw an exception, to be immediately caught by catch.
Am I missing something here?
Isbn has properly answered this in the comments. I'm putting it here again for visibility:
"Since you're using fetch() it would probably make sense to adopt its implementation model and to not consider a 500 response as an exception (attempt 2). But I wouldn't see anything wrong with your third attempt either (axios, for example, would raise an error in that case). Note that fetch() response exposes a ok property to check whether the request succeeded or not"
I'm trying to call API using common HTTPSERVICE method, Strangely I'm getting response whenever the debugger is connected, but API is throwing Network Error when debugger is not connected.
React-Native Version: 0.63.2
axios verison: 0.18.0
const sendRequest = async (url, method, params, data={}, headers = {}) => {
try {
const options = {
url,
method,
params,
data,
headers,
}
console.log(options);
return await axios(options);
} catch (err) {
console.log("request error",err);
throw err;
}
}
function* HttpService(url, methodType, params, data, headerParams = {}) {
try {
const user = yield select(state => state.ssoReducer.user);
if (isTokenExpired(user.token)) {
yield call(getPingFedToken);
}
const signIn = yield select(state => state.ssoReducer);
const authToken = signIn.user?.token;
const headers = {
Authorization: `${authToken}`,
'SWAP-MOBILE-APP': true,
"Content-Type": "application/x-www-form-urlencoded",
...headerParams
};
return yield call(sendRequest, url, methodType, params, data , headers)
} catch (error) {
console.log("http error",error);
if (error.response.status === (403)) {
yield put({
type: SSO_FAILURE,
payload: "Unauthorised alert"
});
}
throw error;
}
}
export default HttpService;
enter image description here
From what I see, the problem is not in this part of the code.
I am suspecting the way you create headerParams.
here is what you need to do.
check all the global methods that you are using in this flow ex. URLSearchParams
make sure that they exist in the web component layer that is part of the JS engine related to the react-native version you are using.
why:
because, when you connect the debugger; you are not executing your JS code in the engine of React Native, but inside the one in the chrome you are debugging with.
this is a known issue in the react-native world, and to avoid it I suggest you use another debugger like https://infinite.red/reactotron
So I am implementing axios call cancelation in the project. Right now looking at axios documentation it seems pretty straight forward https://github.com/axios/axios#cancellation
So I did define variables on the top of my Vue component like
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
obviously on top of that is import axios from 'axios';
Then I have a method of fetching the API
On the top of the method I want to cancel out the request in case it is running so the last one cancels out if the user spams the filtering.
async fetchPartners(inputToClear) {
source.cancel();
...
try {
const response = await axios.get(`../partners?limit=1000${this.createRequestString()}`, {
cancelToken: source.token
});
// Here you can see I did add the cancelToken to the request
this.partners = response.data.data;
} catch (error) {
if (axios.isCancel(error)) {
console.log('Request canceled', error.message);
}
const fetchErrors = this.utilGlobalHandleErrorMessages(error);
this.utilGlobalDisplayMessage(fetchErrors.message, { type: 'error' });
return [];
} finally {
...
}
},
So it is pretty straight forward, just took the code from axios documentation I gave you above, it should be working by logic. But what is actually happening, it doesn't even allow me to fetch the call, it is already cancelled out before I can call it. On console it shows me
Request canceled undefined
It just catches the error as if I am cancelling the call, but how can it be, because I am source.cancel() before the call.
Anyone has any idea?
I hope you should throttle your requests instead of canceling the request.
Could you please try the following if throttle does not suit your requirement?
const CancelToken = axios.CancelToken;
let source;
async fetchPartners(inputToClear) {
if(source){
source.cancel();
}
...
source = CancelToken.source();
try {
const response = await axios.get(`../partners?limit=1000${this.createRequestString()}`, {
cancelToken: source.token
});
// Here you can see I did add the cancelToken to the request
this.partners = response.data.data;
} catch (error) {
if (axios.isCancel(error)) {
console.log('Request canceled', error.message);
}
const fetchErrors = this.utilGlobalHandleErrorMessages(error);
this.utilGlobalDisplayMessage(fetchErrors.message, {
type: 'error'
});
return [];
} finally {
...
}
}
I use openweathermap API to get forecast. App is based on ReactJS and Redux. I have a problem with catch errors. I want to create alert for users when searched city doesn't exists in database.
So, I have action like below:
export function fetchWeather (city) {
const url = `${ROOT_URL}&q=${city}`;
const request = axios.get(url);
return (dispatch) => {
request
.then(({data}) => {
dispatch({type: FETCH_WEATHER, payload: data})
})
.catch((error) => {
dispatch({type: FETCH_WEATHER_ERROR, payload: error})
});
};
And my reducer:
import { FETCH_WEATHER, FETCH_WEATHER_ERROR } from '../actions/index';
export default function (state = [], action) {
switch (action.type) {
case FETCH_WEATHER:
console.log(action.payload) //I receive object, so it's ok
return [...state, action.payload];
case FETCH_WEATHER_ERROR:
console.log(action.payload) // I receive just info in console "Error: Request failed with status code 404"
return state;
}
return state;
}
So, it works properly but I'm curious how to get proper object in error part to simple show alert with message info what happened wrong. Because when I check in inspector tab (Networks) there is nice object in response:
{cod: "404", message: "city not found"}, but in console.log(action.payload) I have just info, no object, array... Why are these things different? How to get proper value of error response to show error message?
It looks like the API will always return 200 (success) when the connection works even though there is a 401 not allowed or 404 not found. Check out the following url in your dev tools network tab:
http://samples.openweathermap.org/data/2.5/weather?q=nomatterNoApiKey
So anything going into catch is actual network problem.
request
.then((response) => {
if(response.cod===200){
dispatch({type: FETCH_WEATHER, payload: response.data});
}else{
dispatch({type: FETCH_WEATHER_ERROR, payload: response.message});
}
})
You have to make sure that is the correct way to use the API and still have to deal with network errors.