HttpRequest Library

HttpRequest for any type of request and HttpRequestHTML to request webpage. Supports caching of responses for a given period and paging.

当前为 2022-06-07 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/405144/1058527/HttpRequest%20Library.js

  1. // ==UserScript==
  2. // @name HttpRequest Library
  3. // @namespace hoehleg.userscripts.private
  4. // @version 0.3
  5. // @description HttpRequest for any type of request and HttpRequestHTML to request webpage. Supports caching of responses for a given period and paging.
  6. // @author Gerrit Höhle
  7. //
  8. // @grant GM_xmlhttpRequest
  9. //
  10. // ==/UserScript==
  11.  
  12. /* jslint esversion: 9 */
  13.  
  14. /**
  15. * @typedef HttpRequestHtmlResponse
  16. * @param {number} status
  17. * @param {HTMLDocument} html
  18. */
  19.  
  20. /**
  21. * @callback ResponseTransformer
  22. * @param {Response} response
  23. * @param {HttpRequestHtmlParams} httpRequestHtml
  24. * @return {any}
  25. */
  26.  
  27. /**
  28. * @callback ResponsePredicate
  29. * @param {Response} response
  30. * @param {HttpRequestHtmlParams} httpRequestHtml
  31. * @return {boolean} true if next page is available
  32. */
  33.  
  34. /**
  35. * @callback UrlPageNrConfigurator
  36. * @param {string} original - the original url and parameters given
  37. * @param {number} pageNr - the actual page
  38. * @returns {String} the new url to request the actual page
  39. */
  40.  
  41. /**
  42. * @callback ParamsPageNrConfigurator
  43. * @param {Object.<string, string>} original - the original url and parameters given
  44. * @param {number} pageNr - the actual page
  45. * @returns {Object.<string, string>} the new url to request the actual page
  46. */
  47.  
  48.  
  49. /**
  50. * @typedef {Object} HttpRequestHtmlParams
  51. * @property {string} url - url including path but without queryParams
  52. * @property {Object.<string, string|number>} [params] - queryParams
  53. * @property {number} [keepInCacheTimoutMs] - ms a request shall be cached
  54. * @property {number} [pageNr] - pageNr to start with
  55. * @property {number} [pagesMaxCount] - max number of pages to request
  56. * @property {ResponseTransformer} [resultTransformer] - transforms a response, typically from parsing the response.html - Document
  57. * @property {ResponsePredicate} [hasNextPage] - checks with the current response available, if a request for the next page shall be made
  58. * @property {UrlPageNrConfigurator} [urlConfiguratorForPageNr] - rewrites the 'url' string that the actual page with given number is requested
  59. * @property {ParamsPageNrConfigurator} [paramsConfiguratorForPageNr] - rewrites the params
  60. */
  61.  
  62. /**
  63. * @typedef {Object} HttpRequestHtml
  64. * @property {string} url
  65. * @property {Object.<string, string>} params
  66. * @property {number} keepInCacheTimoutMs
  67. * @property {number} pageNr
  68. * @property {number} pagesMaxCount - max number of pages to request
  69. * @property {ResponseTransformer} resultTransformer
  70. * @property {ResponsePredicate} hasNextPage
  71. * @property {UrlPageNrConfigurator} [urlConfiguratorForPageNr]
  72. * @property {ParamsPageNrConfigurator} [paramsConfiguratorForPageNr]
  73. */
  74.  
  75.  
  76. const HttpRequest = (() => {
  77.  
  78. const urlWithParams = (url, paramsObject) => {
  79. const params = Object.entries(paramsObject).map(([key, value]) => key + '=' + value).join('&');
  80. return params.length ? url + '?' + params : url;
  81. };
  82.  
  83. const responsesCache = new Map();
  84.  
  85. const requestKey = ({ method, url, params, data }) => `${method}:${urlWithParams(url, params)}:DATA:${data}`;
  86.  
  87. return class HttpRequest {
  88. constructor({
  89. method,
  90. url,
  91. headers = {},
  92. data = '',
  93. keepInCacheTimoutMs = 0,
  94. params = {}
  95. } = {}) {
  96.  
  97. /**
  98. * @type {HttpRequestHtml}
  99. * @public
  100. */
  101. const thisParams = { method, url, headers, data, params, keepInCacheTimoutMs };
  102. Object.assign(this, thisParams);
  103. }
  104.  
  105. async send() {
  106. if (!this.method || !this.url) {
  107. return await Promise.reject("invalid request");
  108. }
  109. return await new Promise((resolve, reject) => {
  110. let method, url, onload, onerror, headers, data;
  111. method = this.method.toUpperCase();
  112. url = this.url;
  113. headers = this.headers;
  114. data = this.data;
  115.  
  116. onload = (response) => {
  117. switch (response.status) {
  118. case 200:
  119. if (this.keepInCacheTimoutMs) {
  120. const key = requestKey(this);
  121. responsesCache.set(key, response);
  122.  
  123. if (this.keepInCacheTimoutMs > 0) {
  124. setTimeout(() => responsesCache.delete(key), this.keepInCacheTimoutMs);
  125. }
  126. }
  127. break;
  128. case 304:
  129. if (this.isCached()) {
  130. response = this.readFromCache();
  131. response.status = 304;
  132. }
  133. break;
  134. default:
  135. reject(`Status: ${response.status}, Error: ${response.statusText}`);
  136. return;
  137. }
  138. resolve(response);
  139. };
  140.  
  141. onerror = (errorEvent) => {
  142. reject("network error");
  143. };
  144.  
  145. switch (method) {
  146. case 'GET':
  147. if (this.params) {
  148. url = urlWithParams(url, this.params);
  149. }
  150. break;
  151. case 'POST':
  152. case 'PUT':
  153. headers = Object.assign({ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, headers || {});
  154. if (this.params) {
  155. data = JSON.stringify({ ...data, ...this.params });
  156. }
  157. break;
  158. }
  159. url = encodeURI(url);
  160. GM_xmlhttpRequest({ method, url, onload, onerror, headers, data });
  161. });
  162. }
  163.  
  164. isCached() {
  165. return responsesCache.has(requestKey(this));
  166. }
  167.  
  168. readFromCache() {
  169. return responsesCache.get(requestKey(this));
  170. }
  171.  
  172. static async send(...args) {
  173. return await new HttpRequest(...args).send();
  174. }
  175. };
  176. })();
  177.  
  178. class HttpRequestHtml extends HttpRequest {
  179.  
  180. /**
  181. * @param {HttpRequestHtmlParams} param0
  182. */
  183. constructor({
  184. url,
  185. params = {},
  186. keepInCacheTimoutMs = 0,
  187. pageNr = 0,
  188. pagesMaxCount = 1,
  189. resultTransformer = (resp, _httpRequestHtml) => resp,
  190. hasNextPage = (_resp, _httpRequestHtml) => false,
  191. urlConfiguratorForPageNr = (url, _pageNr) => url,
  192. paramsConfiguratorForPageNr = (params, _pageNr) => params,
  193. } = {}) {
  194. super({ method: 'GET', url, params, keepInCacheTimoutMs });
  195.  
  196. Object.assign(this, {
  197. pageNr,
  198. pagesMaxCount: Math.max(0, pagesMaxCount),
  199. resultTransformer,
  200. hasNextPage,
  201. urlConfiguratorForPageNr,
  202. paramsConfiguratorForPageNr,
  203. });
  204. }
  205.  
  206. clone() {
  207. return new HttpRequestHtml({ ...this });
  208. }
  209.  
  210. /**
  211. * @returns {Promise<HttpRequestHtmlResponse|object|Array<object>}
  212. */
  213. async send() {
  214. const results = [];
  215.  
  216. let response = null, requestForPage = null;
  217.  
  218. for (let pageNr = this.pageNr; pageNr < this.pageNr + this.pagesMaxCount; pageNr++) {
  219. if (requestForPage && !this.hasNextPage(response, requestForPage)) {
  220. break;
  221. }
  222.  
  223. requestForPage = Object.assign(this.clone(), {
  224. url: this.urlConfiguratorForPageNr(this.url, pageNr),
  225. params: this.paramsConfiguratorForPageNr({ ...this.params }, pageNr)
  226. });
  227.  
  228. response = await HttpRequest.prototype.send.call(requestForPage);
  229. if (response.status == 200 || response.status == 304) {
  230. response.html = new DOMParser().parseFromString(response.responseText, 'text/html');
  231. }
  232.  
  233. const resultForPage = this.resultTransformer(response, requestForPage);
  234. results.push(resultForPage);
  235. }
  236.  
  237. return this.pagesMaxCount > 1 ? results : results[0];
  238. }
  239.  
  240. /**
  241. * @param {HttpRequestHtmlParams} param0
  242. * @returns {Promise<HttpRequestHtmlResponse|object|Array<object>}
  243. */
  244. static async send(...args) {
  245. return await new HttpRequestHtml(...args).send();
  246. }
  247. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址