Membuat Middleware Dengan Vue-Router
Middleware yang saya pahami adalah suatu fungsi atau metode yang berguna untuk menyaring request yang masuk. Contoh paling umum adalah: halaman atau request yang hanya bisa diakses saat user telah login atau memiliki akses. Jika user belum login atau tidak memiliki akses maka request akan ditolak.
Penerapan di Vue router
Skenario
Skenario yang akan saya buat seperti berikut
- Akan ada satu halaman publik
- Akan ada halaman secret yang hanya bisa diakses jika login, jika belum akan diarahkan ke halaman login
- Akan ada halaman read yang hanya bisa diakses jika memiliki akses “read-page“, jika tidak memiliki akses akan diarahkan ke halaman 503
- Jika tidak ada halaman yang terdaftar diarahkan ke 404
Sedangkan kondisi login dan akses permisi saya tambahkan hardcode di localstorage.
Saat kita memasang aplikasi vue.js + vue-router menggunakan vue-cli di berkas router.js sudah ada seperti berikut:
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ './views/About.vue'),
},
],
});
Sesuai skenario di atas saya tambahkan beberapa routes dan komponen ( saya tidak masukan bagaimana cara membuat komponen, fokus tulisan ini pada vue-router ).
import Vue from 'vue';
import Router from 'vue-router';
// eslint-disable-next-line import/no-unresolved
import Home from '~/views/Home.vue';
// eslint-disable-next-line import/no-unresolved
import Forbidden from '~/views/Forbidden.vue';
// eslint-disable-next-line import/no-unresolved
import PageNotFound from '~/views/404.vue';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/login',
name: 'login',
component: () => import('~/views/Login.vue'),
},
{
path: '/secret',
name: 'secret',
component: () => import('~/views/Secret.vue'),
},
{
path: '/read',
name: 'read',
component: () => import('~/views/Read.vue'),
},
{
path: '/503',
name: 'forbidden',
component: Forbidden,
},
{
path: '*',
name: 'notfound',
component: PageNotFound,
},
],
});
Menambahkan Meta
Di dalam array router, di masing-masing objek kita bisa tambahkan properti meta dengan infromasi kustom yang kita inginkan. Di route secret dan read saya akan menambahkan informasi auth dan permission.
{
path: '/secret',
name: 'secret',
component: () => import('~/views/Secret.vue'),
meta: {
auth: true,
},
},
{
path: '/read',
name: 'read',
component: () => import('~/views/Read.vue'),
meta: {
auth: true,
permission: 'read-page',
},
},
beforeEach(to, from, next)
Seperti yang saya tulis di atas, middleware berfungsi menyaring request yang masuk, oleh karena itu sebelum request menuju halaman yang dituju harus ada yang menjaga, di vue-router ada fungsi dengan nama beforeEach
Mari kita ubah sedikit vue router yang saat ini.
import Vue from 'vue';
import Router from 'vue-router';
// eslint-disable-next-line import/no-unresolved
import Home from '~/views/Home.vue';
// eslint-disable-next-line import/no-unresolved
import Forbidden from '~/views/Forbidden.vue';
// eslint-disable-next-line import/no-unresolved
import PageNotFound from '~/views/404.vue';
Vue.use(Router);
const router = new Router({
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/login',
name: 'login',
component: () => import('~/views/Login.vue'),
},
{
path: '/secret',
name: 'secret',
component: () => import('~/views/Secret.vue'),
meta: {
auth: true,
},
},
{
path: '/read',
name: 'read',
component: () => import('~/views/Read.vue'),
meta: {
auth: true,
permission: 'read-page',
},
},
{
path: '/503',
name: 'forbidden',
component: Forbidden,
},
{
path: '*',
name: 'notfound',
component: PageNotFound,
},
],
});
router.beforeEach((to, from, next) => {
next();
});
export default router;
Ada yang berbeda sedikit dengan kode sebelumnya, jika sebelumnya object Router (vue-router) langsung di-export maka yang sekarang kita tampung dulu di dalam objek router, lalu objek tersebut kita tambahakn fungsi beforeEach() untuk menyaring request yang masuk. Setelahnya baru kita export object router tersebut.
Pengecekan Request
Di dalam fungsi beforeEach() saya tambahkan pengecekan sesuai dengan skenario
import Vue from 'vue';
import Router from 'vue-router';
// eslint-disable-next-line import/no-unresolved
import Home from '~/views/Home.vue';
// eslint-disable-next-line import/no-unresolved
import Forbidden from '~/views/Forbidden.vue';
// eslint-disable-next-line import/no-unresolved
import PageNotFound from '~/views/404.vue';
Vue.use(Router);
const router = new Router({
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/login',
name: 'login',
component: () => import('~/views/Login.vue'),
},
{
path: '/secret',
name: 'secret',
component: () => import('~/views/Secret.vue'),
meta: {
auth: true,
},
},
{
path: '/read',
name: 'read',
component: () => import('~/views/Read.vue'),
meta: {
auth: true,
permission: 'read-page',
},
},
{
path: '/503',
name: 'forbidden',
component: Forbidden,
},
{
path: '*',
name: 'notfound',
component: PageNotFound,
},
],
});
router.beforeEach((to, from, next) => {
// mengecek ada tidak meta auth di dalam meta
if (to.matched.some(record => record.meta.auth)) {
// cek di localstorage auth, jika false maka diarahkan ke halaman login
if (localStorage.getItem('auth') === 'false') {
next('/login');
} else {
// jika login auth maka cek adakah permission yang dibutuhkan, jika ada cek dulu permission
// jika permission di dalam localstorage cocok dengan meta permission
// langsung di next() jika tidak arahkan ke halaman 503
const permission = localStorage.getItem('permission');
if (to.matched.some(record => record.meta.permission)) {
if (permission === to.meta.permission) {
next();
} else {
next('/503');
}
} else {
next();
}
}
} else {
next();
}
});
export default router;
Hasilnya
Sebelum login
Setelah login dan memiliki akses yang valid
Setelah login namun akses tak valid
Sumber Referensi:
1. https://router.vuejs.org/guide/advanced/navigation-guards.html#global-resolve-guards
2. https://scotch.io/tutorials/vue-authentication-and-route-handling-using-vue-router