I miss response .status when HTTP response status 400, - reactjs

my problem is when i use custom axios aka axiosInstance, i only catch res.status == 200, when response is status 400, i can not take res.status and use it.
My config custom axios:
import axios from "axios";
import { API_AUTH } from "./urlConfig";
import store from "../store";
import { authConstants } from "../store/actions/constants";
const token = localStorage.getItem("token") ? localStorage.getItem("token") : sessionStorage.getItem("token");
const axiosInstance = axios.create({
baseURL: API_AUTH,
headers: {
Authorization: token ? `Bearer ${token}` : "",
},
});
axiosInstance.interceptors.response.use(
(res) => {
return res;
},
(err) => {
const { status } = err.response;
if (status === 400) {
localStorage.clear();
store.dispatch({ type: authConstants.LOGOUT_SUCCESS });
}
return err;
}
);
export default axiosInstance;
and this code when i use axios
import { authConstants } from "./constants";
import { LOGIN } from "../../helpers/urlConfig";
import authAPI from "../../helpers/authAPI";
export const login = (input) => {
const { isRemember, ...inputUser } = input;
return async (dispatch) => {
dispatch({
type: authConstants.LOGIN_REQUEST,
});
const res = await authAPI.post(LOGIN, {
...inputUser,
});
if (res.status === 200) {
//do something OK when status code = 200.
}
if (res.status === 400) {
//can not take res and check res.status
console.log("res", res);
}
};
};
Thanks for all help.

You probably need to handle the error using catch.
Usually I am doing something like this:
const res = await authAPI.post(LOGIN, {
...inputUser
}).catch(error => ({
status: error.response.status,
data: error.response.data
}))
then you should be able to get res.status properly.

Related

Infinite loop for post and put requests when trying to refresh-token

I'm in an application with NEXTjs and whenever the token expires and you try to make a post request it goes into a loop, the POST request returns 401 then it does the refresh-token returns 200 after trying to make the post and it returns 401 and the loop continues.
The code api.ts where did the refresh-token
import { GetServerSidePropsContext } from 'next'
import axios, { AxiosError } from 'axios'
import { setCookie, parseCookies, destroyCookie } from 'nookies'
import { error as notifyError } from 'helpers/notify/error'
import { STORAGE } from 'constants/storage'
import { logOut } from 'helpers/auth/logOut'
import { ErrorResponse } from 'shared/types'
let isRefreshing = false
let failedRequestsQueue: {
onSuccess(token: string): void
onFailure(err: AxiosError): void
}[] = []
export function baseInstance(
ctx: GetServerSidePropsContext | undefined = undefined
) {
const cookies = parseCookies(ctx)
const token = cookies[STORAGE.TOKEN_KEY]
const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL
})
api.defaults.headers.common.Authorization = `Bearer ${token}`
api.interceptors.response.use(
response => response,
(error: AxiosError) => {
const expectedError =
error.response &&
error.response.status >= 400 &&
error.response.status < 500
if (!expectedError) {
notifyError('Encontramos um problema por aqui.')
}
return Promise.reject(error)
}
)
return api
}
export function setupAPIClient(
ctx: GetServerSidePropsContext | undefined = undefined
) {
const cookies = parseCookies(ctx)
const token = cookies[STORAGE.TOKEN_KEY]
const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL
})
api.defaults.headers.common.Authorization = `Bearer ${token}`
axios.interceptors.request.use(
config => {
const cookies = parseCookies(ctx)
const token = cookies[STORAGE.TOKEN_KEY]
// eslint-disable-next-line no-param-reassign
config.headers.authorization = `Bearer ${token}`
api.defaults.headers.common.Authorization = `Bearer ${token}`
return config
},
error => {
Promise.reject(error)
}
)
api.interceptors.response.use(
response => response,
(error: AxiosError<ErrorResponse>) => {
const expectedError =
error.response &&
error.response.status >= 400 &&
error.response.status < 500
if (!expectedError && error.response.status !== 401) {
notifyError('Encontramos um problema por aqui.')
}
if (error.response.status === 401) {
if (error.response.data.message === 'Refresh token not found') {
logOut()
}
const cookies = parseCookies(ctx)
const { '#mf_rt_key': refresh_token } = cookies
const originalConfig = error.config
if (!isRefreshing) {
isRefreshing = true
api
.put('/refresh-tokens', {
refresh_token
})
.then(response => {
const { token, refresh_token } = response.data
setCookie(ctx, STORAGE.TOKEN_KEY, token, {
maxAge: 30 * 24 * 60 * 60, // 30 days
path: '/'
})
setCookie(ctx, STORAGE.REFRESH_TOKEN_KEY, refresh_token, {
maxAge: 30 * 24 * 60 * 60, // 30 days
path: '/'
})
api.defaults.headers.common.Authorization = `Bearer ${token}`
failedRequestsQueue.forEach(request => request.onSuccess(token))
failedRequestsQueue = []
})
.catch((error: AxiosError) => {
failedRequestsQueue.forEach(request => request.onFailure(error))
failedRequestsQueue = []
if (process.browser) {
logOut()
}
})
.finally(() => {
isRefreshing = false
})
}
return new Promise((resolve, reject) => {
failedRequestsQueue.push({
onSuccess: (token: string) => {
originalConfig.headers.Authorization = `Bearer ${token}`
resolve(api(originalConfig))
},
onFailure: (error: AxiosError) => {
reject(error)
}
})
})
}
return Promise.reject(error)
}
)
return api
}
I tried everything but nothing works.
I just update the axios version for 1.3.3 and works for me!
change_axios_version

ReactJs how to add interceptor in axios

I've been working on this for hours, and I have no idea where did it go wrong.
I want to have an axios interceptor for my ReactJs
this is my interceptor axiosHandler.js
import axios from "axios";
const axiosHandler = axios.create({
baseURL: process.env.REACT_APP_BASE_URL,
headers: {
Accept: "application/json",
},
});
axiosHandler.interceptors.request.use(
(config) => {
const token = localStorage.getItem("token");
if (token) {
config.headers["Authorization"] = "Bearer " + token;
}
return config;
},
(error) => {
Promise.reject(error);
}
);
//axiosHandler.interceptors.response
export default axiosHandler;
And here is how I use the handler in my other component
import axiosHandler from "../services/axiosHandler";
const getData = async () => {
await axiosHandler
.get(`/path`)
.then((response) => {
//do something
})
};
And I get an error of below
services_axiosHandler__WEBPACK_IMPORTED_MODULE_0_.get is not a function
I've read many other solutions, but I can't find the difference as how it leads to the error of mine.
Where do I put it wrong?
Thank you
inside axios.index
import axios from "axios";
import { API_URL } from "../config/config";
const axiosHttp = axios.create({
baseURL: `${API_URL}`,
});
axiosHttp.interceptors.request.use(
(config) => {
const token = "Your Token here"
return {
...config,
headers: {
...(token !== null && { Authorization: `${token}` }),
...config.headers,
},
};
},
(error) => {
return Promise.reject(error);
}
);
axiosHttp.interceptors.response.use(
(response) => {
//const url = response.config.url;
//setLocalStorageToken(token);
return response;
},
(error) => {
if (error.response.status === 401) {
//(`unauthorized :)`);
//localStorage.removeItem("persist:root");
//removeLocalStorageToken
//window.location.href = "/login";
}
return Promise.reject(error);
}
);
export default axiosHttp;
Then inside your API function use it like below
import axiosHttp from "./utils/axios";
const getData = async ()=>{
try{
const response = await axiosHttp.get('/path')
return resposne;
}
catch(error){
//handle error here...
}
}
Last but not least, you shouldn't use await when using callback (then/catch)

Refresh token is working, but showing error message- React Native

I am struggling to find out the issue with my refresh token mechanism.
I have a some problem that I can't fix. Refresh token is working, but it showing an alert 'somthingWentWrong' but the application is still in a good state and working properly.
Here is my code, please tell me where is my mistake?
import axios from 'axios';
import config from './env';
import { Alert } from 'react-native';
import { Constants, storeData } from '#utils';
import { store } from '#data/store';
import { t } from 'i18next';
enum StatusCode {
BadRequest = 400,
Unauthorized = 401,
Fobbiden = 403,
NotFound = 404,
NotAllowed = 405,
Conflict = 409,
ServerError = 500,
}
const getErrorMessage = (error: any): string => {
if (error?.request?.responseURL?.includes('Authentication/signin') && error?.request === StatusCode.Fobbiden) {
return t('invalidEmailOrPassword');
} else if (error?.response?.data) {
return error.response.data;
} else if (error?.response?.data?.length) {
return error.response.data;
} else {
return t('somethingWentWrong');
}
};
export const createAxios = () => {
const baseURL = config.apiUrl;
const token = store.getState()?.auth?.token;
const refreshToken = store.getState()?.auth?.refreshToken;
let headers: any = { 'Content-Type': 'application/json' };
if (token) {
headers = { ...headers, Authorization: `Bearer ${token}` };
}
const instance = axios.create({ baseURL, headers });
instance.interceptors.response.use(
(res) => Promise.resolve(res.data),
async (error) => {
if (error?.request?.status === StatusCode.Unauthorized) {
try {
const body = { token: token, refreshToken: refreshToken };
const _instance = axios.create({ baseURL: config.apiUrl, headers: headers });
const tokenResponse = await _instance.post(config.token_url, body);
if (tokenResponse) {
await store.dispatch({ type: '[AUTH] SET_TOKEN', payload: tokenResponse?.data?.accessToken });
await store.dispatch({ type: '[AUTH] SET_REFRESH_TOKEN', payload: tokenResponse?.data?.refreshToken });
await storeData(Constants.asyncKeys.AUTH_TOKEN, tokenResponse?.data?.accessToken);
await storeData(Constants.asyncKeys.REFRESH_TOKEN, tokenResponse?.data?.refreshToken);
headers.Authorization = 'Bearer ' + tokenResponse?.data?.accessToken;
error.config.headers.Authorization = 'Bearer ' + tokenResponse?.data?.accessToken;
const failedResponse = await axios.request(error.config);
if (failedResponse) {
return failedResponse.data;
}
}
// eslint-disable-next-line no-shadow
} catch (error: any) {
error.request.status && Alert.alert(t('error'), getErrorMessage(error));
}
return Promise.reject(error?.response);
}
return Promise.reject(error?.response);
},
);
return instance;
};

Axios making 2 requests on refresh

When I navigate using Link (react router-dom) I don't have this problem, but if I refresh the browser I get a 403 error in console saying unauthorised and then I get the data in the next request with a 200 response. Why is this making what looks like 2 requests when refreshing the browser?
import { AuthContext } from "../../shared/context/auth-context";
const ContactEntries = () => {
const auth = useContext(AuthContext);
useEffect(() => {
const source = Axios.CancelToken.source();
setIsLoading(true);
const getContactEnquiries = async () => {
try {
const response = await Axios.get(
`${process.env.REACT_APP_BACKEND_URL}/v1/contact`,
{
cancelToken: source.token,
headers: { Authorization: "Bearer " + auth.token },
}
);
if (response.status === 200) {
setIsLoading(false);
setEnquiries(response.data.enquiries);
}
} catch (err) {
setIsLoading(false);
console.log(err.response);
}
};
getContactEnquiries();
return () => {
source.cancel();
};
}, [!!auth.token]);
}
Here is my authContext:
import { createContext } from "react";
export const AuthContext = createContext({
isLoggedIn: false,
userId: null,
token: null,
email: null,
firstName: null,
login: () => {},
logout: () => {},
});
This is because your useEffect is running twice on refresh. On first render it is not getting auth.token and may be it null. And on second render it is making call with 200 status code.
You have to check auth token it coming successfully.
You can check it this way
useEffect(() => {
const source = Axios.CancelToken.source();
setIsLoading(true);
const getContactEnquiries = async () => {
try {
const response = await Axios.get(
`${process.env.REACT_APP_BACKEND_URL}/v1/contact`,
{
cancelToken: source.token,
headers: { Authorization: "Bearer " + auth.token },
}
);
if (response.status === 200) {
setIsLoading(false);
setEnquiries(response.data.enquiries);
}
} catch (err) {
setIsLoading(false);
console.log(err.response);
}
};
if(auth.token) getContactEnquiries();
return () => {
source.cancel();
};
}, [!!auth.token]);

Generic function to request api with Axios

I am trying to build a generic function for my endpoints, using Axios and React. Generic because I have always the same header and I do not want to repeat a lot of code for each of my components.
To do that, I built this function (sorry, a lot of comments that I will remove after of course) :
export const getRequest = ( endpoint ) => axios
.get( env._URL_SERVER_ + endpoint, { headers: getHeaders() } )
.then((res) => {
// Success
console.log(res);
return {error: false, response: res.data};
})
.catch((error) => {
// Error
if (error.response) {
/*
* The request was made and the server responded with a
* status code that falls out of the range of 2xx
*/
console.log(error.response.data);
console.log(error.response.status);
return {error: true, status: error.response.status, data: error.response.data};
} else if (error.request) {
/*
* The request was made but no response was received, `error.request`
* is an instance of XMLHttpRequest in the browser and an instance
* of http.ClientRequest in Node.js
*/
console.log(error.request);
return {error: true, data: error.request };
} else {
// Something happened in setting up the request and triggered an Error
console.log('Error', error.message);
return {error: true, data: error.message}
}
});
Ant then in my components I do that :
getSchools = () => {
this.setState({
loadingSchools: true
}, () => {
getRequest(`/schools?name=${this.state.filterByName}&city=${this.state.filterByCity}&school_type_id=${this.state.filterBySchoolTypeId}&page=${this.state.selectedPage}`)
.then((response) => {
// there is an error
if (!response.error) {
this.setState({
schools: response.response.data,
meta: response.response.meta,
links: response.response.links
})
} else {
this.setState({
error: true,
errorMessage: response.data,
})
}
})
.then(() => {
this.setState({loadingSchools : false});
})
})
}
It works fine. I tested it in several situation (all is OK - 200, not found - 404, no response). But is it a good practice ? I feel that there is a lot of codes in the parent component. Maybe I complicate my life?
Here is how I've done it:
var URL_BACKEND = "http://localhost:5000/";
// Create Function to handle requests from the backend
callToBackend = async (ENDPOINT, METHOD) => {
const options = {
url: `${URL_BACKEND}${ENDPOINT}`,
method: METHOD,
headers: {
Accept: "application/json",
"Content-Type": "application/json;charset=UTF-8",
},
};
const response = await axios(options);
return response.data;
}
// Then you make a call with the exact endpoint and method:
const response = await this.callToBackend('createSetupIntent', 'POST');
console.log(JSON.stringify(response));
create one common file for base URL let's say api.js
// api.js file code
export const apiUrl = axios.create({
baseURL: 'http://localhost:5000',
});
Register file
// register.js file code
import { apiUrl } from './api';
try {
const resp = await apiUrl.post('/api/register', {
username,
email,
password,
});
const { data, status } = resp;
if (Object.keys(data).length && status === 200) {
// received api data successfully
console.log('API response', data);
}
} catch (err) {
console.log(err);
}
// For auth request
try {
const token = localstorage.getItem('token');
const res = await apiUrl.post(
'/authroute',
{
name: fullName,
originCountry: country,
career: careerStatus,
},
{
headers: { Authorization: `Bearer ${token}` },
}
);
const { data, status } = strapiRes;
if (Object.keys(data).length && status === 200) {
return res.status(status).json(data);
}
} catch (error) {
throw new Error(error);
}
// same for all request
apiUrl.get(endpoint);
apiUrl.post(endpoint, body);
apiUrl.put(endpoint, body);
apiUrl.delete(endpoint, body);

Resources