This repository has been archived on 2023-08-30. You can view files and clone it, but cannot push or open issues/pull-requests.
bytedo
/
wcui
Archived
1
0
Fork 0
wcui/src/request/index.js

440 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
*
* @authors yutent (yutent@doui.cc)
* @date 2018-03-25 23:59:13
* @version $Id$
*/
'use strict'
import Format from './lib/format'
// 本地协议/头 判断正则
const rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/
const log = console.log
const noop = function(e, res) {
this.defer.resolve(res)
}
let isLocal = false
try {
isLocal = rlocalProtocol.test(location.ptyperotocol)
} catch (e) {}
let originAnchor = document.createElement('a')
originAnchor.href = location.href
const NOBODY_METHODS = ['GET', 'HEAD']
const ERRORS = {
10001: 'Argument url is required',
10012: 'Parse error',
10100: 'Request canceled',
10104: 'Request pending...',
10200: 'Ok',
10204: 'No content',
10304: 'Not modified',
10500: 'Internal Server Error',
10504: 'Connected timeout'
}
const FORM_TYPES = {
form: 'application/x-www-form-urlencoded; charset=UTF-8',
json: 'application/json; charset=UTF-8',
text: 'text/plain; charset=UTF-8'
}
const convert = {
text(val) {
return val
},
xml(val, xml) {
return xml !== undefined ? xml : Format.parseXML(val)
},
html(val) {
return Format.parseHTML(val)
},
json(val) {
return JSON.parse(val)
},
script(val) {
return Format.parseJS(val)
}
}
class _Request {
constructor(url = '', method = 'GET', param = {}) {
if (!url) {
throw new Error(ERRORS[10001])
}
// url规范化
url = url.replace(/#.*$/, '').replace(/^\/\//, location.protocol + '//')
method = method.toUpperCase()
this.xhr = new XMLHttpRequest()
this.defer = Promise.defer()
this.opt = {
url,
method,
headers: {},
data: {},
dataType: 'text',
withCredentials: false // 跨域选项,是否验证凭证
}
// 取消网络请求
this.defer.promise.abort = () => {
this.cancel = true
this.xhr.abort()
}
this.__next__(Object.assign({}, request.__INIT__, param))
return this.defer.promise
}
__next__(param) {
/* -------------------------------------------------------------- */
/* ------------------------ 1»» 配置头信息 ---------------------- */
/* -------------------------------------------------------------- */
if (param.headers) {
Object.assign(this.opt.headers, param.headers)
}
/* -------------------------------------------------------------- */
/* --------- 2»» 设置表单类型, 其中 form-data不能手动设置 ---------- */
/* -------------------------------------------------------------- */
let hasAttach = false
if (param.formType) {
switch (param.formType) {
case 'form':
this.__set__('form')
break
case 'json':
this.__set__('json')
break
case 'form-data':
this.opt.method = 'POST'
hasAttach = true
break
default:
if (NOBODY_METHODS.includes(this.opt.method)) {
this.__set__('form')
} else {
this.__set__('text')
}
}
} else {
this.__set__('form')
}
/* -------------------------------------------------------------- */
/* ------------------- 3»» 设置缓存 ---------------------------- */
/* -------------------------------------------------------------- */
if (param.cache) {
if (NOBODY_METHODS.includes(this.opt.method)) {
this.opt.cache = true
}
}
/* -------------------------------------------------------------- */
/* ------------------- 4»» 设置超时时间(毫秒) --------------------- */
/* -------------------------------------------------------------- */
param.timeout = param.timeout >>> 0
if (param.timeout > 0) {
this.opt.timeout = param.timeout
}
/* -------------------------------------------------------------- */
/* -------------------------- 5»» 请求的内容 --------------------- */
/* -------------------------------------------------------------- */
if (param.data) {
let type = typeof param.data
switch (type) {
case 'number':
case 'string':
this.__set__('text')
this.opt.data = param.data
break
case 'object':
// 解析表单DOM
if (param.data.nodeName === 'FORM') {
this.opt.method = param.data.method.toUpperCase() || 'POST'
this.opt.data = Format.parseForm(param.data)
hasAttach = this.opt.data.constructor === FormData
if (hasAttach) {
delete this.opt.headers['content-type']
}
// 如果是一个 FormData对象
// 则直接改为POST
} else if (param.data.constructor === FormData) {
hasAttach = true
this.opt.method = 'POST'
delete this.opt.headers['content-type']
this.opt.data = param.data
} else {
// 有附件,则改为FormData
if (hasAttach) {
this.opt.data = Format.mkFormData(param.data)
} else {
this.opt.data = param.data
}
}
}
}
/* -------------------------------------------------------------- */
/* -------------------------- 6»» 处理跨域 --------------------- */
/* -------------------------------------------------------------- */
if (param.withCredentials) {
this.opt.withCredentials = true
}
try {
let anchor = document.createElement('a')
anchor.href = this.opt.url
this.opt.crossDomain =
originAnchor.protocol !== anchor.protocol ||
originAnchor.host !== anchor.host
} catch (err) {}
// 6.1»» 进一步处理跨域
// 非跨域或跨域但支持Cors时自动加上一条header信息用以标识这是ajax请求
// 如果是跨域,开启Cors会需要服务端额外返回一些headers
if (this.opt.crossDomain) {
if (this.opt.withCredentials) {
this.xhr.withCredentials = true
this.opt.headers['X-Requested-With'] = 'XMLHttpRequest'
}
} else {
this.opt.headers['X-Requested-With'] = 'XMLHttpRequest'
}
/* -------------------------------------------------------------- */
/* ------------- 7»» 根据method类型, 处理g表单数据 ---------------- */
/* -------------------------------------------------------------- */
// 是否允许发送body
let allowBody = !NOBODY_METHODS.includes(this.opt.method)
if (allowBody) {
if (!hasAttach) {
if (param.formType === 'json') {
this.opt.data = JSON.stringify(this.opt.data)
} else {
this.opt.data = Format.param(this.opt.data)
}
}
} else {
// 否则拼接到url上
this.opt.data = Format.param(this.opt.data)
this.opt.url += (/\?/.test(this.opt.url) ? '&' : '?') + this.opt.data
if (this.opt.cache === false) {
this.opt.url +=
(/\?/.test(this.opt.url) ? '&' : '?') + '_=' + Math.random()
}
}
/* -------------------------------------------------------------- */
/* ------------- 8»» 设置响应的数据类型 ---------------- */
/* -------------------------------------------------------------- */
// arraybuffer | blob | document | json | text
if (param.dataType) {
this.opt.dataType = param.dataType.toLowerCase()
}
this.xhr.responseType = this.opt.dataType
/* -------------------------------------------------------------- */
/* ------------- 9»» 构造请求 ---------------- */
/* -------------------------------------------------------------- */
// response ready
this.xhr.onreadystatechange = ev => {
if (this.opt.timeout > 0) {
this.opt['time' + this.xhr.readyState] = ev.timeStamp
if (this.xhr.readyState === 4) {
this.opt.isTimeout =
this.opt.time4 - this.opt.time1 > this.opt.timeout
}
}
if (this.xhr.readyState !== 4) {
return
}
this.__dispatch__(this.opt.isTimeout)
}
// 9.1»» 初始化xhr
this.xhr.open(this.opt.method, this.opt.url, true)
// 9.2»» 设置头信息
for (let i in this.opt.headers) {
this.xhr.setRequestHeader(i, this.opt.headers[i])
}
// 9.3»» 发起网络请求
this.xhr.send(this.opt.data)
// 9.4»» 超时处理
if (this.opt.timeout && this.opt.timeout > 0) {
this.xhr.timeout = this.opt.timeout
}
}
__set__(type) {
this.opt.headers['content-type'] = FORM_TYPES[type]
}
__dispatch__(isTimeout) {
let result = {
status: 200,
statusText: 'ok',
text: '',
body: '',
error: null
}
// 主动取消
if (this.cancel) {
return this.__cancel__(result)
}
// 超时
if (isTimeout) {
return this.__timeout__(result)
}
// 是否请求成功(resful规范)
let isSucc = this.xhr.status >= 200 && this.xhr.status < 400
let headers = this.xhr.getAllResponseHeaders().split('\n') || []
let contentType = ''
//处理返回的 Header, 拿到content-type
for (let it of headers) {
it = it.trim()
if (it) {
it = it.split(':')
let tmp = it.shift().toLowerCase()
if (tmp === 'content-type') {
contentType = it
.join(':')
.trim()
.toLowerCase()
break
}
}
}
if (isSucc) {
result.status = this.xhr.status
if (result.status === 204) {
result.statusText = ERRORS[10204]
} else if (result.status === 304) {
result.statusText = ERRORS[10304]
}
} else {
result.status = this.xhr.status || 500
result.statusText = this.xhr.statusText || ERRORS[10500]
result.error = new Error(result.statusText)
}
// log(this.opt.dataType, this.xhr)
switch (this.opt.dataType) {
case 'arraybuffer':
case 'blob':
case 'document':
case 'json':
result.text = result.body = this.xhr.response
break
// text
default:
try {
//处理返回的数据
let dataType = contentType.match(/json|xml|script|html/)
dataType = (dataType && dataType[0].toLowerCase()) || 'text'
result.text = this.xhr.response
result.body = convert[dataType](result.text, this.xhr.response)
} catch (err) {
result.error = err
result.statusText = ERRORS[10012]
}
break
}
this.__success__(isSucc, result)
}
__success__(isSucc, result) {
if (isSucc) {
this.defer.resolve(result)
} else {
this.defer.reject(result)
}
delete this.xhr
delete this.opt
delete this.defer
}
__cancel__(result) {
result.status = 0
result.statusText = ERRORS[10100]
result.error = new Error(ERRORS[10100])
this.defer.reject(result)
delete this.xhr
delete this.opt
delete this.defer
}
__timeout__(result) {
result.status = 504
result.statusText = ERRORS[10504]
result.error = new Error(ERRORS[10504])
this.defer.reject(result)
delete this.xhr
delete this.opt
delete this.defer
}
}
if (!window.request) {
window.request = {
get(url, param = {}) {
return new _Request(url, 'GET', param)
},
post(url, param = {}) {
return new _Request(url, 'POST', param)
},
upload(url, param = {}) {
param.formType = 'form-data'
return this.post(url, param)
},
download(url, param = {}) {
param.dataType = 'blob'
return this.get(url, param)
},
open(url, method = 'GET', param = {}) {
if (typeof method === 'object') {
param = method
method = 'GET'
}
return new _Request(url, method, param)
},
version: '2.0.0-normal',
init(param = {}) {
this.__INIT__ = param
}
}
Anot.ui.request = request.version
}
export default request
wcui是一套基于`Web Components`的UI组件库, 宗旨是追求简单、实用、不花哨。
JavaScript 95.2%
CSS 4.8%