Vue-router: Detecting redirects

Created on 17 Oct 2017  ·  7Comments  ·  Source: vuejs/vue-router

What problem does this feature solve?

With vue-router 1.x, it was possible to see where a route had been redirected from using router.currentRoute.redirectedFrom. This was useful when server-side rendering, as you could send a 301 from the server instead of sending the HTML for the new page and then changing the URL on the client.

With 2.x, the property has gone and I can't find it anywhere. Has it been removed? If so, it would be great if it could come back!

Reason it was nice that it existed: https://github.com/vuejs/vue-router/issues/968

What does the proposed API look like?

router.currentRoute.redirectedFrom === '/old-path'

feature request fixed on 4.x group[current route information]

Most helpful comment

image

Hahaha was I the only person using this? _nice._

All 7 comments

image

Hahaha was I the only person using this? _nice._

Solution (in entry-server.js):

import { createApp } from './app';

export default context => new Promise((resolve, reject) => {
  const { app, router, store } = createApp();

  console.log(router.resolve(context.url).route); // object has redirectedFrom property

  router.push(context.url);

  router.onReady(() => { /* ... */ }, reject);
});

In case this helps anyone, after some digging I found that luckily when you push/replace a route, there is a pending property that gets set with that route:

https://github.com/vuejs/vue-router/blob/d539788df8394efe41c1534e1e9d1555aa2edbe2/src/history/base.js#L128

On the server-side, in your entry-server, you can add router and fullPath to context. Something like:

// ...
    const route = router.resolve(url).route

    const { fullPath } = route;

    if (fullPath !== url) {
      return reject({ url: fullPath })
    }

    context.fullPath = fullPath;
    context.router = router;

    // set router's location
    router.push(url)
// ...

That part doesn't handle the case of a redirect the app makes after loading data and trying to render.

To address that, back inside the server/express side where you call renderToString, you can detect those redirects and handle:

// ...
    renderer.renderToString(context, (err, html) => {
      if (err) {
        return handleError(err)
      }
      const {pending} = context.router.history;
      if (pending) {
        if (pending.fullPath !== context.fullPath) {
          console.log(`redirect ${context.fullPath} to ${pending.fullPath}`);
          return res.redirect(301, pending.fullPath);
        }
      }
      res.send(html)
    });
// ...

Hope this helps! And if there is a better way, let me know.

@joenoon What do you mean by your solution? Caz if your url is not the same,

    if (fullPath !== url) {
      return reject({ url: fullPath })
    }

it would directly reject the req and would not do the redirect any longer. As far as I am concerned, It should be solved in the handleError

@NoraGithub its a bit different case. the fullPath !== url is the easy one, where the router instantly knows it is a redirect.

The second one is harder, where async data is fetched, and a component ends up reacting to the data and decides to redirect instead. For example, when a user profile component is loaded, the component fetches the user but discovers the user no longer exists. The component does something like if (!user) this.$router.push('/').

Your component still likely rendered something, like an empty <div></div> etc. But the server side is now able to see the component is also trying to redirect.

router.currentRoute.redirectedFrom actually does exist in version 3: https://github.com/vuejs/vue-router/blob/effb1143db10376c4ed553aec9a909dd1430ef66/src/util/route.js#L32

To me this should not be a feature request but it's rather a documentation issue. With the hints from the comments above there could be a nice how-to.

in Vue Router 3 redirectedFrom only exists for records with a redirect option. In Vue Router 4, it will also exist for redirections made with navigation guards

Was this page helpful?
0 / 5 - 0 ratings