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); // 单一书籍资料
    		},
    	}
    

参考资料


<<:  DAY16:清单元件之简介

>>:  Day 19 不得不变的学习方式

价值型投资 VS 成长型投资

很多投资人都听过,价值型投资与成长型投资 那究竟这两种的差异是甚麽呢?我来给各位做个简单的解释。 价...

Leetcode: 80. Remove Duplicates from Sorted Array II

延续I 变成每种element最多出现2次。   思路 第一直觉是,多加一个判断几次的变数     ...

Day15来吧 展示(CSS)

Display 我认为有了html和css的基本概念後 还有一个观念需要知道的 那就是display...

[Day26] 第二十六章-使用patch送出分数并且修改前端edit.blade.php

前言 昨天我们做了新增point表的判断 今天要正式在point表确认送出分数 并储存於资料库里了 ...

Day10:例外处理,留下来或我跟你走

程序在执行的时候,有些时候我们会遇到一些例外的情况,我们一般会使用 try-catch 来拦截程序执...