From e7799d2db1ad731e70d4a897ae578a430765ee4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=90=E6=AE=8A?= Date: Tue, 24 Sep 2024 15:48:42 +0800 Subject: [PATCH] typo(packages): add types & update code --- packages/alova/src/index.ts | 77 +++++++++++++++++++------------------ packages/alova/src/type.ts | 64 ++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 37 deletions(-) create mode 100644 packages/alova/src/type.ts diff --git a/packages/alova/src/index.ts b/packages/alova/src/index.ts index 5ec59335..1d17ef1e 100644 --- a/packages/alova/src/index.ts +++ b/packages/alova/src/index.ts @@ -1,24 +1,12 @@ import { createAlova } from 'alova'; import VueHook from 'alova/vue'; import adapterFetch from 'alova/fetch'; -import { nanoid } from '@sa/utils'; import { createServerTokenAuthentication } from 'alova/client'; import { BACKEND_ERROR_CODE, REQUEST_ID_KEY } from './constant'; import { isJSON } from './shared'; +import type { CustomAlovaConfig, RequestOptions } from './type'; -export interface CustomAlovaConfig { - baseURL: string; - headers?: Record; - timeout?: number; - expiredTokenCodes: string[]; -} - -export interface RequestOptions { - refreshTokenHandler: () => Promise; - onBackendFail: (response: Response) => Promise; -} - -export const createAlovaRequest = (customConfig: CustomAlovaConfig, options: RequestOptions) => { +export const createAlovaRequest = (customConfig: CustomAlovaConfig, options: RequestOptions) => { const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({ refreshTokenOnSuccess: { isExpired: async response => { @@ -34,46 +22,60 @@ export const createAlovaRequest = (customConfig: CustomAlovaConfig, options: Req return false; }, handler: async () => { - await options.refreshTokenHandler(); + if (options.refreshTokenHandler) { + await options.refreshTokenHandler(); + } } }, - // 附加token - assignToken: method => { - const token = localStorage.getItem('SOY_token'); - method.config.headers.Authorization = token ? `Bearer ${JSON.parse(token)}` : null; + refreshTokenOnError: { + isExpired: async response => { + const contentType = response.headers.get('Content-Type'); + if (isJSON(contentType ?? '')) { + const resp = response.clone(); + const data = await resp.json(); + const responseCode = String(data.code); + if (customConfig.expiredTokenCodes.includes(responseCode)) { + return true; + } + } + return false; + }, + handler: async () => { + if (options.refreshTokenHandler) { + await options.refreshTokenHandler(); + } + } } }); const instance = createAlova({ - baseURL: customConfig.baseURL, + ...customConfig, timeout: customConfig.timeout ?? 10 * 1000, - requestAdapter: adapterFetch(), + requestAdapter: customConfig.requestAdapter ?? adapterFetch(), statesHook: VueHook, - beforeRequest: onAuthRequired(({ config }) => { - // 添加配置headers - config.headers = { - ...config.headers, - ...customConfig.headers - }; - - // set request id - const requestId = nanoid(); - config.headers[REQUEST_ID_KEY] = requestId; - }), + beforeRequest: onAuthRequired(options.onRequest), responded: onResponseRefreshToken({ onSuccess: async resp => { + // check if http status is success if (resp.ok || resp.status === 304) { - if (isJSON(resp.headers.get('Content-Type') ?? '')) { + if ( + !isJSON(resp.headers.get('Content-Type') ?? '') || + (options.isBackendSuccess && (await options.isBackendSuccess(resp))) + ) { + return resp; + } + if (options.onBackendFail) { const fail = await options.onBackendFail(resp); if (fail) { return fail; } - return await resp.json(); } - return resp; + return options.transformBackendResponse ? await options.transformBackendResponse(resp) : resp; } - return Promise.reject(resp); - } + throw new Error(resp.statusText); + }, + onComplete: options.onComplete, + onError: options.onError }) }); @@ -81,3 +83,4 @@ export const createAlovaRequest = (customConfig: CustomAlovaConfig, options: Req }; export { BACKEND_ERROR_CODE, REQUEST_ID_KEY }; +export type * from './type'; diff --git a/packages/alova/src/type.ts b/packages/alova/src/type.ts new file mode 100644 index 00000000..335b8804 --- /dev/null +++ b/packages/alova/src/type.ts @@ -0,0 +1,64 @@ +import type { + AlovaGenerics, + AlovaOptions, + AlovaRequestAdapter, + Method, + ResponseCompleteHandler, + ResponseErrorHandler +} from 'alova'; + +export type CustomAlovaConfig = Omit< + AlovaOptions, + 'statesHook' | 'beforeRequest' | 'responded' | 'requestAdapter' +> & { + /** expired token codes */ + expiredTokenCodes: string[]; + /** request adapter. all request of alova will be sent by it. */ + requestAdapter?: AlovaRequestAdapter; +}; + +export interface RequestOptions { + /** + * The hook before request + * + * For example: You can add header token in this hook + * + * @param method alova Method Instance + */ + onRequest?: (method: Method) => void | Promise; + /** + * The hook to check backend response is success or not + * + * @param response Axios response + */ + isBackendSuccess?: (response: Response) => Promise; + + /** The hook to refresh token */ + refreshTokenHandler?: () => Promise; + /** + * The hook after backend request fail + * + * For example: You can handle the expired token in this hook + * + * @param response Axios response + * @param instance Axios instance + */ + onBackendFail?: (response: Response) => Promise | Promise; + + onComplete?: ResponseCompleteHandler; + + /** + * The hook to handle error + * + * For example: You can show error message in this hook + * + * @param error + */ + onError?: ResponseErrorHandler; + /** + * transform backend response when the responseType is json + * + * @param response Axios response + */ + transformBackendResponse?: (response: AG['Response']) => any | Promise; +}