@scx-js/scx-http 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/FetchError.js ADDED
@@ -0,0 +1,13 @@
1
+ class FetchError {
2
+
3
+ cause;
4
+
5
+ constructor(error) {
6
+ this.cause = error;
7
+ }
8
+
9
+ }
10
+
11
+ export {
12
+ FetchError,
13
+ };
@@ -0,0 +1,7 @@
1
+ class HttpFieldName {
2
+ static CONTENT_TYPE = "Content-Type";
3
+ }
4
+
5
+ export {
6
+ HttpFieldName,
7
+ };
package/HttpMethod.js ADDED
@@ -0,0 +1,10 @@
1
+ class HttpMethod {
2
+ static GET = "GET";
3
+ static POST = "POST";
4
+ static PUT = "PUT";
5
+ static DELETE = "DELETE";
6
+ }
7
+
8
+ export {
9
+ HttpMethod,
10
+ };
package/JsonVOError.js ADDED
@@ -0,0 +1,11 @@
1
+ class JsonVOError {
2
+
3
+ constructor(error) {
4
+ Object.assign(this, error);
5
+ }
6
+
7
+ }
8
+
9
+ export {
10
+ JsonVOError,
11
+ };
package/MediaType.js ADDED
@@ -0,0 +1,10 @@
1
+ class MediaType {
2
+ static APPLICATION_JSON = "application/json";
3
+ static APPLICATION_XML = "application/xml";
4
+ static MULTIPART_FORM_DATA = "multipart/form-data";
5
+ static TEXT_ANY = "text/";
6
+ }
7
+
8
+ export {
9
+ MediaType,
10
+ };
@@ -0,0 +1,13 @@
1
+ class ResponseNotOKError {
2
+
3
+ cause;
4
+
5
+ constructor(error) {
6
+ this.cause = error;
7
+ }
8
+
9
+ }
10
+
11
+ export {
12
+ ResponseNotOKError,
13
+ };
package/ScxFetch.js ADDED
@@ -0,0 +1,178 @@
1
+ import {notNull} from "@scx-js/scx-common";
2
+ import {inject} from "vue";
3
+ import {
4
+ createRequestInit,
5
+ initDefaultOptions,
6
+ mixinOptions,
7
+ setMethod,
8
+ setRequestBody,
9
+ setRequestHeaders,
10
+ } from "./ScxFetchHelper.js";
11
+ import {ScxFetchPromise} from "./ScxFetchPromise.js";
12
+ import {ScxFetchResponse} from "./ScxFetchResponse.js";
13
+ import {ResponseNotOKError} from "./ResponseNotOKError.js";
14
+ import {FetchError} from "./FetchError.js";
15
+ import {HttpMethod} from "./HttpMethod.js";
16
+
17
+ /**
18
+ * ScxFetch : 针对 fetch 的简单封装
19
+ */
20
+ class ScxFetch {
21
+
22
+ /**
23
+ * 默认配置
24
+ *
25
+ */
26
+ defaultOptions = initDefaultOptions();
27
+
28
+ /**
29
+ * 基本路径
30
+ */
31
+ baseURL;
32
+
33
+ /**
34
+ *
35
+ *
36
+ * @param {string|URL} baseURL
37
+ */
38
+ constructor(baseURL = null) {
39
+ if (notNull(baseURL)) {
40
+ this.baseURL = new URL(baseURL);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * 基本的 req
46
+ * @param url {URL | string}
47
+ * @param body {Object}
48
+ * @param options {ScxFetchOptions}
49
+ * @returns {ScxFetchPromise<ScxFetchResponse>}
50
+ */
51
+ fetch(url, body = {}, options = {}) {
52
+ const {
53
+ method,
54
+ headers,
55
+ responseType,
56
+ usePreInterceptor,
57
+ usePostInterceptor,
58
+ charset,
59
+ useBodyPromise,
60
+ } = mixinOptions(this.defaultOptions, options);
61
+
62
+ const requestInit = createRequestInit(method);//初始化 fetch 参数 , 此处携带 cookie
63
+
64
+ const finalURL = notNull(this.baseURL) ? new URL(url, this.baseURL) : new URL(url);//创建 url
65
+
66
+ setRequestBody(requestInit, body, finalURL, charset);//设置 body 并根据 body 类型设置请求头
67
+
68
+ setRequestHeaders(requestInit, headers);//设置请求头 放在 setRequestBody 后以保证 options 中的 headers 可以覆盖 setRequestBody 中设置的值
69
+
70
+ const finalInit = usePreInterceptor ? this.preInterceptor(requestInit) : requestInit;
71
+
72
+ //用于取消
73
+ let abortController = new AbortController();
74
+ finalInit.signal = abortController.signal;
75
+
76
+ //此处进行特殊处理 1, 处理返回结果 2, 将非 2xx 的状态码表示为错误
77
+ const result = new ScxFetchPromise((resolve, reject) => fetch(finalURL, finalInit).then(res => {
78
+ if (res.ok) {
79
+ // resolve 的参数是 Promise 时会直接调用 参数的 resolve
80
+ resolve(ScxFetchResponse.create(res, responseType, useBodyPromise));
81
+ } else {
82
+ reject(new ResponseNotOKError(res));
83
+ }
84
+ }).catch(error => reject(new FetchError(error))));
85
+
86
+ result.abortController = abortController;
87
+
88
+ return usePostInterceptor ? this.postInterceptor(result) : result;
89
+ }
90
+
91
+ /**
92
+ * 前置处理器
93
+ * @param request {RequestInit}
94
+ * @returns {RequestInit}
95
+ */
96
+ preInterceptor(request) {
97
+ return request;
98
+ }
99
+
100
+ /**
101
+ * 后置处理器
102
+ * @param response {ScxFetchPromise<ScxFetchResponse>}
103
+ * @returns {ScxFetchPromise<*>}
104
+ */
105
+ postInterceptor(response) {
106
+ return response;
107
+ }
108
+
109
+ /**
110
+ * GET 方法
111
+ * @param url {URL | string}
112
+ * @param body {Object}
113
+ * @param options {ScxFetchOptions}
114
+ * @returns {ScxFetchPromise<unknown>}
115
+ */
116
+ get(url, body = null, options = {}) {
117
+ return this.fetch(url, body, setMethod(HttpMethod.GET, options));
118
+ }
119
+
120
+ /**
121
+ * POST 方法
122
+ * @param url {URL | string}
123
+ * @param body {Object}
124
+ * @param options {ScxFetchOptions}
125
+ * @returns {ScxFetchPromise<unknown>}
126
+ */
127
+ post(url, body = null, options = {}) {
128
+ return this.fetch(url, body, setMethod(HttpMethod.POST, options));
129
+ }
130
+
131
+ /**
132
+ * PUT 方法
133
+ * @param url {URL | string}
134
+ * @param body {Object}
135
+ * @param options {ScxFetchOptions}
136
+ * @returns {ScxFetchPromise<unknown>}
137
+ */
138
+ put(url, body = null, options = {}) {
139
+ return this.fetch(url, body, setMethod(HttpMethod.PUT, options));
140
+ }
141
+
142
+ /**
143
+ * DELETE 方法
144
+ * @param {URL | string} url
145
+ * @param {Object} body
146
+ * @param options {ScxFetchOptions}
147
+ * @returns {ScxFetchPromise<unknown>}
148
+ */
149
+ delete(url, body = null, options = {}) {
150
+ return this.fetch(url, body, setMethod(HttpMethod.DELETE, options));
151
+ }
152
+
153
+ /**
154
+ * For VUE
155
+ * @param app
156
+ */
157
+ install(app) {
158
+ app.provide(scxFetchKey, this);
159
+ }
160
+
161
+ }
162
+
163
+ //************* 针对 vue ********************
164
+
165
+ const scxFetchKey = "scx-fetch";
166
+
167
+ /**
168
+ *
169
+ * @returns {ScxFetch}
170
+ */
171
+ function useScxFetch() {
172
+ return inject(scxFetchKey);
173
+ }
174
+
175
+ export {
176
+ ScxFetch,
177
+ useScxFetch,
178
+ };
@@ -0,0 +1,73 @@
1
+ import {notNull} from "@scx-js/scx-common";
2
+ import {ScxFetchOptions} from "./ScxFetchOptions.js";
3
+ import {HttpMethod} from "./HttpMethod.js";
4
+ import {ScxFetchResponseType} from "./ScxFetchResponseType.js";
5
+ import {HttpFieldName} from "./HttpFieldName.js";
6
+ import {MediaType} from "./MediaType.js";
7
+
8
+ function createRequestInit(method) {
9
+ return {
10
+ method,
11
+ headers: new Headers(),
12
+ credentials: "include",
13
+ body: null,
14
+ };
15
+ }
16
+
17
+ function setMethod(method, options = {}) {
18
+ options.method = method;
19
+ return options;
20
+ }
21
+
22
+ function setRequestHeaders(requestInit, headers) {
23
+ //循环设置 headers
24
+ if (notNull(headers)) {
25
+ if (headers instanceof Headers) {
26
+ headers.forEach((k, v) => requestInit.headers.set(k, v));
27
+ } else {
28
+ for (const [key, value] of Object.entries(headers)) {
29
+ requestInit.headers.set(key, String(value));
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ function setRequestBody(requestInit, body, url, charset) {
36
+ if (notNull(body)) {
37
+ if (body instanceof FormData) {
38
+ requestInit.body = body;
39
+ } else if (requestInit.method === HttpMethod.GET) {
40
+ for (const [key, value] of Object.entries(body)) {
41
+ url.searchParams.set(key, String(value));
42
+ }
43
+ } else {
44
+ requestInit.headers.set(HttpFieldName.CONTENT_TYPE, `${MediaType.APPLICATION_JSON};charset=${charset}`);
45
+ requestInit.body = JSON.stringify(body);
46
+ }
47
+ }
48
+ }
49
+
50
+ function mixinOptions(defaultOptions, options) {
51
+ return {...defaultOptions, ...options};
52
+ }
53
+
54
+ function initDefaultOptions() {
55
+ const temp = new ScxFetchOptions();
56
+ temp.method = HttpMethod.GET;
57
+ temp.headers = null;
58
+ temp.responseType = ScxFetchResponseType.AUTO;
59
+ temp.useBodyPromise = false;
60
+ temp.usePreInterceptor = false;
61
+ temp.usePostInterceptor = false;
62
+ temp.charset = "utf-8";
63
+ return temp;
64
+ }
65
+
66
+ export {
67
+ createRequestInit,
68
+ setMethod,
69
+ setRequestHeaders,
70
+ setRequestBody,
71
+ mixinOptions,
72
+ initDefaultOptions,
73
+ };
@@ -0,0 +1,45 @@
1
+ class ScxFetchOptions {
2
+
3
+ /**
4
+ * 方法
5
+ */
6
+ method;
7
+
8
+ /**
9
+ * 请求头
10
+ */
11
+ headers;
12
+
13
+ /**
14
+ * 默认想如何处理返回值 取值如下 [auto, arrayBuffer, blob, formData, json, text, ]
15
+ *
16
+ */
17
+ responseType;
18
+
19
+ /**
20
+ * 是否使用 BodyPromise 如果为 true 则 body 为 Promise 对象, false 则会将 body 数据读完再返回
21
+ * @type {boolean}
22
+ */
23
+ useBodyPromise;
24
+
25
+ /**
26
+ * 前置处理器
27
+ * @type {boolean}
28
+ */
29
+ usePreInterceptor;
30
+
31
+ /**
32
+ * 后置处理器
33
+ * @type {boolean}
34
+ */
35
+ usePostInterceptor;
36
+
37
+ /**
38
+ * 字符集
39
+ */
40
+ charset;
41
+ }
42
+
43
+ export {
44
+ ScxFetchOptions,
45
+ };
@@ -0,0 +1,33 @@
1
+ class ScxFetchPromise extends Promise {
2
+
3
+ abortController;
4
+
5
+ then(onfulfilled, onrejected) {
6
+ const s = super.then(onfulfilled, onrejected);
7
+ s.abortController = this.abortController;
8
+ return s;
9
+ }
10
+
11
+ catch(onrejected) {
12
+ const s = super.catch(onrejected);
13
+ s.abortController = this.abortController;
14
+ return s;
15
+ }
16
+
17
+ finally(onFinally) {
18
+ const s = super.finally(onFinally);
19
+ s.abortController = this.abortController;
20
+ return s;
21
+ }
22
+
23
+ cancel(reason) {
24
+ if (this.abortController) {
25
+ this.abortController.abort(reason);
26
+ }
27
+ }
28
+
29
+ }
30
+
31
+ export {
32
+ ScxFetchPromise,
33
+ };
@@ -0,0 +1,68 @@
1
+ import {ScxFetchResponseType} from "./ScxFetchResponseType.js";
2
+
3
+ /**
4
+ * ScxFetch 响应体
5
+ */
6
+ class ScxFetchResponse {
7
+
8
+ headers;
9
+ type;
10
+ url;
11
+ responseType;
12
+ body;
13
+
14
+ constructor(headers, type, url, body, responseType) {
15
+ this.headers = headers;
16
+ this.type = type;
17
+ this.url = url;
18
+ this.body = body;
19
+ this.responseType = responseType;
20
+ }
21
+
22
+ /**
23
+ * 根据 响应类型 获取对应的 body 处理器
24
+ * @param responseType
25
+ * @param response
26
+ * @return {Promise<ArrayBuffer>|ArrayBuffer|Promise<Blob>|Blob|*|Promise<FormData>}
27
+ */
28
+ static getBodyPromise(responseType, response) {
29
+ if (responseType === ScxFetchResponseType.JSON) {
30
+ return response.json();
31
+ } else if (responseType === ScxFetchResponseType.ARRAY_BUFFER) {
32
+ return response.arrayBuffer();
33
+ } else if (responseType === ScxFetchResponseType.BLOB) {
34
+ return response.blob();
35
+ } else if (responseType === ScxFetchResponseType.FORM_DATA) {
36
+ return response.formData();
37
+ } else if (responseType === ScxFetchResponseType.TEXT) {
38
+ return response.text();
39
+ } else {
40
+ return response.arrayBuffer();
41
+ }
42
+ }
43
+
44
+ /**
45
+ * 创建 ScxFetchResponse
46
+ * @param response
47
+ * @param defaultResponseType
48
+ * @param useBodyPromise
49
+ * @return {Promise<unknown>}
50
+ */
51
+ static create(response, defaultResponseType, useBodyPromise) {
52
+ return new Promise((resolve, reject) => {
53
+ const responseType = ScxFetchResponseType.getByHeaders(response.headers, defaultResponseType);
54
+ if (useBodyPromise) {
55
+ resolve(new ScxFetchResponse(response.headers, response.type, response.url, ScxFetchResponse.getBodyPromise(responseType, response), responseType));
56
+ } else {
57
+ ScxFetchResponse.getBodyPromise(responseType, response)
58
+ .then(body => resolve(new ScxFetchResponse(response.headers, response.type, response.url, body, responseType)))
59
+ .catch(e => reject(e));
60
+ }
61
+ });
62
+ }
63
+
64
+ }
65
+
66
+ export {
67
+ ScxFetchResponse,
68
+ };
@@ -0,0 +1,42 @@
1
+ import {HttpFieldName} from "./HttpFieldName.js";
2
+ import {MediaType} from "./MediaType.js";
3
+
4
+ class ScxFetchResponseType {
5
+
6
+ static ARRAY_BUFFER = "arrayBuffer";
7
+ static BLOB = "blob";
8
+ static FORM_DATA = "formData";
9
+ static JSON = "json";
10
+ static TEXT = "text";
11
+ static AUTO = "auto";
12
+
13
+ /**
14
+ * 获取 header 中的 CONTENT_TYPE 判断相应的类型
15
+ * @param headers
16
+ * @param defaultType 优先的返回值
17
+ */
18
+ static getByHeaders(headers, defaultType) {
19
+ if (defaultType && defaultType !== ScxFetchResponseType.AUTO) {
20
+ return defaultType;
21
+ }
22
+ let contentType = headers.get(HttpFieldName.CONTENT_TYPE);
23
+ if (contentType == null) {
24
+ return ScxFetchResponseType.ARRAY_BUFFER;
25
+ }
26
+ contentType = contentType.toLowerCase();
27
+ if (contentType.startsWith(MediaType.APPLICATION_JSON)) {
28
+ return ScxFetchResponseType.JSON;
29
+ } else if (contentType.startsWith(MediaType.TEXT_ANY) || contentType.startsWith(MediaType.APPLICATION_XML)) {
30
+ return ScxFetchResponseType.TEXT;
31
+ } else if (contentType.startsWith(MediaType.MULTIPART_FORM_DATA)) {
32
+ return ScxFetchResponseType.FORM_DATA;
33
+ } else {
34
+ return ScxFetchResponseType.ARRAY_BUFFER;
35
+ }
36
+ }
37
+
38
+ }
39
+
40
+ export {
41
+ ScxFetchResponseType,
42
+ };
package/ScxReq.js ADDED
@@ -0,0 +1,91 @@
1
+ import {ScxFetch} from "./ScxFetch.js";
2
+ import {inject} from "vue";
3
+ import {jsonVoProcessor, setResponseType} from "./ScxReqHelper.js";
4
+
5
+ /**
6
+ * 针对后台的 JsonVo 和 DataJsonVo 对 ScxFetch 进行一次包装
7
+ * 1, 将 JsonVo.ok() 和 JsonVo.fail() 区分开
8
+ * 2, 解构 JsonVo 返回的 data 字段
9
+ */
10
+ class ScxReq {
11
+
12
+ scxFetch;
13
+
14
+ baseURL;
15
+
16
+ constructor(scxFetch) {
17
+ if (scxFetch instanceof ScxFetch) {
18
+ this.scxFetch = scxFetch;
19
+ } else {
20
+ this.scxFetch = new ScxFetch(scxFetch);
21
+ }
22
+ this.baseURL = this.scxFetch.baseURL;
23
+ }
24
+
25
+ /**
26
+ * GET 方法
27
+ * @param url {URL | string}
28
+ * @param body {Object}
29
+ * @param options {ScxFetchOptions}
30
+ * @returns {ScxFetchPromise<unknown>}
31
+ */
32
+ get(url, body = null, options = {}) {
33
+ return jsonVoProcessor(this.scxFetch.get(url, body, setResponseType(options)));
34
+ }
35
+
36
+ /**
37
+ * POST 方法
38
+ * @param url {URL | string}
39
+ * @param body {Object}
40
+ * @param options {ScxFetchOptions}
41
+ * @returns {ScxFetchPromise<unknown>}
42
+ */
43
+ post(url, body = null, options = {}) {
44
+ return jsonVoProcessor(this.scxFetch.post(url, body, setResponseType(options)));
45
+ }
46
+
47
+ /**
48
+ * PUT 方法
49
+ * @param url {URL | string}
50
+ * @param body {Object}
51
+ * @param options {ScxFetchOptions}
52
+ * @returns {ScxFetchPromise<unknown>}
53
+ */
54
+ put(url, body = null, options = {}) {
55
+ return jsonVoProcessor(this.scxFetch.put(url, body, setResponseType(options)));
56
+ }
57
+
58
+ /**
59
+ * DELETE 方法
60
+ * @param url {URL | string}
61
+ * @param body {Object}
62
+ * @param options {ScxFetchOptions}
63
+ * @returns {ScxFetchPromise<unknown>}
64
+ */
65
+ delete(url, body = null, options = {}) {
66
+ return jsonVoProcessor(this.scxFetch.delete(url, body, setResponseType(options)));
67
+ }
68
+
69
+
70
+ install(app) {
71
+ app.provide(scxReqKey, this);
72
+ }
73
+
74
+ }
75
+
76
+
77
+ /**
78
+ *
79
+ * @type {string}
80
+ */
81
+ const scxReqKey = "scx-req";
82
+
83
+ /**
84
+ *
85
+ * @returns {ScxReq}
86
+ */
87
+ function useScxReq() {
88
+ return inject(scxReqKey);
89
+ }
90
+
91
+ export {ScxReq, useScxReq};
@@ -0,0 +1,45 @@
1
+ import {isNull} from "@scx-js/scx-common";
2
+ import {ScxFetchResponseType} from "./ScxFetchResponseType.js";
3
+ import {ScxFetchPromise} from "./ScxFetchPromise.js";
4
+ import {JsonVOError} from "./JsonVOError.js";
5
+
6
+ /**
7
+ * 通过 scx-req 发送的请求我们默认都是 json 格式的
8
+ * @param {ScxFetchOptions} options
9
+ * @returns {ScxFetchOptions}
10
+ */
11
+ function setResponseType(options = {}) {
12
+ if (isNull(options.responseType)) {
13
+ options.responseType = ScxFetchResponseType.JSON;
14
+ }
15
+ return options;
16
+ }
17
+
18
+
19
+ function jsonVoProcessor(r) {
20
+ //这里是特殊处理
21
+ const result = new ScxFetchPromise((resolve, reject) => r.then(res => {
22
+ const {
23
+ responseType,
24
+ body,
25
+ } = res;
26
+ if (responseType === ScxFetchResponseType.JSON) {
27
+ if (body.message === "ok") {
28
+ resolve(body.data);
29
+ } else {
30
+ reject(new JsonVOError(body));
31
+ }
32
+ } else {
33
+ resolve(res);
34
+ }
35
+ }).catch(error => reject(error)));
36
+
37
+ result.abortController = r.abortController;
38
+
39
+ return result;
40
+ }
41
+
42
+ export {
43
+ setResponseType,
44
+ jsonVoProcessor,
45
+ };
package/index.js ADDED
@@ -0,0 +1,14 @@
1
+ export * from "./FetchError.js";
2
+ export * from "./HttpFieldName.js";
3
+ export * from "./HttpMethod.js";
4
+ export * from "./JsonVOError.js";
5
+ export * from "./MediaType.js";
6
+ export * from "./ResponseNotOKError.js";
7
+ export * from "./ScxFetch.js";
8
+ export * from "./ScxFetchHelper.js";
9
+ export * from "./ScxFetchOptions.js";
10
+ export * from "./ScxFetchPromise.js";
11
+ export * from "./ScxFetchResponse.js";
12
+ export * from "./ScxFetchResponseType.js";
13
+ export * from "./ScxReq.js";
14
+ export * from "./ScxReqHelper.js";
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@scx-js/scx-http",
3
+ "version": "0.0.1",
4
+ "description": "SCX HTTP",
5
+ "license": "MIT",
6
+ "author": "scx567888",
7
+ "main": "index.js",
8
+ "type": "module",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/scx567888/scx-js.git"
12
+ },
13
+ "dependencies": {
14
+ "@scx-js/scx-common": "0.0.1",
15
+ "vue": "^3.0.0"
16
+ }
17
+ }