/** 默认头信息 */ const DEFAULT_HEAD = { 'Content-Type': 'application/json', 'Accept-Charset': 'utf-8', /** 为解决跨域问题 */ 'Origin': '*' }; type HTTPRequestMethod = "CONNECT" | "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "TRACE"; const HREQM: { [key in HTTPRequestMethod]: string } = { CONNECT: 'lightslategray', HEAD: 'cadetblue', OPTIONS: 'thistle', GET: 'lightgreen', PUT: 'darkcyan', DELETE: 'lightcoral', POST: 'deepskyblue', PATCH: 'silver', TRACE: 'steelblue', } /** * 创建请求头 * @param conf */ function createHeader(conf?: { [field: string]: string }) { let header = new Headers(); [DEFAULT_HEAD, conf].forEach(list => { if (!list || list == null) return void 0; for (let field in list) header.append(field, Reflect.get(list, field)); }); return header; } /** * 格式化请求数据 * @param param 请求数据 * @param type 格式类型 */ function FormateData(param: { [name: string]: string | number }, type: 'json' | 'form' | 'search') { let data: string | FormData; if (type === 'json') data = JSON.stringify(param); else if (type === 'search') { let arr = []; for (let name in param) arr.push(`${name}=${param[name]}`); data = arr.join('&'); } else if (type === 'form') { data = new FormData(); for (let name in param) data.set(name, String(param[name])); } return data; } /** * 创建一个请求对象 * @param url 请求路径 * @param data 请求参数 * @param method 请求方法类型 * @param head 请求头配置 */ function createRequest(url: string, data: any, method?: HTTPRequestMethod, head?: { [name: string]: string }) { let body = FormateData(data, 'json'); let options: RequestInit = { method: method ?? "GET", headers: createHeader(head) } if (options.method == 'POST') options.body = body; let request = new Request(url, options); return request; } /** 向服务器发送请求 */ export async function http_send(...args: Parameters) { args[2] = args[2] ?? 'GET'; let req_token = mtec.string.randomToken(8, 36, t => !tokan_map.has(t)); tokan_map.set(req_token, tokan_map.size); // mtec.log.tag([ // ['SERVICE', 'slategray'], // [args[2], Reflect.get(HREQM, args[2])], // ['>>', 'dodgerblue'] // ].map(_=>_.join(':')).join(';'), args[0].split('/').slice(3).join('/'), '\n--------------\nHEADERS:', args[3], '\nDATA:', args[1], '\n--------------\n'); let start_date = new Date(); let response: Response; let delay = 0; let data: any; let try_count = 0; while (!response?.ok && try_count < 3) { response = await fetch(createRequest(...args)) .then(res => res) .catch(err => { // console.log('请求失败信息', JSON.stringify(err)); log_response('单次请求失败', req_token, args[0], start_date, delay, try_count, data, -100, undefined); return void 0; }); delay = Date.now() - start_date.valueOf(); data = response?.ok ? await response.json() : undefined; try_count++; if (response && response.ok) { } else { __exception_call_list__.forEach(call => call(req_token, try_count, delay, response?.status ?? -101)); } } if (try_count > 1) { log_response('触发接口重试', req_token, args[0], start_date, delay, try_count, data, response?.status ?? -102, response); __exception_call_list__.forEach(call => call(req_token, try_count, delay, response?.status ?? -102)); } else if (delay > 5000) { log_response('请求延迟过高', req_token, args[0], start_date, delay, try_count, data, response?.status ?? -103, response); __exception_call_list__.forEach(call => call(req_token, try_count, delay, response?.status ?? -103)); } if (!response?.ok) { log_response('异常接口最终信息', req_token, args[0], start_date, delay, try_count, data, response?.status ?? -104, response); __exception_call_list__.forEach(call => call(req_token, try_count, delay, response?.status ?? -104)); } mtec.log.tag([ ['SERVICE', 'slategray'], [args[2], Reflect.get(HREQM, args[2])], ['<<', 'seagreen'], data ? ['OK', 'springgreen'] : ['ERR', 'red'], [delay + ' ms', 'silver'] ].map(_ => _.join(':')).join(';'), args[0].split('/').slice(3).join('/'), '\n--------------\nRESPONSE:', response, '\nDATA:', data, '\n--------------\n'); tokan_map.delete(req_token); return data; } type __exception_call__ = (token: string, try_count: number, delay: number, status: number) => void; const __exception_call_list__: Array<__exception_call__> = []; const tokan_map: Map = new Map(); export function on_network_exception(call: __exception_call__) { __exception_call_list__.push(call); } function log_response(tag: string, token: string, url: string, start_date: Date, delay: number, try_count: number, data: any, status: number, response: Response) { // console.log(tag, // '\n请求接口: '+url, // '\n请求Token: '+token, // '\n请求时间: '+start_date.toLocaleString(), // '\n请求延迟: '+delay+' ms', // '\n请求次数: '+try_count, // '\n请求数据: '+JSON.stringify(data), // '\n请求状态: '+status, // '\n请求结果[Response]: '+JSON.stringify(response) // ); }