I am using flask as my backend server.
vuex action is not setting token from local storage state to Axios API call. Please help me with what am I missing. currently I am stuck here, this is related to my previous question which I dint get answer so posting again..
Below is my vuex store code:
Vue.use(Vuex);
export default new Vuex.Store({
state: {
// accessToken: JSON.parse(localStorage.getItem('access_token')) || null,
// refreshToken: JSON.parse(localStorage.getItem('refresh_token')) || null,
accessToken: localStorage.getItem('access_token') || null,
refreshToken: localStorage.getItem('refresh_token') || null,
APIData: '',
},
actions: {
refreshToken(context) {
return new Promise((resolve, reject) => {
console.log(context.state.refreshToken);
getAPI.post('/refresh', {
// refresh_token: context.state.refreshToken,
headers: { Authorization: `Bearer ${context.state.refreshToken}` },
})
.then((response) => {
console.log('New access token granted');
context.commit('updateAccessToken', response.data.access_token);
console.log(context.state.accessToken);
resolve(response.data.access_token);
})
.catch((error) => {
console.log('\'error in refresh:\'', error);
reject(error);
});
});
},
}
Here is an example that I built with a simplified Vuex store and Vue component in order to demonstrate the functionality.
/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
accessToken: '',
localStorage: window.localStorage
},
getters: {
getTokenFromLocalStorage: state => {
return state.localStorage.getItem('accessToken');
}
},
mutations: {
storeTokenInLocalStorage(state, newToken) {
state.accessToken = newToken;
state.localStorage.setItem('accessToken', newToken);
}
}
})
VuexLocalStorage.vue
<template>
<div class="vuex-local-storage">
<div class="row">
<div class="col-md-6">
<button class="btn btn-secondary" #click="getAccessToken">Get Access Token</button>
<h5>{{ accessToken }}</h5>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
accessToken: ''
}
},
methods: {
getAccessToken() {
this.accessToken = this.$store.getters.getTokenFromLocalStorage;
}
},
created() {
// Store initial access token
this.$store.commit('storeTokenInLocalStorage', 'access-token');
}
}
</script>
Related
Login Vue
<template>
<div class="container mt-5">
<div class="row">
<div class="col-md-12">
<div class="col-md-6">
<div class="form-group">
<form #submit.prevent="onSubmit">
<label>Kullanıcı Adı</label>
<input
type="text"
class="form-control"
v-model="userdata.username"
/>
<br />
<label>Şifre</label>
<input class="form-control" v-model="userdata.password" />
<br />
<button class="btn btn-primary">Login</button>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { reactive } from "vue";
const userdata = reactive({
username: "",
password: "",
});
function loginAction() {
this.$store.dispatch("loginPost", userdata.value);
this.$router.push({ path: "/city" });
}
function onSubmit(event) {
event.preventDefault();
loginAction();
}
</script>
<style></style>
store kısmı import { createStore } from "vuex";
export default createStore({
state: {
cities: [],
user: {},
token: null,
},
getters: {},
mutations: {
setToken(state, token) {
state.token = token;
localStorage.setItem("token", token);
},
setUser(state, user) {
state.user = user;
localStorage.setItem("user", JSON.stringify(user));
},
},
actions: {
async login({ commit }, credentials) {
const response = await fetch("https://localhost:7254/api/Auth/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(credentials),
});
const data = await response.json();
if (response.ok) {
commit("setUser", data.user);
commit("setToken", data.token);
}
return response.ok;
},
},
});
store js
import { createStore } from "vuex";
export default createStore({
state: {
cities: [],
user: {},
token: null,
},
getters: {},
mutations: {
setToken(state, token) {
state.token = token;
localStorage.setItem("token", token);
},
setUser(state, user) {
state.user = user;
localStorage.setItem("user", JSON.stringify(user));
},
},
actions: {
async login({ commit }, credentials) {
const response = await fetch("https://localhost:7254/api/Auth/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(credentials),
});
const data = await response.json();
if (response.ok) {
commit("setUser", data.user);
commit("setToken", data.token);
}
return response.ok;
},
},
});
router js
import { createRouter, createWebHistory } from "vue-router";
import { city } from "#/components/City.vue";
import { Login } from "#/components/Login.vue";
import { home } from "#/components/Home.vue";
const routes = [
{
name: "login",
path: "/login",
component: Login,
},
{
name: "city",
path: "/city",
component: city,
},
{
name: "home",
path: "/",
component: home,
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
router.beforeEach((to, next) => {
// localStorage'daki JWT token'ı ve kullanıcı bilgilerini kontrol edin
const token = localStorage.getItem("token");
const user = JSON.parse(localStorage.getItem("user"));
// JWT token veya kullanıcı bilgisi yoksa, giriş sayfasına yönlendirin
// Eğer zaten giriş sayfasına gidiyorsak, yönlendirmeyi iptal edin
if (!token || !user) {
next({ path: "/login" });
}
next();
});
export default router;
main js
import { createApp } from "vue";
import App from "./App.vue";
import store from "../src/store";
import router from "../src/router";
// eslint-disable-next-line no-unused-vars
import bootstrap from "bootstrap/dist/css/bootstrap.min.css";
createApp(App).use(router).use(store).mount("#app");
Home vue
<!-- eslint-disable vue/multi-word-component-names -->
<template>
<h1>anasayfa Vue</h1>
<city />
<login />
</template>
<script setup>
import city from "#/components/City.vue";
import login from "#/components/Login.vue";
</script>
<style></style>
For example, when the page loads, I want it to go directly to the login page because in my router js file, I wrote the condition that it goes directly to the login page if there is no token and user with beforeEach, but it does not work, it gives this warning console log [Vue Router warn]: Unexpected error while initializing the router: TypeError: next is not a function After filling out and submitting the form, I get the following error Uncaught TypeError: Unable to read undefined properties (reading '$store')
first of all "this" doesn't refer to vue instance ,it refers to loginAction block,use arrow function instead ,secondly due to docs you should use useStore() method to get store in vue 3 Composition api
I can't get how to solve problem with POST request.
I am trying implement authorization on my site (via Vuejs,vuex,vue-router, axios).
I will be very pleasure, if you give me some advice.
I searched info on forums , but situation was not the same.
Why 404 (OK)? If OK, why 404?
It means, that server received my data, but can't compare correct this or not?
I have components/pages:
App.vue
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home page</router-link> |
<router-link to="/login">Login</router-link>
<span v-if="isLoggedIn"> | <a #click="logout">Logout</a></span>
</div>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App',
computed : {
isLoggedIn : function(){ return this.$store.getters.isLoggedIn}
},
methods: {
logout: function () {
this.$store.dispatch('logout')
.then(() => {
this.$router.push('/login')
})
}
},
created: function () {
this.http.interceptors.response.use(function (err) {
return new Promise(function (resolve) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
this.$store.dispatch("logout")
resolve()
}
throw err;
});
});
},
}
</script>
LoginAnalytics.vue
Here user must input his mobile phone and password.
<template>
<div>
<form class="login" #submit.prevent="login">
<h1>Sign in</h1>
<label>Mobile</label>
<input required v-model="mobile" type="tel" placeholder="mobile phone"/>
<label>Password</label>
<input required v-model="password" type="password" placeholder="Password"/>
<hr/>
<button type="submit">Login</button>
</form>
</div>
</template>
<script>
export default {
data(){
return {
mobile : "",
password : ""
}
},
methods: {
login: function () {
let login = this.mobile
let password = this.password
this.$store.dispatch('login', { login, password })
.then(() => this.$router.push('/secure'))
.catch(err => console.log(err))
}
}
}
</script>
Vuex store
Here I am creating axios POST requests to server.
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios';
import { API_BASE_URL, TEMPORARY_TOKEN } from '../config';
Vue.use(Vuex);
export default new Vuex.Store({
state:{
trainings: [],
incomings: [],
sources: [],
avgShedule: [],
metro: [],
conversion: [],
avgIncome: [],
status: '',
token: localStorage.getItem('token') || '',
user : {},
},
mutations:{
auth_request(state){
state.status = 'loading'
},
auth_success(state, token, user){
state.status = 'success'
state.token = token
state.user = user
},
auth_error(state){
state.status = 'error'
},
logout(state){
state.status = ''
state.token = ''
},
},
getters:{
isLoggedIn: state => !!state.token,
authStatus: state => state.status,
},
actions:{
login({commit}, user){
return new Promise((resolve, reject) => {
commit('auth_request')
axios({
url: `${API_BASE_URL}/analytics.auth`,
data: user,
method: 'POST',
headers: {
"Content-Type": "application/x-www-form-urlencoded",
}
})
.then(resp => {
const token = resp.data.token
const user = resp.data.user
localStorage.setItem('token', token)
axios.defaults.headers.common['Authorization'] = token
commit('auth_success', token, user)
resolve(resp)
})
.catch(err => {
commit('auth_error')
localStorage.removeItem('token')
reject(err)
})
})
},
logout({commit}){
return new Promise((resolve) => {
commit('logout')
localStorage.removeItem('token')
delete axios.defaults.headers.common['Authorization']
resolve()
})
}
}
},
)
router.
There all refs to components and pages
import Vue from 'vue'
import Router from 'vue-router'
import store from '#/store'
import Analytics from '#/pages/Analytics-test.vue'
import LoginAnalytics from '#/components/LoginAnalytics.vue'
import HomeAnalytics from '#/components/HomeAnalytics.vue'
Vue.use(Router)
let router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: HomeAnalytics
},
{
path: '/login',
name: 'login',
component: LoginAnalytics
},
{
path: '/secure',
name: 'secure',
component: Analytics,
meta: {
requiresAuth: true
}
},
]
})
router.beforeEach((to, from, next) => {
if(to.matched.some(record => record.meta.requiresAuth)) {
if (store.getters.isLoggedIn) {
next()
return
}
next('/login')
} else {
next()
}
})
export default router
And also main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router';
import store from './store'
import axios from 'axios'
Vue.config.productionTip = false
Vue.prototype.$http = axios;
const token = localStorage.getItem('token')
if (token) {
Vue.prototype.$http.defaults.headers.common['Authorization'] = token
}
new Vue({
store,
router,
render: h => h(App),
}).$mount('#app')
Every time, when I am try login me with correct mobile and password, I received error like this:
Problem was with mistake in API endpoint.
All times I tried call this url:
url: ${API_BASE_URL}/analytics.auth,
But correct is:
url: ${API_BASE_URL}/account.auth.
I am a beginner. I have a Lumen API. The project runs on http://localhost:8000/. In Postman the API is working fine. Now I want to call the API from a NuxtJs project using Axios. My NuxtJs project is running on http://localhost:3000/.
<template>
<div>
CV List
<v-row v-for="(applicant, i) in applicants" :key="i">
<v-col>
<h1>name: {{ applicant.name }}</h1>
<p>{{ applicant.email }}</p>
</v-col>
</v-row>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
async fetch({ store, error }) {
try {
await store.dispatch('applicants/fetchApplicants')
} catch (e) {
error({
statusCode: 503,
message: 'Unable to fetch applicants at this time. Please try again.',
})
}
},
computed: mapState({
applicants: (state) => state.applicants.applicants,
}),
}
</script>
my applicants.js file like this:
import CVService from '#/services/CVService.js'
export const state = () => ({
applicants: [],
applicant: {},
})
export const mutations = {
SET_APPLICANTS(state, applicants) {
state.applicants = applicants
},
}
export const actions = {
fetchApplicants({ commit }) {
return CVService.getApplicants().then((response) => {
commit('SET_APPLICANTS', response.data)
})
},
}
CVService is like this,
import axios from 'axios'
const apiClient = axios.create({
baseURL: `http://localhost:8000`,
withCredentials: false,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
export default {
getApplicants() {
return apiClient.get('/api/authors')
},
}
The console is showing error, 503 (Service unavailable). What causing is the problem?
In the quasar project, I have a Vuex function "asyncValidateToken" that checks whether the user is logged in to the system. It is located in the file "src/store/index.js". The file contains the following code:
import Vue from 'vue'
import Vuex from 'vuex'
import { api } from 'boot/axios'
Vue.use(Vuex)
export default function (/* { ssrContext } */) {
const Store = new Vuex.Store({
state: {
isLogin: false
},
mutations: {
changeIsLogin (state, payload) {
state.isLogin = payload;
}
},
actions: {
asyncValidateToken: async (context, payload) => {
await api.post('/accounts/token', '', {
headers: {
'Authorization': `Bearer ${localStorage.token}`,
}
})
.then(response => {
if (response.data == localStorage.userId) {
context.commit('changeIsLogin', true);
return true;
} else {
context.commit('changeIsLogin', false);
return false;
}
})
.catch(error => {
context.commit('changeIsLogin', false);
return false;
});
}
}
})
return Store
}
The page "Results.vue" where the route protection is used via the function "beforeRouteEnter"
<template>
<q-page class="flex flex-center">
<div>
<charts />
<feedback />
</div>
</q-page>
</template>
<script>
import Charts from 'src/components/Charts.vue'
import Feedback from 'src/components/Feedback.vue'
import store from 'src/store/index.js'
export default {
name: 'Results',
components: {
Charts,
Feedback
},
beforeRouteEnter (to, fromR, next) {
if (store.dispatch('asyncValidateToken')) {
next();
} else { this.$router.push('/login'); }
}
}
</script>
I get an error "src_store_index_js__WEBPACK_IMPORTED_MODULE_2__.default.dispatch is not a function
at beforeRouteEnter (Results.vue?82a0:23)
at routeEnterGuard (vue-router.esm.js?85f8:2333)". The construction "this.$store.dispatch('asyncValidateToken')" also does not work. Why?
Try
store().dispatch('')
Why?
Because your store.js module is exporting a function as default, and it returns the store.
There is a Form component.vue, which takes the event object from getter and substitutes it in v-model:
<template>
<form #submit.prevent="submitForm">
<div class="form-group row">
<div class="col-10 d-flex">
<input type="" class="title form-control" v-model="getEvent.title" placeholder="Название">
<input type="" class="content form-control" v-model="getEvent.content" placeholder="Содержание">
<input type="" class="event_date form-control" v-model="getEvent.event_date" placeholder="Дата">
<input type="" class="email form-control" v-model="getEvent.email" placeholder="Email">
</div>
<div class="d-flex flex-column">
<button class="btn btn-success mt-auto" >Создать</button>
</div>
</div>
</form>
</template>
<script>
import { mapGetters, mapActions } from "vuex"
export default {
computed: mapGetters(['getEvent']),
methods: mapActions(['submitForm'])
}
However, vue returns an error stating that getter undefined. store/index.js:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
Date.prototype.getWeek = function () {
var onejan = new Date(this.getFullYear(), 0, 1);
var today = new Date(this.getFullYear(), this.getMonth(), this.getDate());
var dayOfYear = ((today - onejan + 86400000) / 86400000);
return Math.ceil(dayOfYear / 7)
}
export const store = new Vuex.Store({
actions: {
async getEvents(context) {
var response = await fetch('http://127.0.0.1:8000/rest/');
var data = await response.json()
context('getEvents', data)
},
async createEvent(context) {
await this.getEvents();
await fetch('http://127.0.0.1:8000/rest/', {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({ event: context.state.event })
});
await this.getEvents();
context.commit('createEvent', context.state.event)
},
async editEvent(context) {
await this.getEvents();
await fetch(`http://127.0.0.1:8000/rest/${context.state.event.id}/`, {
method: 'put',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({ event: context.state.event })
});
await this.getEvents();
context.state.event = {};
},
async deleteEvent(context) {
await this.getEvents();
await fetch(`http://127.0.0.1:8000/rest/${context.state.event.id}/`, {
method: 'delete',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({ event: context.state.event })
});
await this.getEvents();
},
submitForm(context) {
if (context.state.event.id === undefined) {
this.createEvent();
} else {
this.editEvent();
}
},
isMonthEqualNow(object) {
var event_date = new Date(object.event_date)
var date_now = new Date()
return event_date.getMonth() === date_now.getMonth()
},
isWeekEqualNow(object) {
var event_date = new Date(object.event_date)
var date_now = new Date()
return event_date.getWeek() === date_now.getWeek()
},
isDayEqualNow(object) {
var event_date = new Date(object.event_date)
var date_now = new Date()
return event_date.getDate() === date_now.getDate()
},
eventsByFilters(context) {
var events = context.state.events
if (context.state.search === '' && context.state.selected) {
switch (context.state.selected) {
case 'month':
return events.filter(item => this.isMonthEqualNow(item))
case 'week':
return events.filter(item => this.isMonthEqualNow(item) && this.isWeekEqualNow(item))
case 'day':
return events.filter(item => this.isMonthEqualNow(item) && this.isWeekEqualNow(item)
&& this.isDayEqualNow(item))
default:
return events
}
} else {
events.filter(item => item.title.indexOf(context.state.search) !== -1)
}
}
},
mutations: {
setEvents(state,events){
state.events = events
},
createEvent(state, event){
state.events.push(event)
}
},
state: {
events: [],
event: {},
selected: '',
search: ''
},
getters: {
eventsByFilters(state) {
return state.events
},
getSearch(state){
return state.search
},
getSelected(state){
return state.selected
},
getEvent(state) {
return state.event
}
},
});
And also i have warning(warning in ./src/main.js
"export 'default' (imported as 'store') was not found in './store')
main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store';
Vue.config.productionTip = false
new Vue({
render: h => h(App),
store
}).$mount('#app')
And the components themselves are not output
The only issue, I have seen is
your store is not exporting any default
export const store = new Vuex.Store(...
yet, your main.js uses to import the default
import store from 'src/store'
so use the following and hope your issue gets solved
import { store } from './store';
please check these links
export-const-vs-export-default-in-es6
named-export-vs-default-export-in-es6
One point to suggests
in the following lines, I do not think you need to use await for this.getEvents() because it has already used await inside its action.
for example,
await this.getEvents();
await fetch('http://127.0.0.1:8000/rest/', {
action is for commit data to mutation and in mutation you have to set data to state.
You should not fetch data in action, instead call it from component, in mounted() or something.
an example:
export default {
mounted() {
var response = await fetch('http://127.0.0.1:8000/rest/');
var data = await response.json()
this.$store.dispatch("eventsList", data);
}
}
and in store.js:
actions: {
eventsList({commit}, data) {
commit('eventsList', data)
}
},
mutations: {
eventsList(state, data) {
state.events= data
},
}
dispatch calls action -> commit calls mutation => in mutation set the data directly to state.