vue interceptors retry failed request and get response from component? - javascript

How I can get the response from vue axios interceptors to component where the request failed when I retry the failed request inside the interceptor?
code:
Component1.vue
axios.get('/api-1').then((response) => {
// Do something here
});
Component2.vue (failed API request)
axios.get('/api-2').then((response) => {
// Do something here
});
code in Interceptors.js
axios.interceptors.response.use(function (config) {
// Do something before request is sent
return config;
}, async function (error) {
// Do something in Error response
const status = error?.response?.status || 0
const originalRequest = error.config;
if (status === 401) { {
try {
setTimeout(async () => {
const tokenData = await axios.get('/refresh-token');
return axios(originalRequest); <-- how can I get this response to the components with failed API request?
}, 2000);
} catch (error) {
return false;
}
}
return Promise.reject(error);
});
how can I get the response from interceptors.js to Component2.vue without using vuex?

Related

Avoid multiple time api calls when user clicks on button accidently multiple times in javascript/React.js [duplicate]

I use axios for ajax requests and reactJS + flux for render UI. In my app there is third side timeline (reactJS component). Timeline can be managed by mouse's scroll. App sends ajax request for the actual data after any scroll event. Problem that processing of request at server can be more slow than next scroll event. In this case app can have several (2-3 usually) requests that already is deprecated because user scrolls further. it is a problem because every time at receiving of new data timeline begins redraw. (Because it's reactJS + flux) Because of this, the user sees the movement of the timeline back and forth several times. The easiest way to solve this problem, it just abort previous ajax request as in jQuery. For example:
$(document).ready(
var xhr;
var fn = function(){
if(xhr && xhr.readyState != 4){
xhr.abort();
}
xhr = $.ajax({
url: 'ajax/progress.ftl',
success: function(data) {
//do something
}
});
};
var interval = setInterval(fn, 500);
);
How to cancel/abort requests in axios?
Axios does not support canceling requests at the moment. Please see this issue for details.
UPDATE: Cancellation support was added in axios v0.15.
EDIT: The axios cancel token API is based on the withdrawn cancelable promises proposal.
UPDATE 2022: Starting from v0.22.0 Axios supports AbortController to cancel requests in fetch API way:
Example:
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// cancel the request
controller.abort()
Using useEffect hook:
useEffect(() => {
const ourRequest = Axios.CancelToken.source() // <-- 1st step
const fetchPost = async () => {
try {
const response = await Axios.get(`endpointURL`, {
cancelToken: ourRequest.token, // <-- 2nd step
})
console.log(response.data)
setPost(response.data)
setIsLoading(false)
} catch (err) {
console.log('There was a problem or request was cancelled.')
}
}
fetchPost()
return () => {
ourRequest.cancel() // <-- 3rd step
}
}, [])
Note: For POST request, pass cancelToken as 3rd argument
Axios.post(`endpointURL`, {data}, {
cancelToken: ourRequest.token, // 2nd step
})
Typically you want to cancel the previous ajax request and ignore it's coming response, only when a new ajax request of that instance is started, for this purpose, do the following:
Example: getting some comments from API:
// declare an ajax request's cancelToken (globally)
let ajaxRequest = null;
function getComments() {
// cancel previous ajax if exists
if (ajaxRequest ) {
ajaxRequest.cancel();
}
// creates a new token for upcomming ajax (overwrite the previous one)
ajaxRequest = axios.CancelToken.source();
return axios.get('/api/get-comments', { cancelToken: ajaxRequest.token }).then((response) => {
console.log(response.data)
}).catch(function(err) {
if (axios.isCancel(err)) {
console.log('Previous request canceled, new request is send', err.message);
} else {
// handle error
}
});
}
import React, { Component } from "react";
import axios from "axios";
const CancelToken = axios.CancelToken;
let cancel;
class Abc extends Component {
componentDidMount() {
this.Api();
}
Api() {
// Cancel previous request
if (cancel !== undefined) {
cancel();
}
axios.post(URL, reqBody, {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
}),
})
.then((response) => {
//responce Body
})
.catch((error) => {
if (axios.isCancel(error)) {
console.log("post Request canceled");
}
});
}
render() {
return <h2>cancel Axios Request</h2>;
}
}
export default Abc;
There is really nice package with few examples of usage called axios-cancel.
I've found it very helpful.
Here is the link: https://www.npmjs.com/package/axios-cancel
https://github.com/axios/axios#cancellation
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
let url = 'www.url.com'
axios.get(url, {
progress: false,
cancelToken: source.token
})
.then(resp => {
alert('done')
})
setTimeout(() => {
source.cancel('Operation canceled by the user.');
},'1000')
This is how I did it using promises in node. Pollings stop after making the first request.
var axios = require('axios');
var CancelToken = axios.CancelToken;
var cancel;
axios.get('www.url.com',
{
cancelToken: new CancelToken(
function executor(c) {
cancel = c;
})
}
).then((response) =>{
cancel();
})
Using cp-axios wrapper you able to abort your requests with three diffent types of the cancellation API:
1. Promise cancallation API (CPromise):
Live browser example
const cpAxios= require('cp-axios');
const url= 'https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=5s';
const chain = cpAxios(url)
.timeout(5000)
.then(response=> {
console.log(`Done: ${JSON.stringify(response.data)}`)
}, err => {
console.warn(`Request failed: ${err}`)
});
setTimeout(() => {
chain.cancel();
}, 500);
2. Using AbortController signal API:
const cpAxios= require('cp-axios');
const CPromise= require('c-promise2');
const url= 'https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=5s';
const abortController = new CPromise.AbortController();
const {signal} = abortController;
const chain = cpAxios(url, {signal})
.timeout(5000)
.then(response=> {
console.log(`Done: ${JSON.stringify(response.data)}`)
}, err => {
console.warn(`Request failed: ${err}`)
});
setTimeout(() => {
abortController.abort();
}, 500);
3. Using a plain axios cancelToken:
const cpAxios= require('cp-axios');
const url= 'https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=5s';
const source = cpAxios.CancelToken.source();
cpAxios(url, {cancelToken: source.token})
.timeout(5000)
.then(response=> {
console.log(`Done: ${JSON.stringify(response.data)}`)
}, err => {
console.warn(`Request failed: ${err}`)
});
setTimeout(() => {
source.cancel();
}, 500);
4. Usage in a custom React hook (Live Demo):
import React from "react";
import { useAsyncEffect } from "use-async-effect2";
import cpAxios from "cp-axios";
/*
Note: the related network request will be aborted as well
Check out your network console
*/
function TestComponent({ url, timeout }) {
const [cancel, done, result, err] = useAsyncEffect(
function* () {
return (yield cpAxios(url).timeout(timeout)).data;
},
{ states: true, deps: [url] }
);
return (
<div>
{done ? (err ? err.toString() : JSON.stringify(result)) : "loading..."}
<button onClick={cancel} disabled={done}>
Cancel async effect (abort request)
</button>
</div>
);
}
Update
Axios v0.22.0+ supports AbortController natively:
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// cancel the request
controller.abort()
Starting from v0.22.0 Axios supports AbortController to cancel requests in fetch API way:
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// cancel the request
controller.abort()
CancelToken deprecated
You can also cancel a request using a CancelToken.
The axios cancel token API is based on the withdrawn cancelable promises proposal.
This API is deprecated since v0.22.0 and shouldn't be used in new projects
You can create a cancel token using the CancelToken.source factory as shown below:
import {useState, useEffect} from 'react'
export function useProfileInformation({accessToken}) {
const [profileInfo, setProfileInfo] = useState(null)
useEffect(() => {
const abortController = new AbortController()
window
.fetch('https://api.example.com/v1/me', {
headers: {Authorization: `Bearer ${accessToken}`},
method: 'GET',
mode: 'cors',
signal: abortController.signal,
})
.then(res => res.json())
.then(res => setProfileInfo(res.profileInfo))
return function cancel() {
abortController.abort()
}
}, [accessToken])
return profileInfo
}
// src/app.jsx
import React from 'react'
import {useProfileInformation} from './hooks/useProfileInformation'
export function App({accessToken}) {
try {
const profileInfo = useProfileInformation({accessToken})
if (profileInfo) {
return <h1>Hey, ${profileInfo.name}!</h1>
} else {
return <h1>Loading Profile Information</h1>
}
} catch (err) {
return <h1>Failed to load profile. Error: {err.message}</h1>
}
}

Not printing http statusCode in react project

Its react project and using axios for server communication
I am experimenting server error response (I just put down server for testing)
Problem is I could not track server error status code (I tried to print in console) but I can see status code result in network tab
login.js
import { Service } from "./service";
const loginGo = async (e) => {
let response = await Service(formData);
console.log(response)
}
=====
service.js
export const Service = (data) => {
let response = new Promise((resolve, reject) => {
axios.post('/api/auth/login', data, {
})
.then(response => {
resolve(response);
}, error => {
reject(error)
});
});
return response;
};
I am not sure this is correct way of calling http request, Please correct me if its wrong way
axios.get('/user/12345').then(function (response) {
console.log(response.status);
});

How to Redirect if API call fails in React Js?

I'm trying to implement Redirect in my react js App so if API call returns status other that 200, I can redirect users to according pages. The problem is that Redirect doesn't work. My code so far:
function catchErr(res) {
try {
if (res.status === 200) {
return res.json();
} else if (res.status === 404) {
<Redirect to="/404" // doesn't redirect to this route
console.log("404") // prints 404
throw Error(res.status);
}
else {
if (res.ok) {
return res.data;
}
}
} catch (err) {
console.log(err);
}
}
export async function getData() {
let getParams = {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
};
const data = await fetch(url, obj)
.then((res) => catchErr(res))
.then((res) => {
return res.data;
});
return data;
}
My api call is a function in separate file, it's not react functional component so I can't use useState hook to store state or use useHistory(history.push) inside getData() function. At the moment api call works great, catchErr() console.loges 404 if res.status === 404 but doesn't redirect to "/404" as I need to. I can't figure out why Redirect wouldn't work in this case, please help.
You can't call JSX like that in the middle of a function. You'll need to refactor your code to handle the failures in a React component and either set some state to conditionally render a Redirect component into the DOM or access the history object to do an imperative redirect, i.e. history.replace.
Here's an example component using history.replace.
import { useHistory } from 'react-router-dom';
const MyComponent = () => {
const history = useHistory();
...
useEffect(() => {
const fetchData = async () => {
// set any loading state
try {
const response = await fetch(url, options);
// process response, throw error if STATUS 404
} catch(error) {
// handle any error responses and redirect
history.replace("/404");
} finally {
// clear any loading state
}
}
fetchData();
}, []);
...
return (
<div>My JSX</div>
)
};

Axios override, get status code from the data response instead of status

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);
});

How to handle net::ERR_CONNECTION_REFUSED in Axios - Vue.js

Below is my connection request code :
doLogin(this.login).then(response => {
var token = response.data.token;
localStorage.setItem('AUTH_TOKEN', JSON.stringify(token));
this.$router.push({name: 'Devices'});
});
}).catch(error => {
console.log(error.response.data.message);
});
the catch() part works fine for http errors (like 400, 404, 403..etc). But when my server is offline this script just throws net::ERR_CONNECTION_REFUSED. Is there any way to handle this error and let the front-end user know that the server is currently offline.?
Here is the doLogin() function just in case,
function doLogin(data) {
const url = 'http://localhost:3000/authenticate';
return axios.post(url,data);
}
You can try this in the catch part:
catch(error => {
if (!error.response) {
// network error
this.errorStatus = 'Error: Network Error';
} else {
this.errorStatus = error.response.data.message;
}
})
You may use interceptors:
axios.interceptors.response.use(
response => {
return response
},
error => {
if (!error.response) {
console.log("Please check your internet connection.");
}
return Promise.reject(error)
}
)
You should do the same validation that #chithra pointed out in the .then() because i'm having a weird issue when testing requests with my servers down, the "response" comes as if it was a success.
Also, on the .then() do it with response.status instead of response.error
By using npm; a standard package manager for the JavaScript runtime environment Node.js.
With npm:
npm i axios
Next, you should import Axios in your src/App.vue file
import axios from 'axios';
you will need to call it on a lifecycle hook. Here we will use the beforeCreated() lifecycle hook, this is because we will be able to retrieve sensitive data and events that are active with the beforeCreated hook.
data() {
return {
network: false,
}; },
beforeCreate() {
axios
.get("http://localhost:13172/api/product/getproducts")
.then((response) => {
console.log(response);
this.network = true;
})
.catch((error) => {
console.log(error), (this.network = false);
}); }

Categories