问题
In my Vue app, a user's homepage depends on their role. To ensure that a user is shown the correct homepage, I use this navigation guard:
export default (to, from, next) => {
const authService = getAuthService()
if (to.path === '/') {
// if they've requested the home page, send them to
// different pages depending on their role
if (authService.isUser()) {
next({ name: 'events' })
} else if (authService.isAdmin()) {
next({ name: 'admin-events' })
} else {
next()
}
}
}
Then when a user successfully logs in, I redirect them to '/'
this.$router.push({path: '/'))
and the nav guard above redirects them to their role-specific homepage. However, redirecting twice in the course of a single navigation action is not allowed and causes the following error to appear in the console when the second redirection occurs (in the nav guard) `
Uncaught (in promise) Error: Redirected when going from
"/login"
to"/"
via a navigation guard.
Another case in my app where this happens is the 404 component that handles attempts to access non-existent routes, i.e.
- User attempts to access invalid route
- 404 component redirects back to
'/'
- Nav guard redirects to user-specific homepage, causing an error
Is there a way I can support these use cases without redirecting twice?
回答1:
tldr: vm.$router.push(route)
is a promise and needs to .catch(e=>gotCaught(e))
errors.
This will be changed in the next major@4
Currently@3 errors are not distinguished whether they are NavigationFailures
or regular Errors
.
The naive expected route after vm.$router.push(to)
should be to
. Thus one can expect some failure message once there was a redirect. Before patching router.push to be a promise the error was ignored silently.
The current solution is to antipattern a .catch(...)
onto every push, or to anticipate the change in design and wrap it to expose the failure as result.
Future plans have it to put those informations into the result:
let failure = await this.$router.push(to);
if(failure.type == NavigationFailureType[type]){}
else{}
Imo this error is just by design and should be handled:
hook(route, current, (to: any) => { ... abort(createNavigationRedirectedError(current, route)) ...}
So basically if to
contains a redirect it is an error, which kinda is equal to using vm.$router.push
into a guard.
To ignore the unhandled error behaviour one can pass an empty onComplete (breaks in future releases):
vm.$router.push(Route, ()=>{})
or wrap it in try .. catch
try {
await this.$router.push("/")
} catch {
}
which prevents the promise to throw uncaught.
to support this without redirecting twice means you put the guard to your exit:
let path = "/"
navguard({path}, undefined, (to)=>this.$router.push(to||path))
which will polute every component redirecting to home
btw the router-link
component uses an empty onComplete
Assumption that redirecting twice is not allowed is wrong.
回答2:
A possible solution might be as follows:
Let's assume that /
route points to HomeComponent
.
- User navigates to
/
- In
created
lifecyle hook, get theauthService
and check user's permission - 2.a. If it's a user -> navigate them to
/events
route + replace the history - 2.b. If it's an administrator -> navigate them to
/admin-events
route + replace the history - Add guards for each
/events
route so a user without permission won't see/admin-events
, etc
Bonus: You are now able to add a message to HomeComponent
saying "redirecting you to Events page" or "redirecting you to Administrator Events page" (if needed).
回答3:
Another possible solution might be;
- Adding a query string parameter to define that user coming to the
/
route after login. Like?fromLogin=1
- Adding
beforeEnter
hook to the/
route and attaching the navigation guard check on that hook. - If user coming to the
/
route from login, redirecting them to the role based page.
来源:https://stackoverflow.com/questions/62512168/redirecting-twice-in-a-single-vue-navigation