|
| 1 | +// @ts-nocheck |
| 2 | +/** |
| 3 | + * Base on https://github.com/umijs//Users/mtianyan/tyRepos/tyadmin_api_cli/demos/tyadmin_demo_init/tyadmin/node_modules/umi-request |
| 4 | + */ |
| 5 | +import { |
| 6 | + extend, |
| 7 | + Context, |
| 8 | + RequestOptionsInit, |
| 9 | + OnionMiddleware, |
| 10 | + RequestOptionsWithoutResponse, |
| 11 | + RequestMethod, |
| 12 | + RequestOptionsWithResponse, |
| 13 | + RequestResponse, |
| 14 | + RequestInterceptor, |
| 15 | + ResponseInterceptor, |
| 16 | +} from '/Users/mtianyan/tyRepos/tyadmin_api_cli/demos/tyadmin_demo_init/tyadmin/node_modules/umi-request'; |
| 17 | +// @ts-ignore |
| 18 | + |
| 19 | +import { ApplyPluginsType } from 'umi'; |
| 20 | +import { history, plugin } from '../core/umiExports'; |
| 21 | + |
| 22 | +import { message, notification } from 'antd'; |
| 23 | +import useUmiRequest, { UseRequestProvider } from '/Users/mtianyan/tyRepos/tyadmin_api_cli/demos/tyadmin_demo_init/tyadmin/node_modules/@ahooksjs/use-request'; |
| 24 | +import { |
| 25 | + BaseOptions, |
| 26 | + BasePaginatedOptions, |
| 27 | + BaseResult, |
| 28 | + CombineService, |
| 29 | + LoadMoreFormatReturn, |
| 30 | + LoadMoreOptions, |
| 31 | + LoadMoreOptionsWithFormat, |
| 32 | + LoadMoreParams, |
| 33 | + LoadMoreResult, |
| 34 | + OptionsWithFormat, |
| 35 | + PaginatedFormatReturn, |
| 36 | + PaginatedOptionsWithFormat, |
| 37 | + PaginatedParams, |
| 38 | + PaginatedResult, |
| 39 | +} from '/Users/mtianyan/tyRepos/tyadmin_api_cli/demos/tyadmin_demo_init/tyadmin/node_modules/@ahooksjs/use-request/lib/types'; |
| 40 | + |
| 41 | +type ResultWithData<T = any> = { data: T; [key: string]: any }; |
| 42 | + |
| 43 | +function useRequest< |
| 44 | + R = any, |
| 45 | + P extends any[] = any, |
| 46 | + U = any, |
| 47 | + UU extends U = any |
| 48 | +>( |
| 49 | + service: CombineService<R, P>, |
| 50 | + options: OptionsWithFormat<R, P, U, UU>, |
| 51 | +): BaseResult<U, P>; |
| 52 | +function useRequest<R extends ResultWithData = any, P extends any[] = any>( |
| 53 | + service: CombineService<R, P>, |
| 54 | + options?: BaseOptions<R['data'], P>, |
| 55 | +): BaseResult<R['data'], P>; |
| 56 | +function useRequest<R extends LoadMoreFormatReturn = any, RR = any>( |
| 57 | + service: CombineService<RR, LoadMoreParams<R>>, |
| 58 | + options: LoadMoreOptionsWithFormat<R, RR>, |
| 59 | +): LoadMoreResult<R>; |
| 60 | +function useRequest< |
| 61 | + R extends ResultWithData<LoadMoreFormatReturn> = any, |
| 62 | + RR extends R = any |
| 63 | +>( |
| 64 | + service: CombineService<R, LoadMoreParams<R['data']>>, |
| 65 | + options: LoadMoreOptions<RR['data']>, |
| 66 | +): LoadMoreResult<R['data']>; |
| 67 | + |
| 68 | +function useRequest<R = any, Item = any, U extends Item = any>( |
| 69 | + service: CombineService<R, PaginatedParams>, |
| 70 | + options: PaginatedOptionsWithFormat<R, Item, U>, |
| 71 | +): PaginatedResult<Item>; |
| 72 | +function useRequest<Item = any, U extends Item = any>( |
| 73 | + service: CombineService< |
| 74 | + ResultWithData<PaginatedFormatReturn<Item>>, |
| 75 | + PaginatedParams |
| 76 | + >, |
| 77 | + options: BasePaginatedOptions<U>, |
| 78 | +): PaginatedResult<Item>; |
| 79 | +function useRequest(service: any, options: any = {}) { |
| 80 | + return useUmiRequest(service, { |
| 81 | + formatResult: result => result?.data, |
| 82 | + requestMethod: (requestOptions: any) => { |
| 83 | + if (typeof requestOptions === 'string') { |
| 84 | + return request(requestOptions); |
| 85 | + } |
| 86 | + if (typeof requestOptions === 'object') { |
| 87 | + const { url, ...rest } = requestOptions; |
| 88 | + return request(url, rest); |
| 89 | + } |
| 90 | + throw new Error('request options error'); |
| 91 | + }, |
| 92 | + ...options, |
| 93 | + }); |
| 94 | +} |
| 95 | + |
| 96 | +export interface RequestConfig extends RequestOptionsInit { |
| 97 | + errorConfig?: { |
| 98 | + errorPage?: string; |
| 99 | + adaptor?: (resData: any, ctx: Context) => ErrorInfoStructure; |
| 100 | + }; |
| 101 | + middlewares?: OnionMiddleware[]; |
| 102 | + requestInterceptors?: RequestInterceptor[]; |
| 103 | + responseInterceptors?: ResponseInterceptor[]; |
| 104 | +} |
| 105 | + |
| 106 | +export enum ErrorShowType { |
| 107 | + SILENT = 0, |
| 108 | + WARN_MESSAGE = 1, |
| 109 | + ERROR_MESSAGE = 2, |
| 110 | + NOTIFICATION = 4, |
| 111 | + REDIRECT = 9, |
| 112 | +} |
| 113 | + |
| 114 | +interface ErrorInfoStructure { |
| 115 | + success: boolean; |
| 116 | + data?: any; |
| 117 | + errorCode?: string; |
| 118 | + errorMessage?: string; |
| 119 | + showType?: ErrorShowType; |
| 120 | + traceId?: string; |
| 121 | + host?: string; |
| 122 | + [key: string]: any; |
| 123 | +} |
| 124 | + |
| 125 | +interface RequestError extends Error { |
| 126 | + data?: any; |
| 127 | + info?: ErrorInfoStructure; |
| 128 | + request?: Context['req']; |
| 129 | + response?: Context['res']; |
| 130 | +} |
| 131 | + |
| 132 | +const DEFAULT_ERROR_PAGE = '/exception'; |
| 133 | + |
| 134 | +let requestMethodInstance: RequestMethod; |
| 135 | +const getRequestMethod = () => { |
| 136 | + if (requestMethodInstance) { |
| 137 | + // request method 已经示例化 |
| 138 | + return requestMethodInstance; |
| 139 | + } |
| 140 | + |
| 141 | + // runtime 配置可能应为依赖顺序的问题在模块初始化的时候无法获取,所以需要封装一层在异步调用后初始化相关方法 |
| 142 | + // 当用户的 app.ts 中依赖了该文件的情况下就该模块的初始化时间就会被提前,无法获取到运行时配置 |
| 143 | + const requestConfig: RequestConfig = plugin.applyPlugins({ |
| 144 | + key: 'request', |
| 145 | + type: ApplyPluginsType.modify, |
| 146 | + initialValue: {}, |
| 147 | + }); |
| 148 | + |
| 149 | + const errorAdaptor = |
| 150 | + requestConfig.errorConfig?.adaptor || (resData => resData); |
| 151 | + |
| 152 | + requestMethodInstance = extend({ |
| 153 | + errorHandler: (error: RequestError) => { |
| 154 | + // @ts-ignore |
| 155 | + if (error?.request?.options?.skipErrorHandler) { |
| 156 | + throw error; |
| 157 | + } |
| 158 | + let errorInfo: ErrorInfoStructure | undefined; |
| 159 | + if (error.name === 'ResponseError' && error.data && error.request) { |
| 160 | + const ctx: Context = { |
| 161 | + req: error.request, |
| 162 | + res: error.response, |
| 163 | + }; |
| 164 | + errorInfo = errorAdaptor(error.data, ctx); |
| 165 | + error.message = errorInfo?.errorMessage || error.message; |
| 166 | + error.data = error.data; |
| 167 | + error.info = errorInfo; |
| 168 | + } |
| 169 | + errorInfo = error.info; |
| 170 | + |
| 171 | + if (errorInfo) { |
| 172 | + const errorMessage = errorInfo?.errorMessage; |
| 173 | + const errorCode = errorInfo?.errorCode; |
| 174 | + const errorPage = |
| 175 | + requestConfig.errorConfig?.errorPage || DEFAULT_ERROR_PAGE; |
| 176 | + |
| 177 | + switch (errorInfo?.showType) { |
| 178 | + case ErrorShowType.SILENT: |
| 179 | + // do nothing |
| 180 | + break; |
| 181 | + case ErrorShowType.WARN_MESSAGE: |
| 182 | + message.warn(errorMessage); |
| 183 | + break; |
| 184 | + case ErrorShowType.ERROR_MESSAGE: |
| 185 | + message.error(errorMessage); |
| 186 | + break; |
| 187 | + case ErrorShowType.NOTIFICATION: |
| 188 | + notification.open({ |
| 189 | + message: errorMessage, |
| 190 | + }); |
| 191 | + break; |
| 192 | + case ErrorShowType.REDIRECT: |
| 193 | + // @ts-ignore |
| 194 | + history.push({ |
| 195 | + pathname: errorPage, |
| 196 | + query: { errorCode, errorMessage }, |
| 197 | + }); |
| 198 | + // redirect to error page |
| 199 | + break; |
| 200 | + default: |
| 201 | + message.error(errorMessage); |
| 202 | + break; |
| 203 | + } |
| 204 | + } else { |
| 205 | + message.error(error.message || 'Request error, please retry.'); |
| 206 | + } |
| 207 | + throw error; |
| 208 | + }, |
| 209 | + ...requestConfig, |
| 210 | + }); |
| 211 | + |
| 212 | + // 中间件统一错误处理 |
| 213 | + // 后端返回格式 { success: boolean, data: any } |
| 214 | + // 按照项目具体情况修改该部分逻辑 |
| 215 | + requestMethodInstance.use(async (ctx, next) => { |
| 216 | + await next(); |
| 217 | + const { req, res } = ctx; |
| 218 | + // @ts-ignore |
| 219 | + if (req.options?.skipErrorHandler) { |
| 220 | + return; |
| 221 | + } |
| 222 | + const { options } = req; |
| 223 | + const { getResponse } = options; |
| 224 | + const resData = getResponse ? res.data : res; |
| 225 | + const errorInfo = errorAdaptor(resData, ctx); |
| 226 | + if (errorInfo.success === false) { |
| 227 | + // 抛出错误到 errorHandler 中处理 |
| 228 | + const error: RequestError = new Error(errorInfo.errorMessage); |
| 229 | + error.name = 'BizError'; |
| 230 | + error.data = resData; |
| 231 | + error.info = errorInfo; |
| 232 | + throw error; |
| 233 | + } |
| 234 | + }); |
| 235 | + |
| 236 | + // Add user custom middlewares |
| 237 | + const customMiddlewares = requestConfig.middlewares || []; |
| 238 | + customMiddlewares.forEach(mw => { |
| 239 | + requestMethodInstance.use(mw); |
| 240 | + }); |
| 241 | + |
| 242 | + // Add user custom interceptors |
| 243 | + const requestInterceptors = requestConfig.requestInterceptors || []; |
| 244 | + const responseInterceptors = requestConfig.responseInterceptors || []; |
| 245 | + requestInterceptors.map(ri => { |
| 246 | + requestMethodInstance.interceptors.request.use(ri); |
| 247 | + }); |
| 248 | + responseInterceptors.map(ri => { |
| 249 | + requestMethodInstance.interceptors.response.use(ri); |
| 250 | + }); |
| 251 | + |
| 252 | + return requestMethodInstance; |
| 253 | +}; |
| 254 | + |
| 255 | +interface RequestMethodInUmi<R = false> { |
| 256 | + <T = any>( |
| 257 | + url: string, |
| 258 | + options: RequestOptionsWithResponse & { skipErrorHandler?: boolean }, |
| 259 | + ): Promise<RequestResponse<T>>; |
| 260 | + <T = any>( |
| 261 | + url: string, |
| 262 | + options: RequestOptionsWithoutResponse & { skipErrorHandler?: boolean }, |
| 263 | + ): Promise<T>; |
| 264 | + <T = any>( |
| 265 | + url: string, |
| 266 | + options?: RequestOptionsInit & { skipErrorHandler?: boolean }, |
| 267 | + ): R extends true ? Promise<RequestResponse<T>>: Promise<T>; |
| 268 | +} |
| 269 | +const request: RequestMethodInUmi = (url: any, options: any) => { |
| 270 | + const requestMethod = getRequestMethod(); |
| 271 | + return requestMethod(url, options); |
| 272 | +}; |
| 273 | + |
| 274 | +export { request, useRequest, UseRequestProvider }; |
0 commit comments