import Axios, { AxiosResponse, AxiosRequestConfig, AxiosError } from 'axios'; export type ResponseDataExtractor = (response: AxiosResponse) => D; export interface ApiError { message: string; source: string; statusCode: number; status: string; } export class ApiClient { constructor( protected baseUrl: string, protected headerProcessor: () => object, protected extractor: ResponseDataExtractor, protected errorHandler: (error: ApiError) => void ) { } async get( url: string, params?: object, apiOptions: ApiOptions = {} ): Promise { const request: AxiosRequestConfig = { ...this.baseRequestOptions(apiOptions), method: 'get', url, params }; return this.execute(request); } // 仅仅适用于web端 exportFile( url: string, params?: object, apiOptions: ApiOptions = {}, tokenKeyName?: string ): void { const request = { ...this.baseRequestOptions(apiOptions), method: 'get', url, params }; // 参数拼接,默认为 gssToken const tokenKey = tokenKeyName || 'gssToken'; const preWholeUrl = `${request.baseURL}${request.url}`; let tokenValue = (request.headers.hasOwnProperty('Authorization')) ? (request.headers as any).Authorization : void 0; if (!tokenValue) { return; } tokenValue = tokenValue.replace('Bearer ', ''); const paramsUrl = this.encodeSearchParams({ ...request.params, [tokenKey]: tokenValue } || {}); const finalUrl = `${preWholeUrl}?${paramsUrl}`; // 开始下载 this.dynamicDownload(finalUrl); } // 获取请求地址和请求头 getAllRequestData( url: string, params?: object, apiOptions: ApiOptions = {}, tokenKeyName?: string ): Promise { const request = { ...this.baseRequestOptions(apiOptions), method: 'get', url, params }; const tokenKey = tokenKeyName || 'gssToken'; const preWholeUrl = `${request.baseURL}${request.url}`; let tokenValue = (request.headers.hasOwnProperty('Authorization')) ? (request.headers as any).Authorization : void 0; const paramsUrl = this.encodeSearchParams({ ...request.params, [tokenKey]: tokenValue } || {}); const finalUrl = `${preWholeUrl}?${paramsUrl}`; return { wholeUrl: finalUrl, authorization: tokenValue } as any; } async delete( url: string, params?: object, apiOptions: ApiOptions = {} ): Promise { const request: AxiosRequestConfig = { ...this.baseRequestOptions(apiOptions), method: 'delete', url, params }; return this.execute(request); } async post( url: string, data?: object | any[], params?: object, apiOptions: ApiOptions = {} ): Promise { const request: AxiosRequestConfig = { ...this.baseRequestOptions(apiOptions), method: 'post', url, data, params }; return this.execute(request); } async put( url: string, data?: object, params?: object, apiOptions: ApiOptions = {} ): Promise { const request: AxiosRequestConfig = { ...this.baseRequestOptions(apiOptions), method: 'put', url, data, params }; return this.execute(request); } async patch( url: string, params?: object, apiOptions: ApiOptions = {} ): Promise { const request: AxiosRequestConfig = { ...this.baseRequestOptions(apiOptions), method: 'patch', url, params }; return this.execute(request); } async execute(axiosOptions: AxiosRequestConfig): Promise { try { const resp = await Axios.request(axiosOptions); // console.log('status code:==', resp.status); return this.extractor(resp); } catch (e) { const error = e as AxiosError; if (error.response) { // The request was made and the server responded with a status code // that falls out of the range of 2xx const message = JSON.stringify(error.response.data); // throw new Error(`HTTP Status: ${error.response.status}, ${message}`) const apiError: ApiError = { source: '', message: message, status: error.response.statusText, statusCode: error.response.status }; // console.log(this.errorHandler) this.errorHandler(apiError); throw new Error(`HTTP Status: ${error.response.status}, ${message}`); } else if (error.request) { // The request was made but no response was received // `error.request` is an instance of XMLHttpRequest in the browser and an instance of // http.ClientRequest in node.js console.log(error.request); throw new Error(`网络没有响应, 请检查网络然后重试`); } else { // Something happened in setting up the request that triggered an Error console.log('Error', error.message); throw e; } // const error=e as ApiError; // if (error.response) { // // The request was made and the server responded with a status code // // that falls out of the range of 2xx // const message = JSON.stringify(error.response.data); // throw new Error(`HTTP Status: ${error.response.status}, ${message}`) // } else if (error.request) { // // The request was made but no response was received // // `error.request` is an instance of XMLHttpRequest in the browser and an instance of // // http.ClientRequest in node.js // console.log(error.request); // throw new Error(`网络没有响应, 请检查网络然后重试`); // } else { // // Something happened in setting up the request that triggered an Error // console.log('Error', error.message); // throw e; // } } } private dynamicDownload(url: string) { const link = document.createElement('iframe'); link.style.display = 'none'; link.src = url; document.body.appendChild(link); link.onload = function() { link.parentNode.removeChild(link); }; } private encodeSearchParams(paramsObj: object): string { const paramsArr = Array.of(); Object.keys(paramsObj).forEach(key => { let value = (paramsObj as any)[key]; // 如果值为undefined, 则置空 if (([undefined, 'undefined'] as any).includes(value)) { value = ''; } // 针对需要编码的进行编码(比如中文)编码 paramsArr.push([key, encodeURIComponent(value)].join('=')); }); return paramsArr.join('&'); } private baseRequestOptions({ ignoreHeaders }: ApiOptions = {}) { const headers = ignoreHeaders ? {} : this.headerProcessor(); const baseRequest = { baseURL: this.baseUrl, method: 'get', headers }; return baseRequest; } } export interface ApiOptions { ignoreHeaders?: boolean; }