簡介
自訂參數可以其調用時更客製化,像是要連兩個不同的 RESTful API 來源時,或著對不同的 API 有不同的等待時間時,都可以更容易的調整想要的呼叫方式。
開發
加入 Axios 的抽像轉換函式
主要為不同的 Axios 實例可以實作自己的轉換函式,所以才需要先定義成抽像的。
建立 ./src/utils/http/axios/axiosTransform.ts
1 2 3 4 5 6 7 8 9
| import type { AxiosRequestConfig, AxiosResponse } from 'axios' import type { RequestOptions, Result } from './types'
export abstract class AxiosTransform { beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig transformRequestData?: (res: AxiosResponse<Result>, options: RequestOptions) => any }
|
調整呼叫 Axios 的 Interface
讓轉換函式及自訂參考都先定義好明確的 Interface,在初使化 Axios 或調用 request 時,可以透過強型態取得
調整 ./src/utils/http/axios/types.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| import type { AxiosRequestConfig } from 'axios' import type { AxiosTransform } from './axiosTransform'
export interface CreateAxiosOptions extends AxiosRequestConfig { transform?: AxiosTransform requestOptions?: RequestOptions }
export interface RequestOptions { apiUrl?: string urlPrefix?: string joinTime?: boolean
isReturnNativeResponse?: boolean isTransformResponse?: boolean isShowMessage?: boolean }
export interface Result<T = any> { code: number type?: 'success' | 'error' | 'warning' message: string result?: T }
|
添加 ResultEnum 的可讀性 Enums
調整 ./src/enums/httpEnum.ts
加入 ResultEnum
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
export enum ResultEnum { SUCCESS = 200, ERROR = -1, TIMEOUT = 10042, TYPE = 'success', }
export enum RequestEnum { GET = 'GET', POST = 'POST', PATCH = 'PATCH', PUT = 'PUT', DELETE = 'DELETE', }
export enum ContentTypeEnum { JSON = 'application/json;charset=UTF-8', TEXT = 'text/plain;charset=UTF-8', FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', FORM_DATA = 'multipart/form-data;charset=UTF-8', }
|
加入 helper 放置輔助函数
其實就是把一些常用的函数取出,也可以放置原頁面。
建立 ./src/utils/http/axios/helper.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| export function joinTimestamp(join: boolean, restful = false): string | object { if (!join) return restful ? '' : {}
const now = new Date().getTime() if (restful) return `?_t=${now}`
return { _t: now } }
export function isUrl(url: string): boolean { return /(^http|https:\/\/)/g.test(url) }
|
撰寫轉換函式並在建立 Axios 時帶入
實作 Axios 所需要的轉換函式,並在建立 Axios 實體時將其帶入,於 Axios.request 時就可以予之呼叫使用。
另外在建立 Axios 實體時也一併將相關的客製化參數填入。
也可以先看下一章是用在那邊,會更好明白轉換函式的寫法
調整 ./src/utils/http/axios/index.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| import type { AxiosRequestConfig, AxiosResponse } from 'axios' import { isString } from '@vueuse/core' import { LKAxios } from './Axios' import type { CreateAxiosOptions, RequestOptions, Result } from './types' import type { AxiosTransform } from './axiosTransform' import { isUrl, joinTimestamp } from './helper' import { ContentTypeEnum, RequestEnum, ResultEnum } from '~/enums/httpEnum' import { deepMerge } from '~/utils'
const transform: AxiosTransform = { beforeRequestHook(config: AxiosRequestConfig, options: RequestOptions) { const { apiUrl, urlPrefix, joinTime = true } = options const isUrlStr = isUrl(config.url as string)
if (!isUrlStr && urlPrefix && isString(urlPrefix)) config.url = `${urlPrefix}${config.url}`
if (!isUrlStr && apiUrl && isString(apiUrl)) config.url = `${apiUrl}${config.url}`
const params = config.params || {} if (config.method?.toUpperCase() === RequestEnum.GET) { if (!isString(params)) { config.params = Object.assign(params || {}, joinTimestamp(joinTime, false)) } else { config.url = `${config.url + params}${joinTimestamp(joinTime, true)}` config.params = undefined } } return config }, transformRequestData(res: AxiosResponse<Result>, options: RequestOptions) { const { isReturnNativeResponse, isTransformResponse, isShowMessage, } = options if (isReturnNativeResponse) return res if (!isTransformResponse) return res.data const { data } = res if (!data) throw new Error('request error, please try again later.') const { code, result, message } = data const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS if (isShowMessage) { if (hasSuccess) console.info(`[axios-request]-${message || 'Successful Operation!'}`) } if (code === ResultEnum.SUCCESS) return result
let errorMsg = message let neededLogout = false switch (code) { case ResultEnum.ERROR: errorMsg = `${errorMsg || 'Operation Failed!'}` break case ResultEnum.TIMEOUT: errorMsg = 'timeout, please login again' neededLogout = true break }
if (isShowMessage) console.error(`[axios-request]-${errorMsg}`)
if (neededLogout) window.location.href = '/'
throw new Error(errorMsg) }, }
function createAxios(opt?: Partial<CreateAxiosOptions>) { return new LKAxios( deepMerge({ transform, timeout: 10 * 1000, headers: { 'Content-Type': ContentTypeEnum.JSON }, requestOptions: { apiUrl: '', urlPrefix: '/api/v1', joinTime: true, isReturnNativeResponse: false, isTransformResponse: true, isShowMessage: true, }, }, opt || {})) }
export const http = createAxios()
|
實作調用轉換函式
調整 ./src/utils/http/axios/Axios.ts
中呼叫 request 時去調用轉換函式
可以直接查詢轉換函式的位置並進行添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' import axios from 'axios' import { isFunction } from '@vueuse/core' import type { CreateAxiosOptions, RequestOptions, Result } from './types' import { deepMerge } from '~/utils'
export class LKAxios { private axiosInstance: AxiosInstance private options: CreateAxiosOptions constructor(options: CreateAxiosOptions) { this.options = options this.axiosInstance = axios.create(options) }
request<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> { let conf: CreateAxiosOptions = deepMerge({}, config) const { requestOptions } = this.options const opt: RequestOptions = Object.assign({}, requestOptions, options) conf.requestOptions = opt
const { transform } = this.options const { beforeRequestHook, transformRequestData } = transform || {} if (beforeRequestHook && isFunction(beforeRequestHook)) conf = beforeRequestHook(conf, opt)
return new Promise((resolve, reject) => { this.axiosInstance .request<any, AxiosResponse<Result>>(conf) .then((res: AxiosResponse<Result>) => { const isCancel = axios.isCancel(res) if (transformRequestData && isFunction(transformRequestData) && !isCancel) { try { const ret = transformRequestData(res, opt) resolve(ret) } catch (err) { reject(err || new Error('request error!')) } return } resolve(res as unknown as Promise<T>) }) .catch((e: Error) => { reject(e) }) }) } }
|
測試
在任意的 .vue
檔案中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <script setup lang="ts"> import { http } from '~/utils/http/axios'
http.request({ url: '/test', method: 'get', params: { id: 3 } }).then((res) => { console.log(res) })
http.request({ url: '/test', method: 'post', data: { name: 'new name' } }).then((res) => { console.log(res) })
http.request({ url: '/test/6', method: 'put', data: { name: 'edit name' } }).then((res) => { console.log(res) })
http.request({ url: '/test/1', method: 'delete' }).then((res) => { console.log(res) }) </script>
|
確認 URL 有正確被替換
確認呼叫各 Method 都可以正確回傳結果
參考
Naive UI Third-Party Libraries
Naive UI Admin
Axios
Vitesse
Sample Code Download