Http.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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 dataToString(data: any): string {
  84. data = data || {};
  85. let result = "";
  86. for (let key in data) {
  87. let v = data[key];
  88. if (typeof data[key] == 'object') {
  89. v = JSON.stringify(data[key]);
  90. }
  91. result += encodeURI(key) + "=" + encodeURI(v) + "&";
  92. }
  93. return result;
  94. }
  95. private headerObj: any;
  96. /**
  97. * 给指定的HTTP请求头赋值.在这之前,您必须确认已经调用 open() 方法打开了一个url.
  98. * @param header 将要被赋值的请求头名称.
  99. * @param value 给指定的请求头赋的值.
  100. */
  101. public setRequestHeader(header: string, value: string): void {
  102. if (!this.headerObj) {
  103. this.headerObj = {};
  104. }
  105. this.headerObj[header] = value;
  106. }
  107. /**
  108. * 所有的请求数据
  109. */
  110. protected requestMap: any = {};
  111. /**
  112. * 发送请求
  113. * @param path 路径
  114. * @param data 数据
  115. * @param method 方法
  116. * @returns
  117. */
  118. public async send(path: string, data: any = {}, method: HttpMethod = HttpMethod.GET, useEncrypt?: boolean, useTail = true) {
  119. let result = await this._send(path, data, method, useEncrypt, useTail);
  120. if (result && result.code == HttpResponseCode.KeyError) {
  121. HttpSystem.resetPublicKey();
  122. result = await this._send(path, data, method, useEncrypt, useTail);
  123. if (result && result.code == HttpResponseCode.KeyError) {//续签密钥失败
  124. Http.onKeyError();
  125. return { code: HttpResponseCode.KeyError, data: HttpSystem.getErrorDataType() != ErrorDataType.Null ? {} : null };
  126. }
  127. }
  128. return result;
  129. }
  130. private _send(path: string, data: any = {}, method: HttpMethod = HttpMethod.GET, useEncrypt?: boolean, useTail = true) {
  131. return new Promise<any>((resolve: (data: any) => void, reject) => {
  132. if (useEncrypt === undefined) {
  133. useEncrypt = this.useEncrypt;
  134. }
  135. var self = this;
  136. var request = new HttpRequest((url: string) => {
  137. if (!this.isValid) {
  138. return;
  139. }
  140. console.error(`http请求[${url}]失败`);
  141. delete self.requestMap[url];
  142. var errorDataType = HttpSystem.getErrorDataType();
  143. if (errorDataType == ErrorDataType.Throw) {
  144. reject();
  145. }
  146. else {
  147. resolve(errorDataType == ErrorDataType.Null ? null : {});
  148. }
  149. }, (httpRequest: HttpRequest) => {
  150. if (!this.isValid) {
  151. return;
  152. }
  153. var errorDataType = HttpSystem.getErrorDataType();
  154. var response = httpRequest.response;
  155. if (!response && errorDataType == ErrorDataType.Object) {
  156. response = {};
  157. }
  158. if (useEncrypt) {
  159. response = this.decrypt(response);
  160. self.responseType == ResponseType.Json && (response = JSON.parse(response));
  161. }
  162. let url = httpRequest.url.substring(0, httpRequest.url.indexOf('?'));
  163. this.useLog && HttpSystem.useDebugLog && Log.info(`http请求[${url}]成功,数据为${self.responseType == ResponseType.Json ? JSON.stringify(response) : response}`);
  164. Http.onData(response) && resolve(response);
  165. delete self.requestMap[httpRequest.url];
  166. }, (loaded: number, total: number): void => {
  167. for (let i = 0; i < self.onProgress.length; i++) {
  168. self.onProgress[i].emit([loaded, total]);
  169. }
  170. });
  171. request.withCredentials = this.withCredentials;
  172. request.timeout = this.timeout || 20000;
  173. switch (this.responseType) {
  174. case ResponseType.Json:
  175. request.responseType = "json";
  176. break;
  177. case ResponseType.Arraybuffer:
  178. request.responseType = "arraybuffer";
  179. break;
  180. case ResponseType.Blob:
  181. request.responseType = "blob";
  182. break;
  183. case ResponseType.Document:
  184. request.responseType = "document";
  185. break;
  186. case ResponseType.Text:
  187. request.responseType = "text";
  188. break;
  189. default:
  190. request.responseType = "";
  191. break;
  192. }
  193. if (useEncrypt) {//如果消息加密
  194. data = data || {};
  195. data.timestamp = HttpSystem.serverTime;
  196. }
  197. let getData: string = typeof data == 'object' ? this.dataToString(data) : data;
  198. let postData = method == HttpMethod.GET ? null : (typeof data == 'object' ? JSON.stringify(data) : data);
  199. let url: string = this.getRootURL() + (method == HttpMethod.GET ? path + "?" + getData : path);
  200. this.useLog && HttpSystem.useDebugLog && Log.info(`http请求[${url}]发送`);
  201. if (useEncrypt) {//如果消息加密
  202. if (method == HttpMethod.GET) {
  203. getData = this.encrypt(getData, method);
  204. }
  205. else {
  206. postData = this.encrypt(postData, method);
  207. }
  208. }
  209. useTail && (getData += HttpSystem.tailString);
  210. url = this.getRootURL() + (method == HttpMethod.GET ? path + "?" + getData : path);
  211. if (this.headerObj) {
  212. for (let key in this.headerObj) {
  213. request.setRequestHeader(key, this.headerObj[key]);
  214. }
  215. }
  216. self.requestMap[url] = request;
  217. request.open(url, method);
  218. request.send(postData);
  219. });
  220. }
  221. private encrypt(data: string, method: HttpMethod) {
  222. data = encodeURIComponent(data);
  223. let privateKey = HttpSystem.getPrivateKey();
  224. data = encodeURIComponent(RC4.rc4(HttpSystem.publicKey + data, privateKey.toString()));
  225. if (method == HttpMethod.GET) {
  226. return "d=" + data + "&";
  227. }
  228. else {
  229. return JSON.stringify({ d: data });
  230. }
  231. }
  232. protected decrypt(data: any) {
  233. if (data) {
  234. data = decodeURIComponent(data.d);
  235. data = RC4.rc4(data, HttpSystem.publicKey);
  236. let keyIndex = data.indexOf(HttpSystem.publicKey);
  237. if (keyIndex != 0) {//密钥不正确
  238. return JSON.stringify({ code: HttpResponseCode.KeyError });
  239. }
  240. data = data.substr(HttpSystem.publicKey.length);
  241. }
  242. return data;
  243. }
  244. /**
  245. * 撤销http请求
  246. * 在onDestroy中记得调用,防止页面关闭了,请求还在执行导致产生一系列错误和内存泄漏(需要详细测试,可能并不需要如此)
  247. * @param path {string} http请求的地址
  248. * @param data {any} 请求所携带的数据
  249. */
  250. public cancel(path: string): void {
  251. for (var key in this.requestMap) {
  252. if (key.indexOf(this.getRootURL() + path) == 0) {
  253. var request: XMLHttpRequest = this.requestMap[key];
  254. request.abort();
  255. delete this.requestMap[key];
  256. }
  257. }
  258. }
  259. /**
  260. * 撤销所有http请求
  261. */
  262. public cancelAll(): void {
  263. for (var key in this.requestMap) {
  264. var request: XMLHttpRequest = this.requestMap[key];
  265. request.abort();
  266. }
  267. this.requestMap = {};
  268. }
  269. public onDestroy() {
  270. this.cancelAll();
  271. }
  272. }