-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathajax.ts
120 lines (113 loc) · 3.66 KB
/
ajax.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
ApiResponse,
HttpMethod,
HttpRequestHeaders,
HttpResponse,
HttpResponseHeaders,
HttpStatus,
parseHeaders,
} from './http';
const enum AjaxState {
UNSENT = 0,
OPENED = 1,
HEADERS_RECEIVED = 2,
LOADING = 3,
DONE = 4,
}
export interface AjaxRequest {
method: HttpMethod;
url: string;
payload?: any;
headers?: HttpRequestHeaders;
}
export async function ajaxJson(request: AjaxRequest): Promise<ApiResponse> {
const textResponse = await requestRaw(request);
const { statusCode, body, headers } = textResponse;
let response;
if (body) {
// Attempt to parse the response text as JSON object.
try {
response = { statusCode, headers, data: JSON.parse(body) };
} catch (error) {
throw new AjaxError(request, statusCode, {}, body, undefined, error);
}
} else {
response = { statusCode, headers };
}
if (statusCode >= 200 && statusCode < 300) {
return response;
}
throw new AjaxError(request, statusCode, headers, response.data, body);
}
export async function ajax(request: AjaxRequest): Promise<HttpResponse> {
const response = await requestRaw(request);
const { statusCode } = response;
if (statusCode >= 200 && statusCode < 300) {
return response;
}
throw new AjaxError(request, statusCode, response.headers, response.body);
}
function requestRaw(request: AjaxRequest): Promise<HttpResponse> {
return new Promise<HttpResponse>((resolve, reject) => {
const { headers, payload, url, method } = request;
const xhr = new XMLHttpRequest();
function onReadyStateChange(this: XMLHttpRequest) {
if (xhr.readyState === AjaxState.DONE) {
// Ajax request has completed
let statusCode = xhr.status;
// normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
if (statusCode === 1223) {
statusCode = 204;
}
resolve({
statusCode,
body: getBody(xhr),
headers: parseHeaders(xhr.getAllResponseHeaders()),
});
}
}
function onError(this: XMLHttpRequestEventTarget, error: ProgressEvent) {
reject(new AjaxError(request, 0, {}, getBody(xhr), undefined, error));
}
xhr.open(method, url, true);
// Set the request headers
if (headers) {
Object.keys(headers).forEach((headerName) => {
const headerValue = headers[headerName];
if (headerValue) {
xhr.setRequestHeader(headerName, headerValue);
}
});
}
xhr.onerror = onError;
xhr.onreadystatechange = onReadyStateChange;
if (payload == null) {
xhr.send();
} else if (payload instanceof FormData) {
xhr.send(payload);
} else {
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(payload));
}
});
}
function getBody(xhr: XMLHttpRequest): string {
try {
return xhr.responseText;
} catch {
return '';
}
}
export class AjaxError extends Error implements ApiResponse, HttpResponse {
constructor(
public readonly request: AjaxRequest,
public readonly statusCode: HttpStatus | 0,
public readonly headers: HttpResponseHeaders,
public readonly body: string,
public readonly data: any = null,
public readonly error?: Error | Event,
) {
super();
}
}