Alert() pops up twice in axios interceptor (Vue.js) - javascript

In my Vue app, I instantiate a single instance of axios and use it across the app for HTTP requests. I have set up a response interceptor which checks if any response from the backend is 401 unauthorized, and if so, shows an alert message. This basic flow has been implemented, but you need to hit "OK" on the alert message twice for it to go away, and I am not sure why.
Axios instance:
import axios, { AxiosError, AxiosInstance, AxiosResponse } from 'axios';
const axiosInstance: AxiosInstance = axios.create();
axiosInstance.interceptors.response.use(
(response: AxiosResponse) => response,
(error: AxiosError) => {
if(error.response && error.response.status === 401) {
alert('There has been an issue. Please log out and then log in again.');
return Promise.reject(error);
}
}
);
export default axiosInstance;
The request whose response is being intercepted:
import axiosInstance from 'axios-instance';
public async getloggedInUserId() {
await axiosInstance.get('/sessions.json')
.then((response) => {
if(response.data.user_id) {
this.SET_USER_ID(response.data.user_id);
}
});
}
I've read this thread, but my issue seems to be different: Javascript alert shows up twice
I've tried changing the return statement from return Promise.reject(error); to return false; but that did nothing for me.

As Phil suggested in the comment above, looking at at the Network tab in the browser console helped me solve the issue. The console showed how each component in the page was being loaded along with the resulting response code. In short, two processes were actually returning 401, which was the reason why the alert was being called twice.
I have decided to move the code that calls alert from a global axios interceptor (called whenever any process returns 401) to a .catch block inside one specific axios process, so that it only gets called once.

Your promise throws error in axios error interceptor, and error called second times.

Related

Axios response interceptor - get API response for non 2xx http statuses

I have a custom Axios instance that uses interceptors to return responses. According to the Axios docs, the success interceptor is called whenever there is a 2xx status and the error interceptor is called if it's any status other than 2xx. I want to display an error dialog when the error interceptor is called.
The problem: I want to display the error message coming from the API response in the dialog. For instance, the API may respond with 401 status and still have a custom response with user friendly error messages. However, I am not sure how to obtain the response in the error interceptor function itself.
const responseErrorInterceptor = async (error: AxiosError): ServiceResult<AxiosError> => {
if (error.response) {
store.dispatch(setErrorDialog(undefined, /*display api error response here*/));
//right now it just displays the unfriendly Axios error content
}
return Promise.reject(error);
};
Any ideas if it's possible to achieve this?
Yes, it is possible to achieve this. The AxiosError object passed to the error interceptor function contains a response property which contains the response data from the API. You can use this to get the user friendly error message and display it in the dialog.
For example:
const responseErrorInterceptor = async (error: AxiosError): ServiceResult<AxiosError> => {
if (error.response) {
const userFriendlyErrorMessage = error.response.data.errorMessage;
store.dispatch(setErrorDialog(undefined, userFriendlyErrorMessage));
}
return Promise.reject(error);
};

Unable to dispatch redux actions in axios success response interceptor

Usually we dispatch actions in Axios error response interceptor and it works fine
axiosInstance.interceptors.response.use(
(next) => {
return Promise.resolve(next);
},
(error) => {
// Dispatching to handle error 1
if (error.response.status === 401) {
if (error.response.data.errorCode === 'USER_LOGIN_ERROR_008') {
store.dispatch({ type: 'RESET_SECURITY_QUESTION', payload: { id: null, qid: null, question: null } });
}
throw error;
}
);
Every successful API response sets a token in user's cookies and if no API is called in the next X minutes then that token will expire
So after every successful API response I need to start a timer in my App.js and once that timer ends would show a Modal to the user that his session has been invalidated
However if a user's actions result in successful API calls then I want to reset that timer
To reset that timer I would want to call a redux action after every successful API response
So this is what I tried to do
axiosInstance.interceptors.response.use(
(next) => {
console.log(`Intercepting ${next.config.url} success response`, next);
if (next.status === 200) {
store.dispatch(incrementSessionRefreshTimer());
}
return Promise.resolve(next);
},
There is an problem here
Whenever we use axios interceptor to intercept success request, We can modify the request and have to return the request back
What happens in my code is that my action is dispatched but the response is never returned, No lines below store.dispatch(myAction()) are executed and hence the Component in which I made an API call does not get the success response
Any workaround for this?
The code should definitely continue executing after that dispatch. It might cause an immediate rerender, in which case the rerender might happen before the next line, but unless an exception is thrown anywhere, that return Promise.resolve(next); line will execute. I think your problem lies elsewhere.

Can We Explicitly Catch Puppeteer (Chrome/Chromium) Error net::ERR_ABORTED?

Can we explicitly and specifically catch Puppeteer (Chromme/Chromium) error net::ERR_ABORTED? Or is string matching the only option currently?
page.goto(oneClickAuthPage).catch(e => {
if (e.message.includes('net::ERR_ABORTED')) {}
})
/* "net::ERROR_ABORTED" occurs for sub-resources on a page if we navigate
* away too quickly. I'm specifically awaiting a 302 response for successful
* login and then immediately navigating to the auth-protected page.
*/
await page.waitForResponse(res => res.url() === href && res.status() === 302)
page.goto(originalRequestPage)
Ideally, this would be similar to a potential event we could catch with page.on('requestaborted')
I'd recommend putting your api calls and so in a trycatch block
If it fails, you catch the error, like you are currently doing. But it just looks a bit nicer
try {
await page.goto(PAGE)
} catch(error) {
console.log(error) or console.error(error)
//do specific functionality based on error codes
if(error.status === 300) {
//I don't know what app you are building this in
//But if it's in React, here you could do
//setState to display error messages and so forth
setError('Action aborted')
//if it's in an express app, you can respond with your own data
res.send({error: 'Action aborted'})
}
}
If there are not specific error codes in the error responses for when Puppeteer is aborted, it means that Puppeteer's API has not been coded to return data like that, unfortunately :')
It's not too uncommon to do error messages checks like you are doing in your question. It's, unfortunately, the only way we can do it, since this is what we're given to work with :'P

Returning Error Values Through Axios/Express To React App

I've got a handleSubmit function that fetches data from my backend as part of a larger component. I'd like to send the error information to my redux store and/or local component when the back-end fails, but am unable to do so.
The handleSubmit function looks like this (it's using React hooks, which are wired up correctly. Can post the full component if that is useful):
const handleSubmit = async (e, { dataSource, filter, filterTarget }) => {
e.preventDefault();
setIsLoading(true);
setErrorValue(null);
setError(false);
const token = localStorage.JWT_TOKEN;
const link = filterTarget === "Identifier" ? `http://localhost:8081/api/${dataSource}/${filter}`: `http://localhost:8081/api/${dataSource}?filter=${filter}&filterTarget=${filterTarget}`;
try {
let data = await axios.get(link, { headers: { authorization: token }});
props.setData(data);
setError(false);
setIsLoading(false);
} catch (err){
setErrorValue(err.message);
setError(true);
setIsLoading(false);
};
};
I'm intentionally making bad requests through the form, which will trigger an error in my backend. These are handled through my custom Express middleware function, which looks like this (I'll add more once I get this framework to work):
handleOtherError: (error, req, res, next) => { // Final custom error handler, with no conditions. No need to call next() here.
console.log("This error handler is firing!");
return res.status(500).json({
message: error.message,
type: "ServerError"
});
}
I know that this function is firing because the console.log statement is appearing on my server, and if I change the status code, so does the status code error on the front-end in my Google Chrome console.
In fact, if I go to the network tab, the correct error information appears for my request. Here's the video of me making a bad request:
However, when I try to access the err.message on my front-end, I'm not able to do so. The err.message in my try/catch handler for the handleSubmit function only ever gives me the Request failed with status code XXX
What am I doing wrong?
See https://github.com/axios/axios#handling-errors
You can access the response by using err.response.data.message, not err.message.
Found the answer posted elsewhere: https://github.com/axios/axios/issues/960
Apparently, to get the message, you have to use err.response.data.message
Simply using "err" will only give you a basic string respresentation of the error.

Angular 2 Http get not triggering

As said in the title, nothing is happening when I subscribe to my observable. There is no error in the console or during the build. Here is my code :
My service
getBlueCollars(): Observable<BlueCollar[]> {
return this.http.get(this.defaultAPIURL + 'bluecollar?limit=25').map(
(res: Response) => {
return res.json();
});
}
My component
ngOnInit() {
this.planifRequestService.getBlueCollars().subscribe(
data => {
this.blueCollars = data;
console.log('Inner Blue Collars', this.blueCollars);
},
err => console.log(err)
);
console.log('Value BlueCollars : ', this.blueCollars);
}
So the second console.log is triggering with "Value BlueCollars : Undefined", and the log in my subscribe is never showed. As well, I can't see the request sent in the Networt tab of Chrome.
So I tried to simplify everything with the following code :
let response: any;
this.http.get('myUrl').subscribe(data => response = data);
console.log('TestRep: ', response);
Same problem here, no error, response is undefined. It seems the subscribe is not triggering the observable. (The URL is correct, it is working on my swagger or with postman.)
I'm on Angular 2.4.9
Edit
So I tried to copy/past the code of my request on a brand new project, everything is working fine. The request is triggered and I can get the JSON response correctly. So there is something maybe on the configuration of my project that is forbiding the request to trigger correctly.
Ok just found what was going on. I am using a fake backend in order to try my login connexions that is supposed to catch only specified URL. However for wathever raison it was catching all the requests, so that explain everything. Thx for your help everybody.
Try adding a catch block to your service code:
getBlueCollars(): Observable<BlueCollar[]> {
return this.http.get(this.defaultAPIURL + 'bluecollar?limit=25')
.map(
(res: Response) => {
return res.json();
})
.catch(err => Observable.throw(err))
}
Don't forget to
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';`
I imagine this will result in the error that'll give you an idea where your code is going wrong.
The reason the console.log outside the subscribe call is undefined is because the subscribe/http call is happening asynchronously and so, in effect, the order (in time!) the code is running is:
1) the observable is subscribed to (and then waits for a response)
2) the outer console log runs with blueCollars undefined
3) when the response (or error) comes back from the http request (potentially after several seconds), only then will the inner assignment of this.blueCollar = data happen (and the inner console log), OR an error will get logged
Apart from that the subscribe code looks fine...!

Categories

Resources