Http.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import { _decorator, Component, Enum, EventHandler } from 'cc';
  2. import { Log } from '../utils/Log';
  3. import { RC4 } from '../utils/RC4';
  4. import { HttpSystem, HostData, ErrorDataType } from './HttpSystem';
  5. import { HttpRequest } from './HttpRequest';
  6. const { ccclass, property, executionOrder } = _decorator;
  7. /**
  8. * Http请求方法
  9. */
  10. export enum HttpMethod {
  11. GET = "get",
  12. POST = "post"
  13. }
  14. /**
  15. * Http请求的数据类型
  16. */
  17. export enum ResponseType {
  18. Arraybuffer,
  19. Blob,
  20. Document,
  21. Json,
  22. Text
  23. }
  24. export enum HttpResponseCode {
  25. /**
  26. * 成功
  27. */
  28. Success,
  29. /**
  30. * 没有登录
  31. */
  32. NoLogin,
  33. /**
  34. * 未知错误
  35. */
  36. UnKnowError,
  37. /**
  38. * 密钥错误
  39. */
  40. KeyError
  41. }
  42. /**
  43. * Http组件
  44. * @description 建议使用@requireComponent(Http)、this.getComponent(Http)来配合使用Http组件
  45. * @description post模式暂时还不支持消息加密
  46. * @author 袁浩
  47. */
  48. @ccclass('Http')
  49. @executionOrder(-1)
  50. export class Http extends Component {
  51. @property({ tooltip: "HttpConfig中配置的服务端别名,不填则取HttpConfig中的默认值" })
  52. public hostAlias: string = "";
  53. @property({ tooltip: "请求是否应当带有授权信息,如cookie或授权header头" })
  54. public withCredentials: boolean = false;
  55. @property({ tooltip: "该请求的超时时间(毫秒),0为20秒", step: 1, min: 0 })
  56. public timeout: number = 0;
  57. @property({ type: Enum(ResponseType), tooltip: "返回的数据格式" })
  58. public responseType: ResponseType = ResponseType.Json;
  59. @property({ type: EventHandler, tooltip: "加载的进度回调函数" })
  60. public onProgress: EventHandler;
  61. @property({ tooltip: "消息加密" })
  62. public useEncrypt: boolean = true;
  63. @property({ tooltip: "是否打印消息" })
  64. public useLog: boolean = true;
  65. /**
  66. * 收到数据后的公共回调,返回值为是否执行后续调用
  67. */
  68. public static onData: (data: any) => boolean = () => { return true; }
  69. /**
  70. * 密钥错误
  71. */
  72. public static onKeyError: () => void = () => { }
  73. private get config(): HostData {
  74. return HttpSystem.getConfig(this.hostAlias);
  75. }
  76. public getRootURL(): string {
  77. var port = `:${this.config.port}`;
  78. if (this.config.SSL && this.config.port == 443 || !this.config.SSL && this.config.port == 80) {
  79. port = '';
  80. }
  81. return `${this.config.SSL ? "https://" : "http://"}${this.config.host}${port}`;
  82. }
  83. private headerObj: any;
  84. /**
  85. * 给指定的HTTP请求头赋值.在这之前,您必须确认已经调用 open() 方法打开了一个url.
  86. * @param header 将要被赋值的请求头名称.
  87. * @param value 给指定的请求头赋的值.
  88. */
  89. public setRequestHeader(header: string, value: string): void {
  90. if (!this.headerObj) {
  91. this.headerObj = {};
  92. }
  93. this.headerObj[header] = value;
  94. }
  95. /**
  96. * 所有的请求数据
  97. */
  98. protected requestMap: any = {};
  99. /**
  100. * 发送请求
  101. * @param path 路径
  102. * @param data 数据
  103. * @param method 方法
  104. * @returns
  105. */
  106. public async send(path: string, data: any = {}, method: HttpMethod = HttpMethod.GET, useEncrypt?: boolean, useTail = true) {
  107. let result = await this._send(path, data, method, useEncrypt, useTail);
  108. if (result && result.code == HttpResponseCode.KeyError) {
  109. HttpSystem.resetPublicKey();
  110. result = await this._send(path, data, method, useEncrypt, useTail);
  111. if (result && result.code == HttpResponseCode.KeyError) {//续签密钥失败
  112. Http.onKeyError();
  113. return { code: HttpResponseCode.KeyError, data: HttpSystem.getErrorDataType() != ErrorDataType.Null ? {} : null };
  114. }
  115. }
  116. return result;
  117. }
  118. private _send(path: string, data: any = {}, method: HttpMethod = HttpMethod.GET, useEncrypt?: boolean, useTail = true) {
  119. return new Promise<any>((resolve: (data: any) => void, reject) => {
  120. if (useEncrypt === undefined) {
  121. useEncrypt = this.useEncrypt;
  122. }
  123. var self = this;
  124. var request = new HttpRequest((url: string) => {
  125. if (!this.isValid) {
  126. return;
  127. }
  128. console.error(`http请求[${url}]失败`);
  129. delete self.requestMap[url];
  130. var errorDataType = HttpSystem.getErrorDataType();
  131. if (errorDataType == ErrorDataType.Throw) {
  132. reject();
  133. }
  134. else {
  135. resolve(errorDataType == ErrorDataType.Null ? null : {});
  136. }
  137. }, (httpRequest: HttpRequest) => {
  138. if (!this.isValid) {
  139. return;
  140. }
  141. var errorDataType = HttpSystem.getErrorDataType();
  142. var response = httpRequest.response;
  143. if (!response && errorDataType == ErrorDataType.Object) {
  144. response = {};
  145. }
  146. if (useEncrypt) {
  147. response = HttpSystem.decrypt(response);
  148. self.responseType == ResponseType.Json && (response = JSON.parse(response));
  149. }
  150. let url = httpRequest.url.substring(0, httpRequest.url.indexOf('?'));
  151. this.useLog && HttpSystem.useDebugLog && Log.info(`http请求[${url}]成功,数据为${self.responseType == ResponseType.Json ? JSON.stringify(response) : response}`);
  152. Http.onData(response) && resolve(response);
  153. delete self.requestMap[httpRequest.url];
  154. }, (loaded: number, total: number): void => {
  155. self.onProgress.emit([loaded, total]);
  156. });
  157. request.withCredentials = this.withCredentials;
  158. request.timeout = this.timeout || 20000;
  159. switch (this.responseType) {
  160. case ResponseType.Json:
  161. request.responseType = "json";
  162. break;
  163. case ResponseType.Arraybuffer:
  164. request.responseType = "arraybuffer";
  165. break;
  166. case ResponseType.Blob:
  167. request.responseType = "blob";
  168. break;
  169. case ResponseType.Document:
  170. request.responseType = "document";
  171. break;
  172. case ResponseType.Text:
  173. request.responseType = "text";
  174. break;
  175. default:
  176. request.responseType = "";
  177. break;
  178. }
  179. if (useEncrypt) {//如果消息加密
  180. data = data || {};
  181. data.timestamp = HttpSystem.serverTime;
  182. }
  183. let getData: string = typeof data == 'object' ? HttpSystem.dataToString(data) : data;
  184. let postData = method == HttpMethod.GET ? null : (typeof data == 'object' ? JSON.stringify(data) : data);
  185. let url: string = this.getRootURL() + (method == HttpMethod.GET ? path + "?" + getData : path);
  186. this.useLog && HttpSystem.useDebugLog && Log.info(`http请求[${url}]发送`);
  187. if (useEncrypt) {//如果消息加密
  188. if (method == HttpMethod.GET) {
  189. getData = HttpSystem.encrypt(getData, method);
  190. }
  191. else {
  192. postData = HttpSystem.encrypt(postData, method);
  193. }
  194. }
  195. useTail && (getData += HttpSystem.tailString);
  196. url = this.getRootURL() + (method == HttpMethod.GET ? path + "?" + getData : path);
  197. if (this.headerObj) {
  198. for (let key in this.headerObj) {
  199. request.setRequestHeader(key, this.headerObj[key]);
  200. }
  201. }
  202. self.requestMap[url] = request;
  203. request.open(url, method);
  204. request.send(postData);
  205. });
  206. }
  207. /**
  208. * 撤销http请求
  209. * 在onDestroy中记得调用,防止页面关闭了,请求还在执行导致产生一系列错误和内存泄漏(需要详细测试,可能并不需要如此)
  210. * @param path {string} http请求的地址
  211. * @param data {any} 请求所携带的数据
  212. */
  213. public cancel(path: string): void {
  214. for (var key in this.requestMap) {
  215. if (key.indexOf(this.getRootURL() + path) == 0) {
  216. var request: XMLHttpRequest = this.requestMap[key];
  217. request.abort();
  218. delete this.requestMap[key];
  219. }
  220. }
  221. }
  222. /**
  223. * 撤销所有http请求
  224. */
  225. public cancelAll(): void {
  226. for (var key in this.requestMap) {
  227. var request: XMLHttpRequest = this.requestMap[key];
  228. request.abort();
  229. }
  230. this.requestMap = {};
  231. }
  232. public onDestroy() {
  233. this.cancelAll();
  234. }
  235. }