上篇我们在单一元件内使用 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",
},
});
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);
});
axios 回传的会是一个 promise 物件,下列图示为发送 request 後所得到的回传结果:
config
:发送请求时的配置data
:实际的响应主体,即回传的资料内容headers
:服务器发回标头request
:XMLHttpRequest 物件status
:HTTP 状态码statusText
:以文字讯息形式返回的 HTTP 状态原始 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); // 单一书籍资料
},
}
很多投资人都听过,价值型投资与成长型投资 那究竟这两种的差异是甚麽呢?我来给各位做个简单的解释。 价...
延续I 变成每种element最多出现2次。 思路 第一直觉是,多加一个判断几次的变数 ...
Display 我认为有了html和css的基本概念後 还有一个观念需要知道的 那就是display...
前言 昨天我们做了新增point表的判断 今天要正式在point表确认送出分数 并储存於资料库里了 ...
程序在执行的时候,有些时候我们会遇到一些例外的情况,我们一般会使用 try-catch 来拦截程序执...