From ffaee7c79fa6d7f69da673866472a9d9f2ecfa9b Mon Sep 17 00:00:00 2001 From: Arios Date: Fri, 25 Feb 2022 22:48:43 +0800 Subject: [PATCH] Added new interceptor and return with response data --- src/index.js | 401 +++++++++++++++++++++++++-------------------- src/interceptor.js | 35 ++++ 2 files changed, 261 insertions(+), 175 deletions(-) create mode 100644 src/interceptor.js diff --git a/src/index.js b/src/index.js index 3f565b8..05fb6c8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,5 @@ +import Interceptor from './interceptor'; + /** * Copyright 2018 Google Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -66,179 +68,228 @@ /** */ export default (function create(/** @type {Options} */ defaults) { - defaults = defaults || {}; - - /** - * @public - * @template T - * @type {((config?: Options) => Promise>) | ((url: string, config?: Options) => Promise>)} - */ - redaxios.request = redaxios; - - /** @public @type {BodylessMethod} */ - redaxios.get = (url, config) => redaxios(url, config, 'get'); - - /** @public @type {BodylessMethod} */ - redaxios.delete = (url, config) => redaxios(url, config, 'delete'); - - /** @public @type {BodylessMethod} */ - redaxios.head = (url, config) => redaxios(url, config, 'head'); - - /** @public @type {BodylessMethod} */ - redaxios.options = (url, config) => redaxios(url, config, 'options'); - - /** @public @type {BodyMethod} */ - redaxios.post = (url, data, config) => redaxios(url, config, 'post', data); - - /** @public @type {BodyMethod} */ - redaxios.put = (url, data, config) => redaxios(url, config, 'put', data); - - /** @public @type {BodyMethod} */ - redaxios.patch = (url, data, config) => redaxios(url, config, 'patch', data); - - /** @public */ - redaxios.all = Promise.all.bind(Promise); - - /** - * @public - * @template Args, R - * @param {(...args: Args[]) => R} fn - * @returns {(array: Args[]) => R} - */ - redaxios.spread = function (fn) { - return function (results) { - return fn.apply(this, results); - }; - }; - // 3b smaller: - // redaxios.spread = (fn) => /** @type {any} */ (fn.apply.bind(fn, fn)); - - /** - * @private - * @param {Record} opts - * @param {Record} [overrides] - * @param {boolean} [lowerCase] - * @returns {Partial} - */ - function deepMerge(opts, overrides, lowerCase) { - let out = {}, - i; - if (Array.isArray(opts)) { - return opts.concat(overrides); - } - for (i in opts) { - const key = lowerCase ? i.toLowerCase() : i; - out[key] = opts[i]; - } - for (i in overrides) { - const key = lowerCase ? i.toLowerCase() : i; - const value = /** @type {any} */ (overrides)[i]; - out[key] = key in out && typeof value == 'object' ? deepMerge(out[key], value, key === 'headers') : value; - } - return out; - } - - /** - * Issues a request. - * @public - * @template T - * @param {string | Options} url - * @param {Options} [config] - * @param {any} [_method] - * @param {any} [_data] - * @returns {Promise>} - */ - function redaxios(url, config, _method, _data) { - if (typeof url !== 'string') { - config = url; - url = config.url; - } - - const response = /** @type {Response} */ ({ config }); - - /** @type {Options} */ - const options = deepMerge(defaults, config); - - /** @type {Headers} */ - const customHeaders = {}; - - let data = _data || options.data; - - (options.transformRequest || []).map((f) => { - data = f(data, options.headers) || data; - }); - - if (data && typeof data === 'object' && typeof data.append !== 'function') { - data = JSON.stringify(data); - customHeaders['content-type'] = 'application/json'; - } - - const m = - typeof document !== 'undefined' && document.cookie.match(RegExp('(^|; )' + options.xsrfCookieName + '=([^;]*)')); - if (m) customHeaders[options.xsrfHeaderName] = decodeURIComponent(m[2]); - - if (options.auth) { - customHeaders.authorization = options.auth; - } - - if (options.baseURL) { - url = url.replace(/^(?!.*\/\/)\/?(.*)$/, options.baseURL + '/$1'); - } - - if (options.params) { - const divider = ~url.indexOf('?') ? '&' : '?'; - const query = options.paramsSerializer - ? options.paramsSerializer(options.params) - : new URLSearchParams(options.params); - url += divider + query; - } - - const fetchFunc = options.fetch || fetch; - - return fetchFunc(url, { - method: _method || options.method, - body: data, - headers: deepMerge(options.headers, customHeaders, true), - credentials: options.withCredentials ? 'include' : 'same-origin' - }).then((res) => { - for (const i in res) { - if (typeof res[i] != 'function') response[i] = res[i]; - } - - const ok = options.validateStatus ? options.validateStatus(res.status) : res.ok; - - if (options.responseType == 'stream') { - response.data = res.body; - return response; - } - - return res[options.responseType || 'text']() - .then((data) => { - response.data = data; - // its okay if this fails: response.data will be the unparsed value: - response.data = JSON.parse(data); - }) - .catch(Object) - .then(() => (ok ? response : Promise.reject(response))); - }); - } - - /** - * @public - * @type {AbortController} - */ - redaxios.CancelToken = /** @type {any} */ (typeof AbortController == 'function' ? AbortController : Object); - - /** - * @public - * @type {Options} - */ - redaxios.defaults = defaults; - - /** - * @public - */ - redaxios.create = create; - - return redaxios; + defaults = defaults || {}; + + /** + * @public + * @template T + * @type {((config?: Options) => Promise>) | ((url: string, config?: Options) => Promise>)} + */ + redaxios.request = redaxios; + + /** @public @type {BodylessMethod} */ + redaxios.get = (url, config) => redaxios(url, config, 'get'); + + /** @public @type {BodylessMethod} */ + redaxios.delete = (url, config) => redaxios(url, config, 'delete'); + + /** @public @type {BodylessMethod} */ + redaxios.head = (url, config) => redaxios(url, config, 'head'); + + /** @public @type {BodylessMethod} */ + redaxios.options = (url, config) => redaxios(url, config, 'options'); + + /** @public @type {BodyMethod} */ + redaxios.post = (url, data, config) => redaxios(url, config, 'post', data); + + /** @public @type {BodyMethod} */ + redaxios.put = (url, data, config) => redaxios(url, config, 'put', data); + + /** @public @type {BodyMethod} */ + redaxios.patch = (url, data, config) => redaxios(url, config, 'patch', data); + + /** @public */ + redaxios.all = Promise.all.bind(Promise); + + /** + * @public + * @template Args, R + * @param {(...args: Args[]) => R} fn + * @returns {(array: Args[]) => R} + */ + redaxios.spread = function (fn) { + return function (results) { + return fn.apply(this, results); + }; + }; + + redaxios.interceptors = { + request: new Interceptor(), + response: new Interceptor() + }; + // 3b smaller: + // redaxios.spread = (fn) => /** @type {any} */ (fn.apply.bind(fn, fn)); + + /** + * @private + * @param {Record} opts + * @param {Record} [overrides] + * @param {boolean} [lowerCase] + * @returns {Partial} + */ + function deepMerge(opts, overrides, lowerCase) { + let out = {}, + i; + if (Array.isArray(opts)) { + return opts.concat(overrides); + } + for (i in opts) { + const key = lowerCase ? i.toLowerCase() : i; + out[key] = opts[i]; + } + for (i in overrides) { + const key = lowerCase ? i.toLowerCase() : i; + const value = /** @type {any} */ (overrides)[i]; + out[key] = key in out && typeof value == 'object' ? deepMerge(out[key], value, key === 'headers') : value; + } + return out; + } + + /** + * Issues a request. + * @public + * @template T + * @param {string | Options} url + * @param {Options} [config] + * @param {any} [_method] + * @param {any} [_data] + * @returns {Promise>} + */ + function redaxios(url, config, _method, _data) { + if (typeof url !== 'string') { + config = url; + url = config.url; + } + + let response = /** @type {Response} */ ({ config }); + + /** @type {Options} */ + // pre-request interception + let options = deepMerge(defaults, config || {}); + if (redaxios.interceptors.request.handlers.length > 0) { + redaxios.interceptors.request.handlers.forEach(handler => { + if (handler !== null) { + const resultConfig = handler.done(config); + options = deepMerge(options, resultConfig || {}); + } + }); + } + + /** @type {Headers} */ + const customHeaders = {}; + + let data = _data || options.data; + + (options.transformRequest || []).map((f) => { + data = f(data, options.headers) || data; + }); + + if (data && typeof data === 'object' && typeof data.append !== 'function') { + data = JSON.stringify(data); + customHeaders['content-type'] = 'application/json'; + } + + const m = + typeof document !== 'undefined' && document.cookie.match(RegExp('(^|; )' + options.xsrfCookieName + '=([^;]*)')); + if (m) customHeaders[options.xsrfHeaderName] = decodeURIComponent(m[2]); + + if (options.auth) { + customHeaders.authorization = options.auth; + } + + if (options.baseURL) { + url = url.replace(/^(?!.*\/\/)\/?(.*)$/, options.baseURL + '/$1'); + } + + if (options.params) { + const divider = ~url.indexOf('?') ? '&' : '?'; + const query = options.paramsSerializer + ? options.paramsSerializer(options.params) + : new URLSearchParams(options.params); + url += divider + query; + } + + const fetchFunc = options.fetch || fetch; + + return fetchFunc(url, { + method: _method || options.method, + body: data, + headers: deepMerge(options.headers, customHeaders, true), + credentials: options.withCredentials ? 'include' : 'same-origin' + }).then((res) => { + for (const i in res) { + if (typeof res[i] != 'function') response[i] = res[i]; + } + + if (!(res.status >= 200 && res.status < 300) + && redaxios.interceptors.response.handlers.length > 0) { + redaxios.interceptors.response.handlers.forEach(handler => { + if (handler && handler.error) + handler.error(res); + }); + } + + const ok = options.validateStatus ? options.validateStatus(res.status) : res.ok; + + if (options.responseType == 'stream') { + response.data = res.body; + return response; + } + + return res[options.responseType || 'text']() + .then((data) => { + response.data = data; + // its okay if this fails: response.data will be the unparsed value: + response.data = JSON.parse(data); + }) + .catch(() => response) + .then(() => { + if (res.status >= 200 && res.status < 300 + && redaxios.interceptors.response.handlers.length > 0) { + redaxios.interceptors.response.handlers.forEach(handler => { + if (handler !== null) { + console.log(handler) + const interceptedResponse = handler.done(response); + + if (interceptedResponse) + response = interceptedResponse; + } + }); + + } else { + redaxios.interceptors.response.handlers.forEach(handler => { + if (handler !== null) { + console.log(handler) + + const interceptedResponse = handler.error(response); + + if (interceptedResponse) + response = interceptedResponse; + } + }); + } + return response + }); + + }); + } + + /** + * @public + * @type {AbortController} + */ + redaxios.CancelToken = /** @type {any} */ (typeof AbortController == 'function' ? AbortController : Object); + + /** + * @public + * @type {Options} + */ + redaxios.defaults = defaults; + + /** + * @public + */ + redaxios.create = create; + + return redaxios; })(); diff --git a/src/interceptor.js b/src/interceptor.js new file mode 100644 index 0000000..be4880e --- /dev/null +++ b/src/interceptor.js @@ -0,0 +1,35 @@ +function Interceptor() { + + /** + * @type {Array<{done: Function, error: Function }>} + */ + this.handlers = []; + + /** + * Register an interceptor + * @param {Function} done + * @param {Function} [error] + * @returns {number} The interceptor Id to be used for ejection + */ + this.use = function(done, error) { + this.handlers.push({ + done, + error: error || (() => {}) + }); + + + return this.handlers.length - 1; + }; + + + + /** + * @param {number} id - A registered interceptor Id + */ + this.eject = function (id) { + if (this.handlers[id]) + this.handlers[id] = null; + }; +} + +export default Interceptor; \ No newline at end of file