@ams-team/ams
Version:
Admin Materials System.
288 lines (255 loc) • 9.9 kB
JavaScript
import { serialize, deepExtend } from '../utils';
export const httpRequestTypeExcludeGet = ['POST', 'PUT', 'DELETE', 'PATCH'];
export default function initRequest(ams) {
let escape = encodeURIComponent;
ams.param = function(obj) {
let params = [];
params.add = function(key, value) {
if (value == null) value = '';
this.push(escape(key) + '=' + escape(value));
};
serialize(params, obj);
return params.join('&').replace(/%20/g, '+');
};
/**
* 使用FormData对象
* https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/Using_FormData_Objects
* */
ams.formData = function(obj) {
let formData = new FormData();
Object.keys(obj).forEach(key => {
let val = obj[key];
val = val == null ? '' : val;
if (typeof val === 'object') {
if (val instanceof window.File || val instanceof window.Blob) {
formData.append(key, val);
} else {
formData.append(key, JSON.stringify(val));
}
} else {
formData.append(key, val);
}
});
return formData;
};
/**
ams.request({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
},
params, {a: 1},
withCredentials: true,
contentType: true,
responseType: '',
headers: {'X-Custom-Header': 'foobar'}
});
*/
ams.request = function(options) {
return new Promise((resolve, reject) => {
// 全局请求参数拦截器
if (ams.configs.resource.requestInterceptor) {
options = ams.configs.resource.requestInterceptor(options);
}
const api = ams.configs.resource.api;
let {
method,
url,
data,
params,
headers = {},
withCredentials = api.withCredentials,
contentType = api.contentType,
responseType = api.responseType,
} = options;
const xhr = new XMLHttpRequest();
let sendData = null;
method = (method || 'GET').toUpperCase();
if (params) {
url +=
(/\?/.test(url) ? '&' : '?') +
Object.keys(params)
.map(key => `${key}=${escape(params[key])}`)
.join('&');
}
xhr.open(method, url, true);
// 处理sendData
// 可用contentType: json|form|multipart
if (httpRequestTypeExcludeGet.indexOf(method) >= 0) {
if (contentType === 'json') {
headers['Content-Type'] =
headers['Content-Type'] ||
'application/json;charset=UTF-8';
sendData = data ? JSON.stringify(data) : null;
} else if (contentType === 'multipart') {
// 该模式下不要修改Content-Type
sendData = data ? ams.formData(data) : null;
} else {
headers['Content-Type'] =
headers['Content-Type'] ||
'application/x-www-form-urlencoded;charset=UTF-8';
sendData = data ? ams.param(data) : null;
}
}
Object.keys(headers).forEach(key => {
xhr.setRequestHeader(key, headers[key]);
});
const { errorInterceptor } = ams.configs.resource || (err => {
throw err;
});
function handleError(err, response) {
// 没服务器返回的错误状态码会置为0
err.code = xhr.status || 0;
err.response = response;
err.options = options;
try {
const response = errorInterceptor(err);
if (response) {
resolve(response);
} else {
reject(new Error('cancel by error interceptor'));
}
} catch (e) {
reject(e);
}
}
xhr.onreadystatechange = function() {
// console.log('onreadystatechange', xhr.readyState, xhr.status);
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// options.success(xhr.responseText);
let response = {
status: xhr.status,
data: {}
};
try {
response.data = JSON.parse(xhr.responseText);
} catch (e) {}
// 全局请求结果拦截器
if (ams.configs.resource.responseInterceptor) {
response = ams.configs.resource.responseInterceptor(
response,
options
);
}
// 全局错误code处理
const codes = ams.configs.resource.codes;
const code = response && response.data && response.data.code;
if (codes && codes[code]) {
response = codes[code](
response,
options
);
}
if (response) {
resolve(response);
} else {
reject(new Error('cancel by response interceptor'));
}
} else {
const response = {
status: xhr.status,
content: xhr.responseText,
// 默认不放data,本来默认是个对象就是不对,但旧API不能改,只能另写一段
};
try {
response.data = JSON.parse(xhr.responseText);
} catch (e) {}
// 仅当status不为0时才走这里的错误逻辑,否则会和onerror走2次逻辑
if (xhr.status !== 0) {
handleError(new Error(`${xhr.status}`), response);
}
}
}
};
xhr.onerror = function() {
handleError(new Error('network error'));
};
// withCredentials默认为true
xhr.withCredentials = withCredentials;
xhr.responseType = responseType;
xhr.send(sendData);
});
};
/**
* options.getOptions
* options.beforeRequest
* options.success
*/
ams.createApiAction = function(options) {
/**
* params.url
* params.method
* params.params
* params.headers
* params.data
* params.withCredentials
* params.contentType
*
* params.getOptions
* params.beforeRequest
* params.success
*/
return async function(params = {}) {
this.showLoading();
try {
let requestOptions = {
contentType: this.getConfig('resource.api.contentType'),
withCredentials: this.getConfig(
'resource.api.withCredentials'
),
...(params.getOptions || options.getOptions).call(
this,
params
)
};
const resetOptions = {};
if (params.url) {
resetOptions.url = params.url;
}
if (params.method) {
resetOptions.method = params.method;
}
if (params.params) {
resetOptions.params = params.params;
}
if (params.data) {
resetOptions.data = params.data;
}
if (params.headers) {
resetOptions.headers = params.headers;
}
if (params.contentType) {
resetOptions.contentType = params.contentType;
}
if (typeof params.withCredentials !== 'undefined') {
resetOptions.withCredentials = params.withCredentials;
}
// 单次请求可以覆盖url、method、params、data
deepExtend(requestOptions, resetOptions);
const beforeRequest =
params.beforeRequest || options.beforeRequest;
if (beforeRequest) {
requestOptions = beforeRequest.call(
this,
requestOptions,
params
);
}
const res = await ams.request(requestOptions);
this.hideLoading();
return (params.success || options.success).call(
this,
res,
requestOptions
);
} catch (e) {
// 处理完继续抛出错误使 action 中断
this.hideLoading();
throw e;
}
};
};
}