QubitProducts / cherrytree

A flexible nested router.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Middleware can't redirect without `router` in closure

nathanboktae opened this issue · comments

I am writing an authentication middleware, and I was hoping to simply do

var authToken

module.exports = function authMiddleware(transition) {
  if (!authToken && transition.path !== '/login') {
    return transition.redirect('/login?returnUrl=' + encodeURIComponent(transition.path))
  }
}

but transition.redirect or transition.transitionTo doesn't exist on the transition. instead I have to have

var authToken

module.exports = {
  initMiddleware: function(router) {
    return function authMiddleware(transition) {
      if (!authToken && transition.path !== '/login') {
        router.transitionTo('/login?returnUrl=' + encodeURIComponent(transition.path))
        return transition.followRedirects()
      }
    }
  }
}

Much more awkward. It seems natural that middleware would want to do a redirect. Unless I'm missing something and there's a way to redirect without the router?

Thanks for your continuous feedback, I appreciate it.

There used to be a transition.redirectTo and I removed it to reduce the API surface. I wanted to try and keep as few functions as possible in cherrytree.

Having said that, you're right that it's useful for middleware to be able to redirect. We should bring that back. I wonder if

  • transition.redirectTo (for symmetry with transitionTo) or transition.redirect
  • should we just expose the router instance on the transition itself.. transition.router, since you might also want to use router.replaceWith, router.generate and perhaps in same cases, inspect the router state..

The thing is, all that is possible simply by passing the router to the closure, so not sure how valuable it is to attach it to transitions. Makes transitions less like data and more like .. objecty thing.

E.g. in ES6, your middleware could look like:

module.exports.createAuthMiddleware = router => transition => {
  if (!authToken && transition.path !== '/login') {
    router.transitionTo('/login?returnUrl=' + encodeURIComponent(transition.path))
  }
}

(no need to return the transition, redirecting immediately stops executing any further middleware and starts over with a new transition)

I wanted to try and keep as few functions as possible in cherrytree.

That's a good guiding principle and in cherrytree there seems to be only one way to do everything so that's good.

all that is possible simply by passing the router to the closure, so not sure how valuable it is to attach it to transitions

Imagine if server side middleware couldn't do a 301 or 302 without having the same object that configured all the routes. That'd feel really akward right? A transition object that can be canceled and replaced but can't have a redirect is lacking IMO.

My vote is for transition.redirect or transition.redirectTo but I could see exposing the router, but to keep concerns separated I like the former.

Yeah, I'll add transition.redirectTo which will wrap router.replaceWith. Redirects should always be url "replacements", otherwise clicking browser's back button will most likely result in the same redirect rendering back button useless. Though need to test if this bit will work as I imagine.

Though need to test if this bit will work as I imagine.

Yeah I believe your right too here. Thanks!

Actually, it turns out that it doesn't matter which method is used for redirect since all transitions are already converted to replaceWiths when there's an ongoing active transition -

cherrytree/lib/router.js

Lines 132 to 134 in 857b7ae

if (this.state.activeTransition) {
return this.replaceWith.apply(this, arguments)
}
- forgot about that.

Anyways, this means that I just need to revert this change - 194a835