iT邦幫忙

2021 iThome 鐵人賽

DAY 16
0
Modern Web

Vue.js 什麼意思系列 第 16

Day 16:axios 先封裝,API 輕鬆發

上篇我們在單一元件內使用 axios 發送 API,但如果專案規模愈來愈大,需要同時管理多個功能的 API,例如顯示單一書目資料、會員登入系統、查詢會員資料、查詢會員收藏紀錄等,而每次都要重複寫同樣的內容像是 import axios、url、return response 等等等,一直複製也是沒完沒了,所以我們乾脆先來個封裝,把這些重複的瑣事包起來,就在一開始包一次就好,之後搭配使用 Vuex 管理資料狀態時也會更加方便。

那麼先來新增一個名為 utilities 資料夾存放共用工具,並在其中建立 API.js 來封裝 api 內容。

axios.create([config])

利用 create 創造一個 axios 實例來進行個人化的基本設置。以本系列的 API 為例,完整 URL 是「 https://bookshelf.goodideas-studio.com/api 」,主要 domain 是「 https://bookshelf.goodideas-studio.com 」,其後會再接續各種巢狀路由,有時候還會再加上版次,因此可以根據 API 規格設定變數加以彈性控制,組合而成的 baseURL 則是固定發送 API 的基本 URL。

import axios from "axios";

const domain = "https://bookshelf.goodideas-studio.com";
// 版次
// const apiVersion = 'v1';

const bookAPI = axios.create({
  baseURL: `${domain}/api`,
	// 加上板次
  // baseURL: `${domain}/api/${apiVersion}`,
  headers: {
    "Content-Type": "application/json",
    accept: "application/json",
  },
});

Interceptors 攔截器

axios 攔截器可以在發送 requests 或回傳 responses 而進入 then 或 catch 之前,設定需要提前處理的事項。例如會員登入系統需要驗證會員身份,同時登入 API 在設定規格時也會指定請求需在 header 帶上 Token(加密權杖),所以當會員點擊登入按鈕、觸發登入 API 時,趕在發送 requests 到後端之前,先判斷該名會員是否有 Token 資料,若有則將 Token 帶入 header 跟著 request 一起傳給後端,這樣一來後端就能從 request header 取得 Token 進行會員身份的驗證。

// Add a request interceptor
bookAPI.interceptors.request.use(
  function (config) {
    // Do something before request is sent

	// 會員系統需驗證身份時
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`;
    }
    return config;
  },
  function (error) {
    return Promise.reject(error);
  }
);

// Add a response interceptor
bookAPI.interceptors.response.use(
	function (response) {
		// 任何 HTTP status code 為 2xx 開頭時觸發此函式
    return response;
  }, 
	function (error) {
		// 任何 HTTP status code 非 2xx 開頭時觸發此函式
    return Promise.reject(error.response);
  });

Request method

axios 回傳的會是一個 promise 物件,下列圖示為發送 request 後所得到的回傳結果:

  • config:發送請求時的配置
  • data:實際的響應主體,即回傳的資料內容
  • headers:伺服器發回標頭
  • request:XMLHttpRequest 物件
  • status:HTTP 狀態碼
  • statusText:以文字訊息形式返回的 HTTP 狀態

axios response

原始 async await 寫法(參考 axios 範例):

async function fetchBooks() {
  try {
    const response = await axios.get('https://bookshelf.goodideas-studio.com/api');
		return response;
  } catch (error) {
    return Promise.reject(error);
  }
}

由於 data 物件內的資料才是我們所需要的資料本身,因此在封裝時直接回傳 promise 物件裡的 data 以取得 API 資料。

// utilities/API.js
async function GET(url, params) {
  try {
    const response = await bookAPI.get(url, params);
    return response.data;
  }
  catch (error) {
    return Promise.reject(error);
  }
}
// 封裝其他請求方法如 POST、PUT...

export { GET, POST, PUT };

之後在 Vuex 統一管理 API 發送的流程,由於 baseURL 已將 domain 設為基本設置,因此在 url 只需傳入 API 末段路徑即可。

  • 本系列使用的 API 規格並無末段路徑,因此在此不用帶任何參數直接執行。

    // store/index.js
    import { GET } from '@/utilities/API';
    export default new Vuex.Store({
      // ...
    	actions: {
    		async fetchBookList(context) {
    			const books = await GET();
    			console.log(books); // 所有書單資料
    			context.commit('bookList',books);
    		},
    	}
    })
    
  • 假設今天是要從所有書單裡查詢單一書籍資料,則 API 規格可能會是「 https://bookshelf.goodideas-studio.com/api/books/ISBNId 」,此時就可以使用封裝時的參數進行設置。

    	actions: {
    		async fetchBook(context, ISBNId) {
    			const book = await GET(`/books/${ISBNId}`);
    			console.log(book); // 單一書籍資料
    		},
    	}
    

參考資料


上一篇
Day 15:在生命週期呼喊 API
下一篇
Day 17:異步行動,同步變動-Vuex Actions、Mutations
系列文
Vue.js 什麼意思30

尚未有邦友留言

立即登入留言