I am using Vuejs 2.6.11 with Vuex 3.2.0 and axios v0.19.2 and have a vary peculiar issue that I also reported on in this stackoverflow questions : Every Second Vuex Commit to the same action is slow
I have eliminated the backend as not being the issue as I am calling the same backend endpoint from an Angular 1 app and other places with no issues with all calls being consistently fast after the first call.
I also tested the axios call without using vuex directly in the component, but I get exactly the same issue.
Below is the axios call in my local component.
async getDataLocal() {
// this.showCollapse = !this.showCollapse
// this.$store.dispatch('getData', {TableId: this.TableId, ViewId: this.ViewId, PageNumber: this.PageNumber, PageSize: this.PageSize })
await this.$axios
.$get(
'/scad/TablesV2/' +
this.TableId +
'/data?ViewId=' +
this.ViewId +
'&PageNumber=' +
this.PageNumber +
'&PageSize=' +
this.PageSize
)
.then(
response => {
this.items = response
},
error => {
reject(error)
}
)
},
and here is the results from network traffic in Chrome. I did the calls about 1 second apart.
Below is the code for my vuex action.
return new Promise((resolve, reject) => {
let ParameterList = payload.ParameterList || {}
vuexContext.commit('loading', true)
this.$axios
.$get(
'/scad/tablesv2/' +
payload.TableId +
'/data.json?ViewId=' +
payload.ViewId +
'&PageNumber=' +
payload.PageNumber +
'&PageSize=' +
payload.PageSize,
{
params: { ParameterList: payload.ParameterList },
crossdomain: true,
withCredentials: true
}
)
.then(
response => {
const result = {
TableId: payload.TableId,
ViewId: payload.ViewId,
PageNumber: payload.PageNumber,
PageSize: payload.PageSize,
data: response
}
vuexContext.commit('getData', result)
resolve(result)
},
error => {
vuexContext.commit('setError', error.response)
reject(error)
}
)
vuexContext.commit('loading', false)
})
and here is the network traffic in Chrome to the same endpoint. Again 1 second apart
and here is the network traffic to the same endpoint from an angular 1 app.
I searched the internet but was unable to find anything related.
I ran into the same issue while working on a vue.js application with axios.
I found that the delay is due to using localhost in a Chromium-based browser on Windows OS. When viewing the details of each network request you can see that every second axios request is performing a DNS lookup.
If you replace localhost in your address bar with 127.0.0.1 the delay will be gone.
Related
This is a strange one, but here's the situation.
I'm using Next.js with the Next-auth package to handle authentication.
I'm not using Server-Side rendering, it's an admin area, so there is no need for SSR, and in order to authenticate users, I've created a HOC to wrap basically all components except for the "/sign-in" route.
This HOC all does is check if there's a session and then adds the "access token" to the Axios instance in order to use it for all async calls, and if there is no session, it redirects the user to the "sign-in" page like this ...
const AllowAuthenticated = (Component: any) => {
const AuthenticatedComponent = () => {
const { data: session, status }: any = useSession();
const router = useRouter();
useEffect(() => {
if (status !== "loading" && status === "unauthenticated") {
axiosInstance.defaults.headers.common["Authorization"] = null;
signOut({ redirect: false });
router.push("/signin");
} else if (session) {
axiosInstance.defaults.headers.common["Authorization"] = `Bearer ${session.accessToken.accessToken}`;
}
}, [session, status]);
if (status === "loading" || status === "unauthenticated") {
return <LoadingSpinner />;
} else {
return <Component />;
}
};
return AuthenticatedComponent;
};
export default AllowAuthenticated;
And in the Axios instance, I'm checking if the response is "401", then I log out the user and send him to the "sign-in" screen, like this ...
axiosInstance.interceptors.response.use(
response => response,
error => {
const { status } = error.response;
if (status === 401) {
axiosInstance.defaults.headers.common["Authorization"] = null;
signOut({ redirect: false });
return Promise.reject(error);
}
return Promise.reject(error);
},
);
Very simple stuff, and it works like a charm until I decided to upgrade my project to use "react 18.1.0" and "react-dom 18.1.0", then all of a sudden, my API calls doesn't get the "Authorization" header and they return "401" and the user gets logged out :(
If I tried to make an API call inside the HOC right after I set the Auth headers it works, sot I DO get the "token" from the session, but all the async dispatch calls inside the wrapped component return 401.
I forgot to mention, that this issue happens on page refresh, if I didn't refresh the page after I sign in, everything works great, but once I refresh the page the inner async dispatch calls return 401.
I Updated all the packages in my project including Axios and next-auth, but it didn't help.
I eventually had to downgrade back to "react 17.0.2" and everything works again.
Any help is much appreciated.
For those of you who might come across the same issue.
I managed to solve this by not including the logic for adding the token to the "Authorization" header inside the HOC, instead, I used a solution by #kamal-choudhary on a post on Github talking about how to add "JWT" to every axios call using next-auth.
Using #jaketoolson help at that Github post, he was able to attach the token to every "Axios" call.
The solution is basically to create an Axios instance and add an interceptor like I was doing above, but not just for the response, but also for request.
You'll add an interceptor for every request and check if there's a session, and then attach the JWT to the Authorization header.
That managed to solve my issue, and now next-auth works nicely with react 18.
Here's the code he's using ...
import axios from 'axios';
import { getSession } from 'next-auth/react';
const baseURL = process.env.SOME_API_URL || 'http://localhost:1337';
const ApiClient = () => {
const defaultOptions = {
baseURL,
};
const instance = axios.create(defaultOptions);
instance.interceptors.request.use(async (request) => {
const session = await getSession();
if (session) {
request.headers.Authorization = `Bearer ${session.jwt}`;
}
return request;
});
instance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
console.log(`error`, error);
},
);
return instance;
};
export default ApiClient();
Don't forget to give them a thumbs up for their help if it works for you ...
https://github.com/nextauthjs/next-auth/discussions/3550#discussioncomment-1993281
https://github.com/nextauthjs/next-auth/discussions/3550#discussioncomment-1898233
In nuxtjs project, I created an auth middleware to protect page.
and using vuex-persistedstate (also tried vuex-persist and nuxt-vuex-persist) to persist vuex store.
Everything is working fine when navigating from page to page, but when i refresh page or directly land to protected route, it redirect me to login page.
localStorage plugin
import createPersistedState from 'vuex-persistedstate'
export default ({ store }) => {
createPersistedState({
key: 'store-key'
})(store)
}
auth middleware
export default function ({ req, store, redirect, route }) {
const userIsLoggedIn = !!store.state.auth.user
if (!userIsLoggedIn) {
return redirect(`/auth/login?redirect=${route.fullPath}`)
}
return Promise.resolve()
}
I solved this problem by using this plugin vuex-persistedstate instead of the vuex-persist plugin. It seems there's some bug (or probably design architecture) in vuex-persist that's causing it.
With the Current approach, we will always fail.
Actual Problem is Vuex Store can never be sync with server side Vuex store.
The fact is we only need data string to be sync with client and server (token).
We can achieve this synchronization with Cookies. because cookies automatically pass to every request from browser. So we don't need to set to any request. Either you just hit the URL from browser address bar or through navigation.
I recommend using module 'cookie-universal-nuxt' for set and remove of cookies.
For Setting cookie after login
this.$cookies.set('token', 'Bearer '+response.tokens.access_token, { path: '/', maxAge: 60 * 60 * 12 })
For Removing cookie on logout
this.$cookies.remove('token')
Please go through the docs for better understanding.
Also I'm using #nuxt/http module for api request.
Now nuxt has a function called nuxtServerInit() in vuex store index file. You should use it to retrieve the token from request and set to http module headers.
async nuxtServerInit ({dispatch, commit}, {app, $http, req}) {
return new Promise((resolve, reject) => {
let token = app.$cookies.get('token')
if(!!token) {
$http.setToken(token, 'Bearer')
}
return resolve(true)
})
},
Below is my nuxt page level middleware
export default function ({app, req, store, redirect, route, context }) {
if(process.server) {
let token = app.$cookies.get('token')
if(!token) {
return redirect({path: '/auth/login', query: {redirect: route.fullPath, message: 'Token Not Provided'}})
} else if(!isTokenValid(token.slice(7))) { // slice(7) used to trim Bearer(space)
return redirect({path: '/auth/login', query: {redirect: route.fullPath, message: 'Token Expired'}})
}
return Promise.resolve()
}
else {
const userIsLoggedIn = !!store.state.auth.user
if (!userIsLoggedIn) {
return redirect({path: '/auth/login', query: {redirect: route.fullPath}})
// return redirect(`/auth/login?redirect=${route.fullPath}`)
} else if (!isTokenValid(store.state.auth.tokens.access_token)) {
return redirect({path: '/auth/login', query: {redirect: route.fullPath, message: 'Token Expired'}})
// return redirect(`/auth/login?redirect=${route.fullPath}&message=Token Expired`)
} else if (isTokenValid(store.state.auth.tokens.refresh_token)) {
return redirect(`/auth/refresh`)
} else if (store.state.auth.user.role !== 'admin')
return redirect(`/403?message=Not having sufficient permission`)
return Promise.resolve()
}
}
I have write different condition for with different source of token, as in code. On Server Process i'm getting token from cookies and on client getting token store. (Here we can also get from cookies)
After this you may get Some hydration issue because of store data binding in layout. To overcome this issue use <no-ssr></no-ssr> wrapping for such type of template code.
I have the followwing two methods in an vuejs component:
openLender: function(setUrl) {
const win = window.open(setUrl, '_blank');
win.focus();
},
getLender: function(setEnt, setEx, setRev, setCred, setDur, checkWishes, lender) {
const vm = this;
const request = 'lender_click';
const setWishes = vm.arrayToString(checkWishes);
axios({
method:'get',
url:'/api',
params: {
request: request,
setent: setEnt,
setex: setEx,
setrev: setRev,
setcred: setCred,
setdur: setDur,
setwishes: setWishes,
setlender: lender
},
responseType:'json'
})
.then(function(response) {
const url = response.data;
vm.openLender(url[0].response);
})
.catch(function(error) {
alert(error);
});
}
The problem is that i get this error:
TypeError: Cannot read property 'focus' of null
When I console.log(url[0].response) the response, it does show me the url that I got from the axios request, but when I use the openLender() method it gives me this error.
Any thoughts?
EDIT
I used #IgorDymov his solution in this post to work-around this problem
window.open method is always blocked by browser if it's not caused directly by a click event. Turn popup blocking off and it will work.
You may open the url in the same window or provide user a link to the new page though.
I am trying to access a prestashop API with vuejs2
<script>
import axios from 'axios'
export default {
data () {
return {
get: [],
errors: []
}
},
created () {
axios({
method: 'get',
url: 'https://myprestashopsite.com/api/categories/?ws_key=J***************Z&filter[id_parent]=5&output_format=JSON'
}).then(response => {
this.get = response.data
})
.catch(e => {
this.errors.push(e)
})
}
}
In the web developer console of mozilla I see that my request return a 200 with the data in the response. But I get "Error: Network Error app.js%20line%201266%20%3E%20eval:15:15" catch as an error.
I tried with another API and it worked, so I guess it comes from the prestashop api. (prestashop version 1.7.3.0)
Is there a way to fix this ?
The problem seems to come from axios. I had to add a rule to the server.
I found the solution to this on this thread :
https://github.com/axios/axios/issues/853
There are other solutions I didn't try in this thread if mine doesn't work.
how to add the rule : https://enable-cors.org/server.html
I have a sample vue app where I'm trying to add authentication.
The backend for this app returns a jwt string on successful login.
The problem is that the projects are not fetched after login.
But if I refresh the page manually, the projects list is loaded without any errors
login
submit () {
api.login(this.cred).then((res) => {
window.localStorage.setItem('access_token', res.data.access_token)
this.$router.push('/projects')
})
}
projects view has the following part
mounted () {
this.fetchProjects()
}
error
GET http://localhost:3000/api/v1/projects/ 401 (Unauthorized)
Uncaught (in promise) Error: Request failed with status code 401(…)
up (projects api)
const config = {
headers: {
'Authorization': 'Bearer: ' + window.localStorage.getItem('access_token')
}
}
export default {
fetchProjects: () => {
return axios.get(Projects, config)
}
}
You should use a function return headers, like this:
function getHeaders() {
return {
'Authorization': 'Bearer: ' +
window.localStorage.getItem('access_token')
}
}
After you set the the localStorage you need update that config.headers.Authorization. Otherwise it will have effect only when you refresh the page.