Vue-router ー未捕获(在承诺中)错误: 通过导航保护从“/login”重定向到“/”

Vue-router 为什么给我这个错误?需要说明的是,登录流程按预期工作,但是我想 a)消除错误,b)理解错误发生的原因。

错误:

Uncaught (in promise) Error: Redirected from "/login" to "/" via a navigation guard.

登录流程

  1. 开始注销,但输入一个需要认证的 URL (即除“/login”之外的任何内容)
  2. 被重定向到“/login”(正如预期的那样)。
  3. 登入
  4. 成功地从步骤 # 1重定向到启动 Url,除了上面的错误。

登入动作:

doLogin({ commit }, loginData) {
commit("loginStart");
axiosClient
.post("/jwt-auth/v1/token", {
username: loginData.username,
password: loginData.password,
})
.then((response) => {
commit("loginStop", null);
commit("setUserData", response.data);
this.categories = airtableQuery.getTable("Categories");
commit("setCategories", this.categories);
this.locations = airtableQuery.getTable("Locations");
commit("setLocations", this.locations);
router.push("/"); // push to site root after authentication
})
.catch((error) => {
console.log(error.response.data.message);
commit("loginStop", error.response.data.message);
commit("delUserData");
});
},

路由器:

const routes = [
{
path: "/login",
name: "Login",
component: Login,
meta: { requiresAuth: false },
},
{
path: "/",
name: "Home",
component: Home,
meta: { requiresAuth: true },
},
];


let entryUrl = null;
router.beforeEach((to, from, next) => {
let localStorageUserData = JSON.parse(localStorage.getItem("userData"));
let storeUserData = state.getters.getUserData;
let userData = localStorageUserData || storeUserData;
let isAuthenticated = userData.token !== "" && userData.token !== undefined;
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (!isAuthenticated) {
if (to.name !== "Login" && to.name !== "Home") {
entryUrl = to.fullPath;
}
next({ name: "Login" });
} else if (entryUrl) {
let url = entryUrl;
entryUrl = null;
next(url);
} else {
next();
}
} else {
next();
}
});
107930 次浏览

The error message is getting updated in the next version of vue-router. The error will read:

Redirected when going from "/login" to "/" via a navigation guard

Somewhere in your code, after being redirected to "/login", you are redirecting back to "/". And vue-router is complaining about. You'll want to make sure you only have one redirect per navigation action.

If your redirect is after a call to router.push('/someroute) then you can catch this error as router.push() is a promise and you can attach a catch to it as below

$router.push('/somesecureroute')
.catch(error => {
console.info(error.message)
})

I had the same issue, i thought it was config problem but it was not You can try this code

async doLogin({ commit, dispatch }, loginData) {
commit("loginStart");
let response = await axiosClient
.post("/jwt-auth/v1/token", {
username: loginData.username,
password: loginData.password,
})
return dispacth('attempt', response)
}


async attempt({ commit }, response) {
try {
commit("loginStop", null);
commit("setUserData", response.data);
this.categories = airtableQuery.getTable("Categories");
commit("setCategories", this.categories);
this.locations = airtableQuery.getTable("Locations");
commit("setLocations", this.locations);
}
catch( (error) => {
console.log(error.response.data.message);
commit("loginStop", error.response.data.message);
commit("delUserData");
})
}

And in the component where doLogin action is called

this.doLogin(this.loginData)
.then( () => {
this.$router.replace('/')
.catch( error => {
console.log(error)
})
})
.catch( e => {
console.log(e)
})

I had a similar error, but for an onboarding redirect in .beforeEach, which was resolved by replacing in the .beforeEach conditional logic:

next({ name: "Onboarding" });

with

router.push({ path: 'Onboarding' });

This error is meant to inform the caller of $router.push that the navigation didn't go to where it was initially intended. If you expect a redirection you can safely ignore the error with the following code.

import VueRouter from 'vue-router'
const { isNavigationFailure, NavigationFailureType } = VueRouter


...


this.$router.push('/')
.catch((e) => {
if (!isNavigationFailure(e, NavigationFailureType.redirected)) {
Promise.reject(e)
}
}

See https://github.com/vuejs/vue-router/issues/2932 for a discussion regarding this issue.

I have the same error. This error created by router.push("/"); row - it trying to say you that pushing to home was interrupted by redirection in navigation guard. But actually, it's not an error because it is an expected behaviour.

I made ignoring of such errors by the following way:

const router = new VueRouter({
mode: 'history',
routes: _routes,
});


/**
* Do not throw an exception if push is rejected by redirection from navigation guard
*/
const originalPush = router.push;
router.push = function push(location, onResolve, onReject) {
if (onResolve || onReject) {
return originalPush.call(this, location, onResolve, onReject);
}
return originalPush.call(this, location).catch((err) => {
let reg = new RegExp('^Redirected when going from "[a-z_.\\/]+" to "[a-z_.\\/]+" via a navigation guard.$');
if (reg.test(err.message)) {
// If pushing interrupted because of redirection from navigation guard - ignore it.
return Promise.resolve(false);
}
// Otherwise throw error
return Promise.reject(err);
});
};

I spent hours debugging this and got to the following results for the ugly Uncaught (in promise) Error: Redirected when going from ... Error.

Note that the error is not for the "redirect". It's for the initial caller of the first navigation. Keep reading...

It's by design. Why?

Read this comment.

TL;DR: Let's say you are on page A, and click on a button to take you to page B (kinda like method: goToB() { router.push('/B'); } on page A). But there is a Navigation Guard for page B, that sends you to page C.

This error is a way for letting that goToB() function know that the router hasn't been able to fulfill the desired task, and the user hasn't landed on /B.

It's nasty, but informative

The biggest confusion here is that the redirect (landing on Page C) is, both:

  • an "expected" outcome to you, the architect of the system. But, at the same time,
  • an "unexpected" event to the caller of goToB in page A (i.e. router.push), who expects the router to go to page B.

That's why when it's popped as Error, it's confusing and frustrating to "you", who looks at the system entirely and thinks nothing is wrong or erroneous!

Urrrgh... So, what should I do?

Solution 1: Use router-link if you can

I ran into a case that <router-link> was working fine, but router.push was complaining. (I think router-link internally suppresses such errors.)

Solution 2.1: Individual suppress errors on each router.push call

The router.push function is returning a Promise (as it can be considered, or will be, an asynchronous job). All you need to do is to suppress any Error it might throw via

router.push('/B').catch(() => {});
//     Add this: ^^^^^^^^^^^^^^^^

Solution 2.2: Augment Router.prototype.push to always suppress errors

If you think you have multiple instances of this, you can augment the push function on the prototype of the Router via the snippet on the same comment to apply this to all the router.push calls on the entire app.

The good news is it's giving you granularity level to choose which error you want to suppress (e.g. only NavigationFailureTypes.redirected ones, for example. The enum is here)

If you are on TypeScript, be my guest on the conversion and typing https://gist.github.com/eyedean/ce6ab6a5108a1bd19ace64382144b5b0 :)


Other tips:

  1. Upgrade your vue-router! Your case might be solved by the time you read this. (As they have a plan to do so, apparently.)
  2. Make sure you are not forking or reaching to dead-end in your Navigation Guards, if you have multiple ones. Follow them one by one and track them step by step. Note that, double redirecting is fine (thanks to this answer), you just need to be double careful!
  3. I also got a playground here: https://codepen.io/eyedean/pen/MWjmKjV You can start mimicking this to your need to figure out where your problem happens in the first place.

It happened to me on application boot, during an authorization check I tried to push to '/auth' but that navigation was cancelled by another call to '/', which occurred just after mine.

So in the end I found that is possible to listen for readiness (https://router.vuejs.org/api/#isready) of vue-router using:

await router.isReady()

or

router.isReady().then(...)

I had the exact same issue triggered by two specific pages, for the two pages that were being redirected due to a login:

if (isAdmin){
router.push({name: 'page'}).catch(error=>{
console.info(error.message)})
else...

otherwise everyone else who is a regular user gets pushed to a different page using "router.push"

ONLY on the redirects that were throwing the original error/warning. Suppressing the warning as suggested in an earlier comment:

if (!to.matched.length) console.warn('no match');
next()

allowed for users to sign in and access pages without proper permissions.

Catching the errors appears to be the way to go, per the suggestion of: kissu