HttpHelper.ets 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. import { ArrayList, HashMap } from '@kit.ArkTS';
  2. import http from '@ohos.net.http';
  3. import { LogHelper } from '../../../../Index';
  4. import { SZYXLocalStorageHelper } from '../utils/SZYXLocalStorageHelper';
  5. import { SZYXLocalStorageKeys } from '../utils/SZYXLocalStorageKeys';
  6. import { HttpHelperX, HttpParamsForm, HttpParamsGet, HttpParamsPost, HttpParamsUpload } from './HttpHelperX';
  7. import { BusinessError } from '@kit.BasicServicesKit';
  8. import { image } from '@kit.ImageKit';
  9. type AnyResult = object | string | number | boolean | null | undefined;
  10. interface Waiters {
  11. resolve: (v: AnyResult) => void;
  12. reject: (e: Error) => void;
  13. }
  14. interface PendingRequest {
  15. http?: http.HttpRequest
  16. waiters: Array<Waiters>
  17. seq: number
  18. }
  19. export class HttpHelper {
  20. private static instance: HttpHelper | null = null
  21. // 单例模式
  22. static get() {
  23. // 判断系统是否已经有单例了
  24. if (HttpHelper.instance === null) {
  25. HttpHelper.instance = new HttpHelper()
  26. }
  27. return HttpHelper.instance
  28. }
  29. //请求中队列
  30. private httpHandlerList = new HashMap<string, http.HttpRequest>();
  31. // 并发白名单,这个名单里面的api,重复请求不会取消
  32. private concurrentList = new ArrayList<string>();
  33. private pendingMap = new HashMap<string, PendingRequest>();
  34. private seqGen = 0;
  35. constructor() {
  36. SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
  37. SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength, this.httpHandlerList.length)
  38. try {
  39. this.concurrentList.clear()
  40. } catch (error) {
  41. }
  42. }
  43. /**
  44. * 添加并发白名单
  45. * @param apiNo
  46. */
  47. public addConcurrent(apiNo?: string) {
  48. if (!apiNo) {
  49. return
  50. }
  51. try {
  52. if (this.concurrentList.getIndexOf(apiNo) === -1) {
  53. this.concurrentList.add(apiNo)
  54. }
  55. } catch (error) {
  56. }
  57. }
  58. public addConcurrents(apiNo: string[]) {
  59. for (let apiNoElement of apiNo) {
  60. try {
  61. if (this.concurrentList.getIndexOf(apiNoElement) === -1) {
  62. this.concurrentList.add(apiNoElement)
  63. }
  64. } catch (error) {
  65. }
  66. }
  67. }
  68. public removeConcurrent(apiNo: string) {
  69. try {
  70. if (this.concurrentList.getIndexOf(apiNo) !== -1) {
  71. this.concurrentList.remove(apiNo)
  72. }
  73. } catch (error) {
  74. }
  75. }
  76. public removeConcurrents(apiNo: string[]) {
  77. for (let apiNoElement of apiNo) {
  78. try {
  79. if (this.concurrentList.getIndexOf(apiNoElement) !== -1) {
  80. this.concurrentList.remove(apiNoElement)
  81. }
  82. } catch (error) {
  83. }
  84. }
  85. }
  86. /**
  87. * postJson请求
  88. * @param url url地址
  89. * @param headers
  90. * @param apiNo 请求标识,取消请求或者去重使用|考虑做自动重试使用
  91. * @returns
  92. */
  93. public postJson<T>(params: HttpParamsPost, apiNo?: string, showLog?: boolean): Promise<T> {
  94. return new Promise<T>((resolve, reject) => {
  95. let httpRequest = http.createHttp();
  96. const key = apiNo ?? params.url
  97. const pending = this.setHandler(key, httpRequest, {
  98. resolve: (v: AnyResult) => resolve(v as T),
  99. reject: (e: Error) => reject(e)
  100. })
  101. const header = HttpHelperX.getHeaders("application/json;charset=UTF-8", params.headers)
  102. if (showLog) {
  103. LogHelper.debug(`postJson:${apiNo}\n`, JSON.stringify(params))
  104. }
  105. httpRequest.request(HttpHelperX.getUrl(params.url, params.query), {
  106. method: http.RequestMethod.POST,
  107. connectTimeout: 60000,
  108. readTimeout: 60000,
  109. header: header,
  110. extraData: params.data,
  111. usingCache: false,
  112. }).then((data: http.HttpResponse) => {
  113. const latest = this.pendingMap.get(key)
  114. if (showLog && (!pending || (latest && latest.seq === pending.seq))) {
  115. LogHelper.debug(`${apiNo}:\n ${data.result as string}`)
  116. LogHelper.print(data)
  117. }
  118. if (data.responseCode === 200) {
  119. const result = (typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T
  120. if (!pending) {
  121. resolve(result)
  122. } else {
  123. if (!latest || latest.seq !== pending.seq) {
  124. return
  125. }
  126. const result1 = result as AnyResult
  127. latest.waiters.forEach(w => w.resolve(result1))
  128. }
  129. } else {
  130. const err: Error = new Error()
  131. err.name = data.responseCode.toString()
  132. err.message = '服务异常'
  133. if (!pending) {
  134. reject(err)
  135. } else {
  136. if (!latest || latest.seq !== pending.seq) {
  137. return
  138. }
  139. latest.waiters.forEach(w => w.reject(err))
  140. }
  141. }
  142. }).catch((err: Error) => {
  143. let e = err
  144. if (err.message === 'Failed writing received data to disk/application') {
  145. e.name = 'cancel'
  146. e.message = err.message
  147. }
  148. if (!pending) {
  149. LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
  150. reject(e)
  151. } else {
  152. const latest = this.pendingMap.get(key)
  153. if (!latest || latest.seq !== pending.seq) {
  154. return
  155. }
  156. LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
  157. latest.waiters.forEach(w => w.reject(e))
  158. }
  159. }).finally(() => {
  160. if (pending) {
  161. const latest = this.pendingMap.get(key)
  162. if (latest && latest.seq === pending.seq) {
  163. this.pendingMap.remove(key)
  164. this.httpHandlerList.remove(key)
  165. }
  166. } else {
  167. this.httpHandlerList.remove(key)
  168. }
  169. SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
  170. SZYXLocalStorageHelper.storage.setOrCreate(
  171. SZYXLocalStorageKeys.HttpHandlerListLength,
  172. this.httpHandlerList.length
  173. )
  174. })
  175. })
  176. }
  177. setHandler(key: string, httpRequest: http.HttpRequest, waiters: Waiters): PendingRequest | null {
  178. // 白名单:完全绕过
  179. if (this.concurrentList.getIndexOf(key) !== -1) {
  180. return null
  181. }
  182. if (this.seqGen > 99999) {
  183. this.seqGen = 0
  184. }
  185. const seq = ++this.seqGen
  186. let pending = this.pendingMap.get(key)
  187. if (!pending) {
  188. pending = {
  189. waiters: [waiters],
  190. seq,
  191. http: httpRequest
  192. }
  193. this.pendingMap.set(key, pending)
  194. } else {
  195. // cancel 旧请求
  196. const h = pending.http
  197. pending = {
  198. waiters: [...pending.waiters, waiters],
  199. seq,
  200. http: httpRequest
  201. }
  202. this.pendingMap.set(key, pending)
  203. h?.destroy()
  204. }
  205. this.httpHandlerList.set(key, httpRequest)
  206. SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
  207. SZYXLocalStorageHelper.storage.setOrCreate(
  208. SZYXLocalStorageKeys.HttpHandlerListLength,
  209. this.httpHandlerList.length
  210. )
  211. return pending
  212. }
  213. /**
  214. * postForm请求
  215. * @param url url地址
  216. * @param headers
  217. * @param apiNo 请求标识,取消请求或者去重使用|考虑做自动重试使用
  218. * @returns
  219. */
  220. public postForm<T>(params: HttpParamsForm, apiNo?: string, showLog?: boolean): Promise<T> {
  221. return new Promise<T>((resolve, reject) => {
  222. let httpRequest = http.createHttp();
  223. const key = apiNo ?? params.url
  224. const pending = this.setHandler(key, httpRequest, {
  225. resolve: (v: AnyResult) => resolve(v as T),
  226. reject: (e: Error) => reject(e)
  227. })
  228. const header = HttpHelperX.getHeaders("application/x-www-form-urlencoded;charset=UTF-8", params.headers)
  229. let data = HttpHelperX.getContent(params.data)
  230. if (showLog) {
  231. LogHelper.debug(`postForm:${apiNo}\n`, JSON.stringify(params))
  232. }
  233. httpRequest.request(HttpHelperX.getUrl(params.url, params.query), {
  234. method: http.RequestMethod.POST,
  235. connectTimeout: 20000,
  236. readTimeout: 20000,
  237. header: header,
  238. usingCache: false,
  239. extraData: data ? encodeURI(data) : undefined
  240. })
  241. .then((data: http.HttpResponse) => {
  242. const latest = this.pendingMap.get(key)
  243. if (showLog && (!pending || (latest && latest.seq === pending.seq))) {
  244. LogHelper.debug(`${apiNo}:\n ${data.result as string}`)
  245. LogHelper.print(data)
  246. }
  247. if (data.responseCode === 200) {
  248. const result = (typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T
  249. if (!pending) {
  250. resolve(result)
  251. } else {
  252. if (!latest || latest.seq !== pending.seq) {
  253. return
  254. }
  255. const result1 = result as AnyResult
  256. latest.waiters.forEach(w => w.resolve(result1))
  257. }
  258. } else {
  259. const err: Error = new Error()
  260. err.name = data.responseCode.toString()
  261. err.message = '服务异常'
  262. if (!pending) {
  263. reject(err)
  264. } else {
  265. if (!latest || latest.seq !== pending.seq) {
  266. return
  267. }
  268. latest.waiters.forEach(w => w.reject(err))
  269. }
  270. }
  271. }).catch((err: Error) => {
  272. let e = err
  273. if (err.message === 'Failed writing received data to disk/application') {
  274. e.name = 'cancel'
  275. e.message = err.message
  276. }
  277. if (!pending) {
  278. LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
  279. reject(e)
  280. } else {
  281. const latest = this.pendingMap.get(key)
  282. if (!latest || latest.seq !== pending.seq) {
  283. return
  284. }
  285. LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
  286. latest.waiters.forEach(w => w.reject(e))
  287. }
  288. }).finally(() => {
  289. if (pending) {
  290. const latest = this.pendingMap.get(key)
  291. if (latest && latest.seq === pending.seq) {
  292. this.pendingMap.remove(key)
  293. this.httpHandlerList.remove(key)
  294. }
  295. } else {
  296. this.httpHandlerList.remove(key)
  297. }
  298. SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
  299. SZYXLocalStorageHelper.storage.setOrCreate(
  300. SZYXLocalStorageKeys.HttpHandlerListLength,
  301. this.httpHandlerList.length
  302. )
  303. });
  304. });
  305. }
  306. /**
  307. * get请求
  308. * @param url url地址
  309. * @param data 请求参数
  310. * @param headers
  311. * @param apiNo 请求标识,取消请求或者去重使用|考虑做自动重试使用
  312. * @returns
  313. */
  314. public get<T>(params: HttpParamsGet, apiNo?: string, showLog?: boolean): Promise<T> {
  315. return new Promise<T>((resolve, reject) => {
  316. let httpRequest = http.createHttp();
  317. const key = apiNo ?? params.url
  318. const pending = this.setHandler(key, httpRequest, {
  319. resolve: (v: AnyResult) => resolve(v as T),
  320. reject: (e: Error) => reject(e)
  321. })
  322. if (showLog) {
  323. LogHelper.debug(`GET:${apiNo}\n`, HttpHelperX.getUrl(params.url, params.query) + '\n',
  324. JSON.stringify(params.headers))
  325. }
  326. httpRequest.request(HttpHelperX.getUrl(params.url, params.query), {
  327. method: http.RequestMethod.GET,
  328. connectTimeout: 20000,
  329. readTimeout: 20000,
  330. header: params.headers,
  331. usingCache: false,
  332. // extraData: params.data
  333. })
  334. .then((data: http.HttpResponse) => {
  335. const latest = this.pendingMap.get(key)
  336. if (showLog && (!pending || (latest && latest.seq === pending.seq))) {
  337. LogHelper.debug(`${apiNo}:\n ${data.result as string}`)
  338. LogHelper.print(data)
  339. }
  340. if (data.responseCode === 200) {
  341. const result = (typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T
  342. if (!pending) {
  343. resolve(result)
  344. } else {
  345. if (!latest || latest.seq !== pending.seq) {
  346. return
  347. }
  348. const result1 = result as AnyResult
  349. latest.waiters.forEach(w => w.resolve(result1))
  350. }
  351. } else {
  352. const err: Error = new Error()
  353. err.name = data.responseCode.toString()
  354. err.message = '服务异常'
  355. if (!pending) {
  356. reject(err)
  357. } else {
  358. if (!latest || latest.seq !== pending.seq) {
  359. return
  360. }
  361. latest.waiters.forEach(w => w.reject(err))
  362. }
  363. }
  364. }).catch((err: Error) => {
  365. let e = err
  366. if (err.message === 'Failed writing received data to disk/application') {
  367. e.name = 'cancel'
  368. e.message = err.message
  369. }
  370. if (!pending) {
  371. LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
  372. reject(e)
  373. } else {
  374. const latest = this.pendingMap.get(key)
  375. if (!latest || latest.seq !== pending.seq) {
  376. return
  377. }
  378. LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
  379. latest.waiters.forEach(w => w.reject(e))
  380. }
  381. }).finally(() => {
  382. if (pending) {
  383. const latest = this.pendingMap.get(key)
  384. if (latest && latest.seq === pending.seq) {
  385. this.pendingMap.remove(key)
  386. this.httpHandlerList.remove(key)
  387. }
  388. } else {
  389. this.httpHandlerList.remove(key)
  390. }
  391. SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
  392. SZYXLocalStorageHelper.storage.setOrCreate(
  393. SZYXLocalStorageKeys.HttpHandlerListLength,
  394. this.httpHandlerList.length
  395. )
  396. });
  397. });
  398. }
  399. /**
  400. * postJson请求
  401. * @param url url地址
  402. * @param headers
  403. * @param apiNo 请求标识,取消请求或者去重使用|考虑做自动重试使用
  404. * @returns
  405. */
  406. public upload<T>(params: HttpParamsUpload, apiNo?: string, showLog?: boolean): Promise<T> {
  407. return new Promise<T>((resolve, reject) => {
  408. let httpRequest = http.createHttp();
  409. const key = apiNo ?? params.url
  410. const pending = this.setHandler(key, httpRequest, {
  411. resolve: (v: AnyResult) => resolve(v as T),
  412. reject: (e: Error) => reject(e)
  413. })
  414. if (showLog) {
  415. LogHelper.debug(`postJson:${apiNo}\n`, JSON.stringify(params))
  416. }
  417. httpRequest.on("dataSendProgress", (data: http.DataSendProgressInfo) => {
  418. if (showLog) {
  419. LogHelper.debug(`${apiNo}:\n${JSON.stringify(data)}`)
  420. }
  421. params.onProgress && params.onProgress(data.sendSize, data.totalSize)
  422. });
  423. httpRequest.request(HttpHelperX.getUrl(params.url, params.query), {
  424. method: http.RequestMethod.POST,
  425. connectTimeout: 20000,
  426. readTimeout: 20000,
  427. header: HttpHelperX.getHeaders("multipart/form-data", params.headers),
  428. usingCache: false,
  429. multiFormDataList: params.data,
  430. })
  431. .then((data: http.HttpResponse) => {
  432. const latest = this.pendingMap.get(key)
  433. if (showLog && (!pending || (latest && latest.seq === pending.seq))) {
  434. LogHelper.debug(`${apiNo}:\n ${data.result as string}`)
  435. LogHelper.print(data)
  436. }
  437. if (data.responseCode === 200) {
  438. const result = (typeof data.result === 'string' ? JSON.parse(data.result) : data.result) as T
  439. if (!pending) {
  440. resolve(result)
  441. } else {
  442. if (!latest || latest.seq !== pending.seq) {
  443. return
  444. }
  445. const result1 = result as AnyResult
  446. latest.waiters.forEach(w => w.resolve(result1))
  447. }
  448. } else {
  449. const err: Error = new Error()
  450. err.name = data.responseCode.toString()
  451. err.message = '服务异常'
  452. if (!pending) {
  453. reject(err)
  454. } else {
  455. if (!latest || latest.seq !== pending.seq) {
  456. return
  457. }
  458. latest.waiters.forEach(w => w.reject(err))
  459. }
  460. }
  461. }).catch((err: Error) => {
  462. let e = err
  463. if (err.message === 'Failed writing received data to disk/application') {
  464. e.name = 'cancel'
  465. e.message = err.message
  466. }
  467. if (!pending) {
  468. LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
  469. reject(e)
  470. } else {
  471. const latest = this.pendingMap.get(key)
  472. if (!latest || latest.seq !== pending.seq) {
  473. return
  474. }
  475. LogHelper.error(JSON.stringify({ err: err, url: params.url, }))
  476. latest.waiters.forEach(w => w.reject(e))
  477. }
  478. }).finally(() => {
  479. if (pending) {
  480. const latest = this.pendingMap.get(key)
  481. if (latest && latest.seq === pending.seq) {
  482. this.pendingMap.remove(key)
  483. this.httpHandlerList.remove(key)
  484. }
  485. } else {
  486. this.httpHandlerList.remove(key)
  487. }
  488. SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
  489. SZYXLocalStorageHelper.storage.setOrCreate(
  490. SZYXLocalStorageKeys.HttpHandlerListLength,
  491. this.httpHandlerList.length
  492. )
  493. });
  494. });
  495. }
  496. clearHttp() {
  497. for (let handler of this.httpHandlerList.keys()) {
  498. this.cancel(handler)
  499. }
  500. }
  501. cancel(apiNo: string) {
  502. try {
  503. if (this.httpHandlerList.hasKey(apiNo)) {
  504. this.httpHandlerList.get(apiNo).destroy()
  505. this.httpHandlerList.remove(apiNo)
  506. SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerList, this.httpHandlerList)
  507. SZYXLocalStorageHelper.storage.setOrCreate(SZYXLocalStorageKeys.HttpHandlerListLength,
  508. this.httpHandlerList.length)
  509. }
  510. } catch (error) {
  511. }
  512. }
  513. downloadImage(url: string): Promise<image.ImageSource> {
  514. return new Promise<image.ImageSource>((resolve, reject) => {
  515. http.createHttp()
  516. .request(url, (error: BusinessError, data: http.HttpResponse) => {
  517. if (error) {
  518. reject(error)
  519. return
  520. }
  521. if (data.result instanceof ArrayBuffer) {
  522. let imageData: ArrayBuffer = data.result as ArrayBuffer;
  523. let imageSource: image.ImageSource = image.createImageSource(imageData);
  524. resolve(imageSource)
  525. } else {
  526. reject(new Error('下载图片失败'))
  527. }
  528. })
  529. })
  530. }
  531. }