@tarojs/plugin-http 3.8.0-canary.0 → 4.0.0-alpha.0
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/dist/runtime.d.ts +4 -1
- package/dist/runtime.js +33 -6
- package/package.json +5 -5
- package/src/__tests__/xhr.spec.ts +101 -0
- package/src/runtime/XMLHttpRequest.ts +33 -6
package/dist/runtime.d.ts
CHANGED
|
@@ -58,7 +58,10 @@ declare class XMLHttpRequest extends Events {
|
|
|
58
58
|
// 欺骗一些库让其认为是原生的xhr
|
|
59
59
|
static toString(): string;
|
|
60
60
|
toString(): string;
|
|
61
|
-
//
|
|
61
|
+
// 事件正常流转: loadstart => progress(可能多次) => load => loadend
|
|
62
|
+
// error 流转: loadstart => error => loadend
|
|
63
|
+
// abort 流转: loadstart => abort => loadend
|
|
64
|
+
// web在线测试: https://developer.mozilla.org/zh-CN/play
|
|
62
65
|
/** 当 request 被停止时触发,例如当程序调用 XMLHttpRequest.abort() 时 */
|
|
63
66
|
onabort: ((e: XMLHttpRequestEvent) => void) | null;
|
|
64
67
|
/** 当 request 遭遇错误时触发 */
|
package/dist/runtime.js
CHANGED
|
@@ -406,7 +406,10 @@ class XMLHttpRequest extends Events {
|
|
|
406
406
|
_XMLHttpRequest_timeout.set(this, void 0);
|
|
407
407
|
_XMLHttpRequest_withCredentials.set(this, void 0);
|
|
408
408
|
_XMLHttpRequest_requestTask.set(this, void 0);
|
|
409
|
-
//
|
|
409
|
+
// 事件正常流转: loadstart => progress(可能多次) => load => loadend
|
|
410
|
+
// error 流转: loadstart => error => loadend
|
|
411
|
+
// abort 流转: loadstart => abort => loadend
|
|
412
|
+
// web在线测试: https://developer.mozilla.org/zh-CN/play
|
|
410
413
|
/** 当 request 被停止时触发,例如当程序调用 XMLHttpRequest.abort() 时 */
|
|
411
414
|
this.onabort = null;
|
|
412
415
|
/** 当 request 遭遇错误时触发 */
|
|
@@ -606,7 +609,7 @@ _XMLHttpRequest_method = new WeakMap(), _XMLHttpRequest_url = new WeakMap(), _XM
|
|
|
606
609
|
__classPrivateFieldGet(this, _XMLHttpRequest_instances, "m", _XMLHttpRequest_callReadyStateChange).call(this, XMLHttpRequest.HEADERS_RECEIVED);
|
|
607
610
|
if (ENABLE_COOKIE) {
|
|
608
611
|
// 处理 set-cookie
|
|
609
|
-
const setCookieStr =
|
|
612
|
+
const setCookieStr = this.getResponseHeader('set-cookie');
|
|
610
613
|
if (setCookieStr && typeof setCookieStr === 'string') {
|
|
611
614
|
let start = 0;
|
|
612
615
|
let startSplit = 0;
|
|
@@ -634,17 +637,40 @@ _XMLHttpRequest_method = new WeakMap(), _XMLHttpRequest_url = new WeakMap(), _XM
|
|
|
634
637
|
// 处理返回数据
|
|
635
638
|
if (data) {
|
|
636
639
|
__classPrivateFieldGet(this, _XMLHttpRequest_instances, "m", _XMLHttpRequest_callReadyStateChange).call(this, XMLHttpRequest.LOADING);
|
|
637
|
-
const
|
|
640
|
+
const contentLength = Number(this.getResponseHeader('content-length') || 0);
|
|
641
|
+
const loadstartEvent = createXMLHttpRequestEvent('loadstart', this, contentLength);
|
|
638
642
|
this.trigger('loadstart', loadstartEvent);
|
|
639
643
|
isFunction(this.onloadstart) && this.onloadstart(loadstartEvent);
|
|
640
644
|
__classPrivateFieldSet(this, _XMLHttpRequest_response, data, "f");
|
|
641
|
-
const loadEvent = createXMLHttpRequestEvent('load', this,
|
|
645
|
+
const loadEvent = createXMLHttpRequestEvent('load', this, contentLength);
|
|
642
646
|
this.trigger('load', loadEvent);
|
|
643
647
|
isFunction(this.onload) && this.onload(loadEvent);
|
|
644
648
|
}
|
|
645
649
|
}, _XMLHttpRequest_requestFail = function _XMLHttpRequest_requestFail(err) {
|
|
650
|
+
// 微信小程序,无论接口返回200还是其他,响应无论是否有错误,都会进入 success 回调;只有类似超时这种请求错误才会进入 fail 回调
|
|
651
|
+
//
|
|
652
|
+
/**
|
|
653
|
+
* 阿里系小程序,接口返回非200状态码,会进入 fail 回调, 此时 err 对象结构如下(当错误码为 14 或 19 时,会多返回 status、data、headers。可通过这些字段获取服务端相关错误信息):
|
|
654
|
+
{
|
|
655
|
+
data: "{\"code\": 401,\"msg\":\"登录过期,请重新登录\"}"
|
|
656
|
+
error: 19
|
|
657
|
+
errorMessage: "http status error"
|
|
658
|
+
headers: {date: 'Mon, 14 Aug 2023 08:54:58 GMT', content-type: 'application/json;charset=UTF-8', content-length: '52', connection: 'close', access-control-allow-credentials: 'true', …}
|
|
659
|
+
originalData: "{\"code\": 401,\"msg\":\"登录过期,请重新登录\"}"
|
|
660
|
+
status: 401
|
|
661
|
+
}
|
|
662
|
+
*/
|
|
663
|
+
// 统一行为,能正常响应的,都算 success.
|
|
664
|
+
if (err.status) {
|
|
665
|
+
__classPrivateFieldGet(this, _XMLHttpRequest_instances, "m", _XMLHttpRequest_requestSuccess).call(this, {
|
|
666
|
+
data: err,
|
|
667
|
+
statusCode: err.status,
|
|
668
|
+
header: err.headers
|
|
669
|
+
});
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
646
672
|
__classPrivateFieldSet(this, _XMLHttpRequest_status, 0, "f");
|
|
647
|
-
__classPrivateFieldSet(this, _XMLHttpRequest_statusText, err.errMsg, "f");
|
|
673
|
+
__classPrivateFieldSet(this, _XMLHttpRequest_statusText, err.errMsg || err.errorMessage, "f");
|
|
648
674
|
const errorEvent = createXMLHttpRequestEvent('error', this, 0);
|
|
649
675
|
this.trigger('error', errorEvent);
|
|
650
676
|
isFunction(this.onerror) && this.onerror(errorEvent);
|
|
@@ -652,7 +678,8 @@ _XMLHttpRequest_method = new WeakMap(), _XMLHttpRequest_url = new WeakMap(), _XM
|
|
|
652
678
|
__classPrivateFieldSet(this, _XMLHttpRequest_requestTask, null, "f");
|
|
653
679
|
__classPrivateFieldGet(this, _XMLHttpRequest_instances, "m", _XMLHttpRequest_callReadyStateChange).call(this, XMLHttpRequest.DONE);
|
|
654
680
|
if (__classPrivateFieldGet(this, _XMLHttpRequest_status, "f")) {
|
|
655
|
-
const
|
|
681
|
+
const contentLength = Number(this.getResponseHeader('content-length') || 0);
|
|
682
|
+
const loadendEvent = createXMLHttpRequestEvent('loadend', this, contentLength);
|
|
656
683
|
this.trigger('loadend', loadendEvent);
|
|
657
684
|
isFunction(this.onloadend) && this.onloadend(loadendEvent);
|
|
658
685
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tarojs/plugin-http",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0-alpha.0",
|
|
4
4
|
"description": "Taro 小程序端支持使用 web 请求 的插件",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
},
|
|
24
24
|
"homepage": "https://github.com/NervJS/taro#readme",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@tarojs/runtime": "
|
|
27
|
-
"@tarojs/service": "
|
|
28
|
-
"@tarojs/shared": "
|
|
26
|
+
"@tarojs/runtime": "4.0.0-alpha.0",
|
|
27
|
+
"@tarojs/service": "4.0.0-alpha.0",
|
|
28
|
+
"@tarojs/shared": "4.0.0-alpha.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@rollup/plugin-json": "^
|
|
31
|
+
"@rollup/plugin-json": "^6.0.0",
|
|
32
32
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
|
33
33
|
"jest": "^29.3.1",
|
|
34
34
|
"jest-cli": "^29.3.1",
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { window } from '@tarojs/runtime'
|
|
2
|
+
import { request, RequestTask } from '@tarojs/taro'
|
|
3
|
+
|
|
4
|
+
import { XMLHttpRequest, XMLHttpRequestEvent } from '../../dist/runtime'
|
|
5
|
+
|
|
6
|
+
jest.mock('@tarojs/taro', () => {
|
|
7
|
+
return {
|
|
8
|
+
request: jest.fn(),
|
|
9
|
+
}
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const requestMock = jest.mocked(request)
|
|
13
|
+
|
|
14
|
+
describe('XMLHttpRequest', () => {
|
|
15
|
+
it('大写header key', async () => {
|
|
16
|
+
const data = 'success'
|
|
17
|
+
// 模拟实现 request 函数
|
|
18
|
+
requestMock.mockImplementation((opt) => {
|
|
19
|
+
if (opt.success) {
|
|
20
|
+
const header = { 'Content-Length': data.length.toString() }
|
|
21
|
+
opt.success({ data: data as any, header, statusCode: 200, errMsg: '' })
|
|
22
|
+
}
|
|
23
|
+
if (opt.complete) {
|
|
24
|
+
opt.complete({ errMsg: '' })
|
|
25
|
+
}
|
|
26
|
+
return {} as RequestTask<any>
|
|
27
|
+
})
|
|
28
|
+
// 发起请求
|
|
29
|
+
const xhr = new XMLHttpRequest()
|
|
30
|
+
xhr.open('GET', 'localhost')
|
|
31
|
+
const req = new Promise<XMLHttpRequestEvent>((resolve, reject) => {
|
|
32
|
+
xhr.addEventListener('load', resolve)
|
|
33
|
+
xhr.addEventListener('error', reject)
|
|
34
|
+
})
|
|
35
|
+
xhr.send('')
|
|
36
|
+
await req
|
|
37
|
+
// 检查是否能正常读取大写的header
|
|
38
|
+
expect(xhr.status).toBe(200)
|
|
39
|
+
expect(data.length).toBeGreaterThan(0)
|
|
40
|
+
expect(xhr.getResponseHeader('Content-length')).toBe(data.length.toString())
|
|
41
|
+
expect(xhr.getResponseHeader('content-length')).toBe(data.length.toString())
|
|
42
|
+
expect(xhr.getResponseHeader('Content-Length')).toBe(data.length.toString())
|
|
43
|
+
})
|
|
44
|
+
it('小写header key', async () => {
|
|
45
|
+
const data = 'success'
|
|
46
|
+
// 模拟实现 request 函数
|
|
47
|
+
requestMock.mockImplementation((opt) => {
|
|
48
|
+
if (opt.success) {
|
|
49
|
+
const header = { 'content-length': data.length.toString() }
|
|
50
|
+
opt.success({ data: data as any, header, statusCode: 200, errMsg: '' })
|
|
51
|
+
}
|
|
52
|
+
if (opt.complete) {
|
|
53
|
+
opt.complete({ errMsg: '' })
|
|
54
|
+
}
|
|
55
|
+
return {} as RequestTask<any>
|
|
56
|
+
})
|
|
57
|
+
const xhr = new XMLHttpRequest()
|
|
58
|
+
xhr.open('GET', 'localhost')
|
|
59
|
+
const req = new Promise<XMLHttpRequestEvent>((resolve, reject) => {
|
|
60
|
+
xhr.addEventListener('load', resolve)
|
|
61
|
+
xhr.addEventListener('error', reject)
|
|
62
|
+
})
|
|
63
|
+
xhr.send('')
|
|
64
|
+
await req
|
|
65
|
+
// 检查是否能成功读取小写的header
|
|
66
|
+
expect(xhr.status).toBe(200)
|
|
67
|
+
expect(xhr.responseText).toBe(data)
|
|
68
|
+
expect(xhr.getResponseHeader('Content-length')).toBe(data.length.toString())
|
|
69
|
+
expect(xhr.getResponseHeader('content-length')).toBe(data.length.toString())
|
|
70
|
+
expect(xhr.getResponseHeader('Content-Length')).toBe(data.length.toString())
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('set-cookie', async () => {
|
|
74
|
+
const data = 'success'
|
|
75
|
+
// 模拟实现 request 函数
|
|
76
|
+
requestMock.mockImplementation((opt) => {
|
|
77
|
+
if (opt.success) {
|
|
78
|
+
const header = { 'set-cookie': 'aaa=bbb; domain=taro.com' }
|
|
79
|
+
opt.success({ data: data as any, header, statusCode: 200, errMsg: '' })
|
|
80
|
+
}
|
|
81
|
+
if (opt.complete) {
|
|
82
|
+
opt.complete({ errMsg: '' })
|
|
83
|
+
}
|
|
84
|
+
return {} as RequestTask<any>
|
|
85
|
+
})
|
|
86
|
+
// 发送一个请求
|
|
87
|
+
const xhr = new XMLHttpRequest()
|
|
88
|
+
xhr.open('GET', 'localhost')
|
|
89
|
+
const req = new Promise<XMLHttpRequestEvent>((resolve, reject) => {
|
|
90
|
+
xhr.addEventListener('load', resolve)
|
|
91
|
+
xhr.addEventListener('error', reject)
|
|
92
|
+
})
|
|
93
|
+
// 在发送请求之前cookie为空
|
|
94
|
+
expect(window.document.cookie.length).toBe(0)
|
|
95
|
+
xhr.send('')
|
|
96
|
+
// 等待请求加载完成
|
|
97
|
+
await req
|
|
98
|
+
// 检查是否能读取set-cookie
|
|
99
|
+
expect(window.document.cookie.length).not.toBe(0)
|
|
100
|
+
})
|
|
101
|
+
})
|
|
@@ -118,7 +118,10 @@ export class XMLHttpRequest extends Events {
|
|
|
118
118
|
#withCredentials: boolean
|
|
119
119
|
#requestTask: null | Taro.RequestTask<any>
|
|
120
120
|
|
|
121
|
-
//
|
|
121
|
+
// 事件正常流转: loadstart => progress(可能多次) => load => loadend
|
|
122
|
+
// error 流转: loadstart => error => loadend
|
|
123
|
+
// abort 流转: loadstart => abort => loadend
|
|
124
|
+
// web在线测试: https://developer.mozilla.org/zh-CN/play
|
|
122
125
|
|
|
123
126
|
/** 当 request 被停止时触发,例如当程序调用 XMLHttpRequest.abort() 时 */
|
|
124
127
|
onabort: ((e: XMLHttpRequestEvent) => void) | null = null
|
|
@@ -259,7 +262,7 @@ export class XMLHttpRequest extends Events {
|
|
|
259
262
|
|
|
260
263
|
if (ENABLE_COOKIE) {
|
|
261
264
|
// 处理 set-cookie
|
|
262
|
-
const setCookieStr =
|
|
265
|
+
const setCookieStr = this.getResponseHeader('set-cookie')
|
|
263
266
|
|
|
264
267
|
if (setCookieStr && typeof setCookieStr === 'string') {
|
|
265
268
|
let start = 0
|
|
@@ -294,12 +297,13 @@ export class XMLHttpRequest extends Events {
|
|
|
294
297
|
// 处理返回数据
|
|
295
298
|
if (data) {
|
|
296
299
|
this.#callReadyStateChange(XMLHttpRequest.LOADING)
|
|
297
|
-
const
|
|
300
|
+
const contentLength = Number(this.getResponseHeader('content-length') || 0)
|
|
301
|
+
const loadstartEvent = createXMLHttpRequestEvent('loadstart', this, contentLength)
|
|
298
302
|
this.trigger('loadstart', loadstartEvent)
|
|
299
303
|
isFunction(this.onloadstart) && this.onloadstart(loadstartEvent)
|
|
300
304
|
this.#response = data
|
|
301
305
|
|
|
302
|
-
const loadEvent = createXMLHttpRequestEvent('load', this,
|
|
306
|
+
const loadEvent = createXMLHttpRequestEvent('load', this, contentLength)
|
|
303
307
|
this.trigger('load', loadEvent)
|
|
304
308
|
isFunction(this.onload) && this.onload(loadEvent)
|
|
305
309
|
}
|
|
@@ -309,8 +313,30 @@ export class XMLHttpRequest extends Events {
|
|
|
309
313
|
* 请求失败
|
|
310
314
|
*/
|
|
311
315
|
#requestFail (err) {
|
|
316
|
+
// 微信小程序,无论接口返回200还是其他,响应无论是否有错误,都会进入 success 回调;只有类似超时这种请求错误才会进入 fail 回调
|
|
317
|
+
//
|
|
318
|
+
/**
|
|
319
|
+
* 阿里系小程序,接口返回非200状态码,会进入 fail 回调, 此时 err 对象结构如下(当错误码为 14 或 19 时,会多返回 status、data、headers。可通过这些字段获取服务端相关错误信息):
|
|
320
|
+
{
|
|
321
|
+
data: "{\"code\": 401,\"msg\":\"登录过期,请重新登录\"}"
|
|
322
|
+
error: 19
|
|
323
|
+
errorMessage: "http status error"
|
|
324
|
+
headers: {date: 'Mon, 14 Aug 2023 08:54:58 GMT', content-type: 'application/json;charset=UTF-8', content-length: '52', connection: 'close', access-control-allow-credentials: 'true', …}
|
|
325
|
+
originalData: "{\"code\": 401,\"msg\":\"登录过期,请重新登录\"}"
|
|
326
|
+
status: 401
|
|
327
|
+
}
|
|
328
|
+
*/
|
|
329
|
+
// 统一行为,能正常响应的,都算 success.
|
|
330
|
+
if (err.status) {
|
|
331
|
+
this.#requestSuccess({
|
|
332
|
+
data: err,
|
|
333
|
+
statusCode: err.status,
|
|
334
|
+
header: err.headers
|
|
335
|
+
})
|
|
336
|
+
return
|
|
337
|
+
}
|
|
312
338
|
this.#status = 0
|
|
313
|
-
this.#statusText = err.errMsg
|
|
339
|
+
this.#statusText = err.errMsg || err.errorMessage
|
|
314
340
|
const errorEvent = createXMLHttpRequestEvent('error', this, 0)
|
|
315
341
|
this.trigger('error', errorEvent)
|
|
316
342
|
isFunction(this.onerror) && this.onerror(errorEvent)
|
|
@@ -324,7 +350,8 @@ export class XMLHttpRequest extends Events {
|
|
|
324
350
|
this.#callReadyStateChange(XMLHttpRequest.DONE)
|
|
325
351
|
|
|
326
352
|
if (this.#status) {
|
|
327
|
-
const
|
|
353
|
+
const contentLength = Number(this.getResponseHeader('content-length') || 0)
|
|
354
|
+
const loadendEvent = createXMLHttpRequestEvent('loadend', this, contentLength)
|
|
328
355
|
this.trigger('loadend', loadendEvent)
|
|
329
356
|
isFunction(this.onloadend) && this.onloadend(loadendEvent)
|
|
330
357
|
}
|