universal-router
universal-router copied to clipboard
redirect route? authorized routes
How would you do this ? to make sure that the url is authorized
**Update:
Answer from @koistya on Gitter:
One way to do so is to have a route handler similar to this one const authorize = (state, next) => { if (!state.user) { state.redirect = '/login'; next(); } } and then in your routes file: on('/admin/users', authorize, (state) => <ManageUsers />) And add a handler in your render function which checks if state.redirect was set, and if so redirect user to the new location by using Location.pushState(null, state.redirect) on a client, and req.redirect(state.redirect) on server
@rstormsf can you give me full example?
A redirect example would be useful.
This is roughly what I have. I'm not satisfied but it works for now.
router.js
const authorize = (state, next) => {
if (!state.isAuthenticated) {
state.redirect = '/login'; next();
}
}
// If trying to access login page when Authenticated
const redirectAuth = (state, next) => {
if (state.isAuthenticated) {
state.redirect = '/';
}
}
const router = new Router(on => {
on('*', async (state, next) => {
const component = await next();
return component && <App context={state.context}>{component}</App>;
});
// Authenticated pages
on('/editor', async (state, next) => {
authorize(state, next);
return (
<EditorPage />
)
});
// Public pages
on('/login', async (state, next) => {
redirectAuth(state, next);
return (
<LoginPage />
)
});
})
app.js
function run() {
let currentLocation = null;
let currentState = null;
// Make taps on links and buttons work fast on mobiles
//FastClick.attach(document.body);
ReactTapEvent();
// Check cookies if already logged in
AppActions.isLoggedIn();
let isAuthenticated = AppStore.isAuthenticated();
// Get user data if authenticated
if (isAuthenticated) {
let userId = AppStore.getAuth().userId;
AppActions.getUser(userId);
}
// Re-render the app when window.location changes
const unlisten = Location.listen(location => {
isAuthenticated = AppStore.isAuthenticated();
currentLocation = location;
currentState = Object.assign({}, location.state, {
path: location.pathname,
query: location.query,
state: location.state,
isAuthenticated: isAuthenticated,
context
});
render(currentState);
});
...
@mfrye what is ReactTapEvent() ?
@koistya is there any possibility now to redirect to different route? I see that post is quite old and universal-router API has changed since then?
Admin route + redirect example:
client.js
import UniversalRouter from 'universal-router'
import { createHistory } from 'history'
import routes from './routes'
history = useQueries(createBrowserHistory())
history.listen(onLocationChange)
history.replace(history.getCurrentLocation()); // initial render
async function onLocationChange(location) {
try {
const routeActionResult = await UniversalRouter.resolve(routes, {
path: location.pathname,
store: { isAdmin: false },
redirect(to) {
const error = new Error(`Redirecting to "${to}"...`)
error.status = 301
error.path = to
throw error
}
})
syncOrAsyncClientSideRenderFn(routeActionResult)
} catch (error) {
if (error.status === 301) {
history.replace(error.path || '')
return
}
// log or display other errors here
}
}
server.js
import express from 'express'
import UniversalRouter from 'universal-router'
import routes from './routes'
const server = express()
const port = process.env.PORT || 3000
app.get('*', async (req, res, next) => {
try {
const routeActionResult = await UniversalRouter.resolve(routes, {
path: req.path,
store: { isAdmin: false },
redirect(to) {
const error = new Error(`Redirecting to "${to}"...`)
error.status = 301
error.path = to
throw error
}
})
const html = syncOrAsyncServerSideRenderFn(routeActionResult)
res.status(200).send(html)
} catch(error) {
if (error.status === 301) {
req.redirect(status.path || '/')
return
}
next(error)
}
})
server.listen(port, () => {
console.log(`Node.js app is running at http://localhost:${port}/`)
})
routes.js
export default [
{
path: '/',
action() {
return 'main page'
}
},
{
path: '/login',
action() {
return 'login page'
}
},
{
path: '/admin',
action({ store, redirect }) {
if (store.isAdmin) return 'admin page'
return redirect('/login') // or return undefined to render 404 instead
}
}
]
@frenzzy would be great to see such example in documentation :)
@frenzzy It is needed do redirect through exception?
@langpavel nope, it is just the easiest way to stop executing all routing code. In other way you need to handle redirect in several places (for example in each root/parent route) or add implementation to the core of router. But I do not see anything wrong with the exception approach, because we still need to handle errors such as 404 or 500.
@frenzzy It seems that there is already a try catch block for route action in resolve function which take precedence over the try catch block above. For now, I set a boolean for redirection when required and use that to redirect later.