1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | export type UploadProgressEvent = ProgressEvent & {percent: number};
|
7 |
|
8 | export interface RequestResult {
|
9 | status: number;
|
10 | statusText: string;
|
11 | data: any,
|
12 | headers: any,
|
13 | xhr: XMLHttpRequest,
|
14 | }
|
15 |
|
16 | export interface RequestOptions {
|
17 | onProgress?: (e: UploadProgressEvent) => void;
|
18 | onError: (error, body?: object) => void;
|
19 |
|
20 | onSuccess: (result, xhr: XMLHttpRequest) => void;
|
21 | onAbort?: () => void;
|
22 | data?: Document | BodyInit | null;
|
23 |
|
24 |
|
25 | withCredentials?: boolean;
|
26 |
|
27 | headers?:object;
|
28 | method?: string;
|
29 | timeout?: number;
|
30 | }
|
31 |
|
32 | type RequestError = Error & {status?: number; method?: string; url?: string; code?:string};
|
33 |
|
34 | function getError(url, option, xhr) {
|
35 | const codePart = xhr.response.match(/<Code>(.+)<\/Code>/);
|
36 | const messagePart = xhr.response.match(/<Message>(.+)<\/Message>/);
|
37 |
|
38 | const method = option.method || 'GET';
|
39 |
|
40 | let msg = '';
|
41 | if (codePart && codePart[1]) msg += `${codePart[1]}: `;
|
42 | if (messagePart && messagePart[1]) msg += messagePart[1];
|
43 | const err: RequestError = new Error(msg);
|
44 | err.status = xhr.status;
|
45 | err.method = method;
|
46 | err.code = codePart && codePart[1] || '';
|
47 | err.url = url;
|
48 | return err;
|
49 | }
|
50 |
|
51 | function getBody(xhr) {
|
52 | const text = xhr.responseText || xhr.response;
|
53 | if (!text) {
|
54 | return text;
|
55 | }
|
56 |
|
57 | try {
|
58 | return JSON.parse(text);
|
59 | } catch (e) {
|
60 | return text;
|
61 | }
|
62 | }
|
63 |
|
64 | export default function request (url: string, options: RequestOptions) {
|
65 | const xhr = new XMLHttpRequest();
|
66 |
|
67 | xhr.timeout = options.timeout || 60_000;
|
68 |
|
69 | if (options.onProgress && xhr.upload){
|
70 | xhr.upload.onprogress = function progress(e: UploadProgressEvent) {
|
71 | e.percent = e.loaded / e.total * 100;
|
72 | options.onProgress(e);
|
73 | }
|
74 | }
|
75 |
|
76 | |
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 | xhr.onerror = function (e) {
|
87 | options.onError(e);
|
88 | };
|
89 |
|
90 | xhr.onload = function onload() {
|
91 | if (xhr.status < 200 || xhr.status >= 300) {
|
92 | return options.onError(getError(url, options, xhr), getBody(xhr));
|
93 | }
|
94 | const result = {
|
95 | status: xhr.status,
|
96 | statusText: xhr.statusText,
|
97 | xhr,
|
98 | data: getBody(xhr),
|
99 | };
|
100 | options.onSuccess(result, xhr);
|
101 | };
|
102 |
|
103 | xhr.ontimeout = function timeout(ev){
|
104 | const err: Error = new Error(`Request timeout, limit ${xhr.timeout} ms.`);
|
105 | options.onError(err);
|
106 | };
|
107 |
|
108 | if (options.onAbort) xhr.onabort = options.onAbort;
|
109 |
|
110 | xhr.open(options.method || 'get', url, true);
|
111 |
|
112 |
|
113 | if (options.withCredentials && 'withCredentials' in xhr) {
|
114 | xhr.withCredentials = true;
|
115 | }
|
116 |
|
117 | const headers = options.headers || {};
|
118 |
|
119 | if (headers['X-Requested-With'] !== null) {
|
120 | xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
121 | }
|
122 |
|
123 | for (const h in headers){
|
124 | if (headers.hasOwnProperty(h) && headers[h] !== null){
|
125 | xhr.setRequestHeader(h, headers[h]);
|
126 | }
|
127 | }
|
128 |
|
129 | xhr.send(options.data || null);
|
130 |
|
131 | return {
|
132 | abort() {
|
133 | xhr.abort();
|
134 | },
|
135 | }
|
136 | }
|