Here is my function:
import axios from "axios";
import { redirect } from "react-router-dom";
let fetch = async (data, method, url, responseType, header) => {
let requestObj = { "method": method, "url": url }
if (method.toLowerCase() === "get") {
requestObj.params = data;
} else {
requestObj.data = data;
}
if (responseType) {
requestObj["responseType"] = responseType;
}
if (header) {
requestObj["headers"] = header;
}
axios.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error.response.status === 401) {
console.log("Unauthorized");
return redirect("/login");
}else {
return error;
}
}
);
let result = await axios(requestObj);
return result;
}
I want to make this function be called by other components,
Therefore, the useNavigate() solution does not fit my situation.
Is it mean I need to check the response status in each component and forward the browser to the /login when the access is not authorized?
PS: I am using "react-router-dom": "^6.4.2".
import axios from "axios";
import { useNavigate } from "react-router-dom";
let navigate = useNavigate();
let fetch = async (data, method, url, responseType, header) => {
let requestObj = { "method": method, "url": url }
if (method.toLowerCase() === "get") {
requestObj.params = data;
} else {
requestObj.data = data;
}
if (responseType) {
requestObj["responseType"] = responseType;
}
if (header) {
requestObj["headers"] = header;
}
axios.interceptors.response.use(
(response) => {
return response;
},
(error) => {
if (error.response.status === 401) {
console.log("Unauthorized");
return navigate("/login");
}else {
return error;
}
}
);
let result = await axios(requestObj);
return result;
}
try to use useNavigate() instead of redirect this should work
You can use browserHistory to redirect to another component
import { browserHistory } from 'react-router-dom';
if (error.response.status === 401)
{
console.log("Unauthorized");
browserHistory.push('/login');
}
else
{
return error;
}
Related
I'm coding my first react-native program with Axios and found problems making get calls. Sinse it's a Network error I assume the problem is somewhere below.
Api.js:
import axios from "axios";
const api = axios.create({
baseURL: "https://192.168.15.8:3333",
});
export default api;
index.js
async function loadIncidents() {
if (loading) {
return;
}
if (total > 0 && incidents.length === total) {
return;
}
setLoading(true);
try {
const response = await api.get("/incidents", {
params: { page },
});
setIncidents([...incidents, ...response.data]);
setTotal(response.headers["x-total-count"]);
setPage(page + 1);
} catch (err) {
console.log('Ocorreu um erro: ', err)
}
setLoading(false);
}
useEffect(() => {
loadIncidents();
}, []);
you can use try() catch()
async function loadIncidents() {
if (loading) {
return;
}
if (total > 0 && incidents.length === total) {
return;
}
setLoading(true);
let response;
try {
response = await api.get("incidents", {
params: { page },
});
} catch (ex) {
console.log(ex);
}
setIncidents([...incidents, ...response.data]);
setTotal(response.headers["x-total-count"]);
setPage(page + 1);
setLoading(false);
}
axios
.post('http://oud-zerobase.me/api/v1/users/signUp', toSent)
.then((response) => {
if (response.status === 200) {
/**redirect to home */
const authToken = response.data.token;
localStorage.setItem('accessToken', authToken);
console.log('token', authToken);
console.log(response);
/**redirect to home */
} else if (response.status === 400) {
errorMassage = response.statusText;
} else if (response.status === 401) {
/**Unauthorized */
errorMassage = response.statusText;
}
this.setState((prevState) => {
prevState.formErrors.mainError = errorMassage;
return prevState;
});
})
.catch((error) => {
console.log(error.response);
});
here I send the accessToken to a function outside I want to redirect the page when it successful
you can use hooks in react-router-dom to push your route:
import { useHistory } from "react-router-dom";
let history = useHistory();
history.push("/ROUTE_NAME");
I'm getting this error every time I press a specific button on my react-native app. I'm guessing it has to do something with an Api call or maybe it is trying to invoke URLs with localhost:8081, which may not be correct as it should be pointing to a server. I'm not sure exactly how to pinpoint the problem. Any help would be much appreciated. I'm also now sure if the problem is coming from the file I shared underneath.
App Warning message:
This is my RestClient code:
import {Alert, AsyncStorage} from 'react-native'
import {resetRouteTo} from 'util/NavigationHelper'
import {store} from '../index.js'
import {dropdownAlert} from 'util/AlertManager'
const DEFAULT_ERROR = {error: {code: 500, message: 'No JSON message'}}
export default class RestClient {
constructor (baseUrl = '', navigation, { headers = {}, devMode = false, simulatedDelay = 0 } = {}) {
if (!baseUrl) throw new Error('missing baseUrl')
this.navigation = navigation
this.headers = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
Object.assign(this.headers, headers)
this.baseUrl = baseUrl
this.simulatedDelay = simulatedDelay
this.devMode = devMode
}
_clearTokenAndGo () {
AsyncStorage.removeItem('apiToken')
Alert.alert(
'Error',
'Something went wrong and your account needs to be re-instantiated.',
[
{text: 'OK', onPress: () => resetRouteTo(this.navigation, 'OnboardingFirst')}
],
{ cancelable: false }
)
}
_simulateDelay () {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, this.simulatedDelay)
})
}
async _parseIfJson (response) {
var contentType = response.headers.get('content-type')
if (contentType && contentType.indexOf('application/json') !== -1) {
return response.json()
}
return null
}
async _handleError (response) {
const body = await this._parseIfJson(response)
if (!body) {
dropdownAlert('error', `Server Error ${response.status}`, 'Something went wrong on the server')
return DEFAULT_ERROR
}
switch (response.status) {
case 200: {
break
}
case 401: {
if (body.error === 'Unauthenticated.') {
this._clearTokenAndGo()
}
break
}
case 400: {
dropdownAlert('error', `Error ${body.error.code}`, body.error.message)
break
}
default: {
if (body.error) {
dropdownAlert('error', `Error ${body.error.code}`, body.error.message)
} else {
dropdownAlert('error', 'Error', `An unknown error has occurred. Http status ${response.status}`)
}
break
}
}
return body
}
_fullRoute (url) {
return `${this.baseUrl}${url}`
}
async _fetch (route, method, body, isQuery = false) {
if (!route) throw new Error('Route is undefined')
if (!store.getState().netinfo.isConnected) {
this.navigation.navigate('BlockScreen')
return {success: false, error: {code: 1, message: 'No internet connection.'}}
}
var fullRoute = this._fullRoute(route)
if (isQuery && body) {
var qs = require('qs')
const query = qs.stringify(body)
fullRoute = `${fullRoute}?${query}`
body = undefined
}
let opts = {
method,
headers: this.headers
}
if (body) {
Object.assign(opts, { body: JSON.stringify(body) })
}
const fetchPromise = () => fetch(fullRoute, opts)
if (this.devMode && this.simulatedDelay > 0) {
// Simulate an n-second delay in every request
return this._simulateDelay()
.then(() => fetchPromise())
.then(response => response.json())
} else {
let promise = await fetch(fullRoute, opts)
console.log('Logging response =>')
console.log(promise)
return this._handleError(promise)
}
}
GET (route, query) { return this._fetch(route, 'GET', query, true) }
POST (route, body) { return this._fetch(route, 'POST', body) }
PUT (route, body) { return this._fetch(route, 'PUT', body) }
DELETE (route, query) { return this._fetch(route, 'DELETE', query, true) }
}
Other files using RestClient:
import RestClient from 'util/RestClientLib'
import qs from 'qs'
import { Linking, AsyncStorage } from 'react-native'
import Config from 'react-native-config'
var SHA256 = require('crypto-js/sha256')
export default class ApiClient extends RestClient {
constructor (authToken, navigation) {
console.log('constructing apiClient with base: ', Config.API_URL)
super(Config.API_URL, navigation, {
headers: {
'Authorization': 'Bearer ' + authToken
}
})
}
_switchSchemeTo (url, scheme) {
var split = url.split(':')
split[0] = scheme
return split.join(':')
}
_makeAppUrl (url) {
const prefix = url.split('.')[0]
const bank = prefix.substr(prefix.lastIndexOf('/') + 1, prefix.length)
switch (bank) {
case 'florijnbank':
return this._switchSchemeTo(url, 'flrb')
default:
return url
}
}
async _openUrl (url) {
const openInApp = await AsyncStorage.getItem('openInApp')
const appUrl = this._makeAppUrl(url)
Linking.canOpenURL(appUrl).then(supported => {
if (!supported || openInApp !== 'true') {
Linking.canOpenURL(url).then(supported => {
if (!supported) {
console.log('Can\'t handle url: ' + url)
} else {
Linking.openURL(url)
}
}).catch(err => console.error('An error occurred', err))
} else {
Linking.openURL(appUrl)
}
}).catch(err => console.error('An error occurred', err))
}
async createToken (pin) {
var hash = SHA256(pin).toString()
const query = {pincode: hash}
let response = await this.POST('/user', query)
return response.api_token
}
async checkPin (pin) {
const hash = SHA256(pin).toString()
const query = {pincode: hash}
return this.GET('/user/validate', query)
}
getAccounts () {
return this.GET('/account')
}
getBalance (iban) {
return this.GET(`/account/${iban}`)
}
async getPermissionBank (bank) {
const query = {
bank_id: bank
}
let response = await this.POST('/account', query)
return this._openUrl(response.url)
}
postCredentialsAis (form) {
return this.POST('/account/password', form)
}
async ais (iban) {
let response = await this.GET(`/ais/${iban}`)
if (response.url == null) {
return true
}
this._openUrl(response.url)
return false
}
getTransactionDetails (requestId) {
return this.GET(`/request/${requestId}`)
}
getTransactions (iban) {
return this.GET(`/account/${iban}/transaction`)
}
getContacts () {
return this.GET('/contacts')
}
getSettings () {
return this.GET('/startup')
}
deleteAccount (iban) {
return this.DELETE(`/account/${iban}`)
}
/**
* async submitTransfer - submits a transfer
*
* #param {Object} options object which holds the pin, iban, sso and query
* #param {Object} transfer hold the transfer information
* #return {Boolean} either opens a link or a boolean for success
*/
submitTransfer (iban, credentials, transfer) {
const url = `/account/${iban}/transaction?${qs.stringify(credentials)}`
const body = {
counter_iban: transfer.counterIban,
counter_account_name: transfer.name,
amount: transfer.amount,
description: transfer.description,
reference: transfer.reference
}
return this.POST(url, body)
}
qrConfirmTransfer (transactionId, iban, credentials) {
const url = `/request/${transactionId}/confirm/${iban}?${qs.stringify(credentials)}`
return this.POST(url)
}
directConfirmTransfer (transactionId, iban, form) {
const url = `/account/${iban}/transaction/${transactionId}`
return this.POST(url, form)
}
verificationRequired (iban, amount) {
const query = {amount: amount}
return this.GET(`/veriReq/${iban}`, query)
}
};
I am using redux-thunk . Here, I have one login action. On that action I am calling an API which will give me some token, that I have to store in the state. Then immediately, after success of this action, I have to make another API request which will have this token in the header and will fetch more data. Based on this, I would like to redirect the user.
Login Action
import { generateToken } from '../APIs/login';
import HttpStatus from 'http-status-codes';
import { LOGIN_FAILED, LOGIN_SUCCESS } from '../constants/AppConstants';
import { fetchUserJd } from './GetUserJd';
import history from '../history';
export function fetchToken(bodyjson) {
return (dispatch) => {
getLoginDetails(dispatch, bodyjson);
}
}
export function getLoginDetails(dispatch, bodyjson) {
generateToken(bodyjson)
.then((response) => {
if (response.status === 200)
dispatch(sendToken(response.payload))
else
dispatch(redirectUser(response.status));
})
}
export function sendToken(data) {
return {
type: LOGIN_SUCCESS,
data: data,
}
}
export function redirectUser(data) {
return {
type: LOGIN_FAILED,
data: data,
}
}
2nd Action
import { FETCHING_JOBDESCRIPTION_SUCCESS, FETCHING_DATA_FAILED,FETCHING_JOBS } from '../constants/AppConstants';
import { getUserJobs } from '../APIs/GetUserJd';
import history from '../history';
export function fetchUserJd(token) {
console.log(token);
return (dispatch) => {
dispatch(fetchingJobDescription());
}
};
export function getUserJd(dispatch, token) {
getUserJobs(token)
.then((response) => {
if (response.status === 200)
dispatch(sendUserJd(response.payload))
else
dispatch(fetchFailure(response.status));
})
}
export function fetchFailure(data) {
return {
type: FETCHING_DATA_FAILED,
data: data,
}
}
export function sendUserJd(data) {
return {
type: FETCHING_JOBDESCRIPTION_SUCCESS,
data: data,
}
}
export function fetchingJobDescription() {
return {
type: FETCHING_JOBS
}
}
Calling this from
handleClick(event) {
event.preventDefault();
var bodyJson = {
"username": this.state.UserName,
"password": this.state.password
}
this.props.fetchToken(bodyJson);
}
How can I call that second action immediately after the success of the first request. ?
Tried way ->
componentWillReceiveProps(newProps) {
console.log(newProps.token);
if(newProps.token) {
this.props.fetchUserJd(newProps.token);
}
}
export function sendUserJd(data) {
if (data.data.length > 0) {
history.push('/userJobs');
} else {
history.push('/createJob');
}
return {
type: FETCHING_JOBDESCRIPTION_SUCCESS,
data: data,
}
}
You can do without setting it to redux state. You need to return your success action call to get the token in component itself using promise .then and then call this.props.sendToken(token); which will actually set the data in state and follows your existing flow.
handleClick(event) {
event.preventDefault();
var bodyJson = {
"username": this.state.UserName,
"password": this.state.password
}
this.props.getLoginDetails(bodyJson).then((token) => {
this.props.sendToken(token);
});
}
And in actions
const GET_LOGIN_DETAILS_SUCCESS = 'GET_LOGIN_DETAILS_SUCCESS';
export function getLoginDetailsSuccess(data) {
return {
type: GET_LOGIN_DETAILS_SUCCESS,
data: data,
}
}
export function getLoginDetails(bodyjson) {
generateToken(bodyjson)
.then((response) => {
if (response.status === 200)
return dispatch(getLoginDetailsSuccess(response.payload))
else
dispatch(redirectUser(response.status));
})
}
Let me know if you have any questions or if you feel difficult to understand
i am curently stuck in trying to set up an API on a react application. I did test my access token manually and it worked
https://api.instagram.com/v1/users/self/media/recent/?access_token=${accessToken}
but i am stuck when its about to import theses data into the statement of the app.
I check the architecture of the component adding manually url into the state and it worked out, so i get the probleme is probably in the function that i called diplayInsta() or in the util/insta.js method
const Insta = {
getAccessToken() {
//check if token exist
if (accessToken) {
return new Promise(resolve => resolve(accessToken));
}
//token ref
return fetch(`https://api.instagram.com/oauth/authorize/? client_id=${client_id}&redirect_uri=${redirectURI}&response_type=token`, {
method: 'POST'
}).then(response => {
return response.json();
}).then(jsonResponse => {
accessToken = jsonResponse.access_token;
});
},
async display() {
if (!accessToken) {
this.getAccessToken();
}
try {
let response = await fetch(`https://api.instagram.com/v1/users/self/media/recent/?access_token=${accessToken}`, {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`
}
});
if (response.ok) {
let jsonResponse = await response.json();
let medias = jsonResponse.medias.data.map(media => ({
id: media.data.id,
image: media.data.images.standard_resolution.url
}));
return medias;
}
throw new Error('Request failed!');
} catch (error) {
console.log(error);
}
}
}
Here is my github link of the project where you will found the whole project. https://github.com/erwanriou/reactisbroken
I feel that its probably a very small mistake but i can't found where it is...I a good soul could help me to show me the good direction...
UPDATE - After the first answer i did actualise the code and i now resolve the access token part but still stuck in displaying it into the state of the App.js That is my major problem curently
here a screen of the working promise :
https://www.dropbox.com/s/w9wh3h3m58r3n06/Promise.jpg?dl=0
Ok i found myself the solution. i put it here in case some of you are block in a similar problem :
let accessToken;
const Insta = {
getAccessToken() {
if (accessToken) {
return new Promise(resolve => resolve(accessToken));
}
const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);
if (accessTokenMatch) {
accessToken = accessTokenMatch[1];
return accessToken;
} else {
const Url = `https://api.instagram.com/oauth/authorize/?client_id=${client_id}&redirect_uri=${redirectURI}&response_type=token`
window.location = Url;
}
},
async display() {
if (!accessToken) {
this.getAccessToken();
}
try {
let response = await fetch(`https://api.instagram.com/v1/users/self/media/recent/?access_token=${accessToken}`, {
method: 'GET'
});
if (response.ok) {
console.log(response);
let jsonResponse = await response.json();
let medias = jsonResponse.data.map(media => ({
id: media.id,
image: media.images.standard_resolution.url
}));
return medias;
}
throw new Error('Request failed!');
} catch (error) {
console.log(error);
}
}
}
Now the part to import the fetch data into the state is :
import React, { Component } from 'react';
import './App.css';
import Header from '../Header/Header.js';
import MediaList from '../MediaList/MediaList.js';
import Insta from '../../util/insta.js';
class App extends Component {
constructor(props) {
super(props);
this.state = {
mediasResult: [],
accountName: 'Erwan'
};
}
componentDidMount() {
Insta.display().then(medias => this.setState({mediasResult: medias}));
}
render() {
return (
<div className="App">
<Header accountName={this.state.accountName}/>
<MediaList medias={this.state.mediasResult}/>
</div>
);
}
}
export default App;
In fact the componentDidMount save my day. Its that let you import fetch data into the state.