2020 年 11 月 16 日

IT Skills 波林

Polin WEI – 資訊工作者的技術手札

vue.js 整合 axios 並設定 global interceptors 與 toast 訊息

2 min read
axios-interceptor

vue.js 整合 axios 並設定 global interceptors 與 toast 訊息

 

延續  使用 Primevue 製作 Vue 模板 與 Spring Boot Vue Axios 實現前後端分離的跨域訪問(CORS), axios 除了有其兩大特性: Asynchronous JavaScript and XML (ajax) or more modern Asynchronous JavaScript and JSON (ajaj). 以外,還有可以設定 Interceptor for Request and Response。

axios-interceptor

 

  • 建立客製的 axios.js

在 plugins 目錄下,建置客製的 axios 檔案,在此加入要在送出要求給主機 ( request ) 及 主機回應 (response) 時的中斷檢查

import Vue from 'vue';
import axios from 'axios'

 
axios.defaults.baseURL = "http://your.domain.name/"; // 域名

// Error Handle
const errorHandle = (status, msg) => {
  switch (status) {
    case 400:
      console.log("400")
      this.$toast.add({severity:'warn', summary: 'Bad Request', detail: msg, life: 3000});
      break;
    case 401:
      console.log("401")
      Vue.prototype.$toast.add({severity:'warn', summary: '認證失敗', detail: msg , life: 3000});      
      break;
    default:
      break;
  }
}
// doing something with the request
export const jwtToken = localStorage.getItem("jwtToken");
axios.interceptors.request.use(
  (request) => {
    // do something with request meta data, configuration, etc
    // dont forget to return request object, otherwise your app will get no answer
    return request;
  }
);

// doing something with the response
axios.interceptors.response.use(
  (response) => {   
    return response;
  },
  (error) => {
    const {response} = error;
    if (response) {      
      // 成功發出請求且收到 response, 但有 error
      errorHandle(response.status, response.data.error);
      return Promise.reject(error);
    } else {
      // 成功發出請求但沒收到 response
      if (!window.navigator.onLine){        
        //如果是網路斷線
        Vue.prototype.$toast.add({severity:'warn', summary: 'Warn Message', detail: "網路出了問題, 請重新連線", life: 3000});
      } else {
        // 其它問題        
        Vue.prototype.$toast.add({severity:'warn', summary: 'Warn Message', detail: "主機伺服器發生問題, 請連絡資訊單位", life: 3000});
        return Promise.reject(error);
      }
    }    
  }
);

export default axios

 

  • 在 main.js 加入 axios.js & vue-axios

其中程式碼中 Vue.use(VueAxios, axios) , 這讓你可以使用 this.axios or this.$http… 來作整合

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import i18n from './i18n'
import VueAxios from 'vue-axios'
import './plugins/primevue.js'
import axios from './plugins/axios.js'

Vue.use(VueAxios, axios)  // 這讓你可以使用 this.axios or this.$http...
Vue.config.productionTip = false


new Vue({  
  router,
  store,
  i18n,
  render: h => h(App)
}).$mount('#app')

i18n.locale = navigator.language;

 

  • 一般 vue.js & axios.js 的作法

一般的作法都是使用 this.$http.post…後帶網址,但若網址有異動時,所有程式就要重新修正

<template>
<div class="loginPage">
  <Toast />
  <Button icon="pi pi-sign-in" label="Login" @click="btnLogin($event)" class="p-d-block p-m-2" >Login</Button>
</div>
</template>

<script>
export default {
methods: {
    async btnLogin() {
      this.submitted = true;
      if (this.username && this.password) {
        let data = {"username" : this.username, "password": this.password};
        const token =  await this.$http.post('/jwt/login',data).then(res=>res.data.token);
        if (token){
            localStorage.setItem("jwtToken", token);
            setTimeout(() => {
              this.$router.push("/");            
            }, 300);            
            this.blockedDocument = false;
        } else {
          localStorage.removeItem("jwtToken");
        }
        this.blockedDocument = false; 
      }
    },
  },

}
</script>

 

  • 建議的作法

建立 axios 的 api : SystemApi.js,將所有的 request 寫在 api 裡,統一管理,當有網址異動時,只要修改這裡的網址即可。

import axios from '../plugins/axios.js';


/** 若是要連到其它主機, 則需要另外建立獨立的 instance
const authApi = axios.create({
  baseURL: "https://other.domain.name/",
  headers: {
    "X-REQUEST-TYPE":"axios"
  }
});
*/

// User 相關的 api
export const apiUserLogin = data => axios.post('/jwt/login', data).then(res=>res.data);

 

在應用時,呼叫 SystemApi.js 裡的 apiUserLogin 即可。

<template>
<div class="loginPage">
 <Toast />
 <div class="p-field p-fluid">
   <div class="p-field p-py-2">
  <span class="p-float-label">
    <InputText type="text" v-model="username" required="true" autofocus :class="{'p-invalid': submitted && !username}" />
    <label for="username">{{ $t('SYSTEM.USER_NAME') }}</label>
    <small class="p-invalid" v-if="submitted && !username">{{ $t('REQUIRED.USER_NAME') }}</small>
  </span>
   </div>
   <div class="p-field p-py-2">
  <span class="p-float-label">
    <InputText type="password" v-model="password" required="true" :class="{'p-invalid': submitted && !password}" />
    <label for="password">{{ $t('SYSTEM.PASSWORD') }}</label>
    <small class="p-invalid" v-if="submitted && !password">{{ $t('REQUIRED.PASSWORD') }}</small>
  </span>
   </div>
 </div>
    <Button icon="pi pi-sign-in" label="Login" @click="Login($event)" class="p-d-block p-m-2" >Login</Button>
</div>
</template>

<script>
import {apiUserLogin} from '@/apis/SystemApi.js';
export default {
  data() {
    return {
      username: "",
      password: "",
      submitted: false,
      blockedDocument: false
    };
  },
  mounted() {
      let jwtToken = localStorage.getItem("jwtToken");      
      if (jwtToken) {
        this.$router.push("/");
      }
  },
  methods: {
    async Login() {
      this.submitted = true;
      
      if (this.username && this.password) {
        let data = {"username" : this.username, "password": this.password};
        const token =  await apiUserLogin(data).then(data=>data.token);
        if (token){
            localStorage.setItem("jwtToken", token);                     
            setTimeout(() => {
              this.$router.push("/");            
            }, 300);            
            this.blockedDocument = false;
        } else {
          localStorage.removeItem("jwtToken");
        }
        this.blockedDocument = false;
      }
    },
  },
};
</script>

 

Copyright © All rights reserved. | Newsphere by AF themes.