update request to 2.x
parent
2fb181bcc2
commit
29cf4e7ace
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
import Format from './lib/format'
|
import Format from './lib/format'
|
||||||
|
import format from './lib/format'
|
||||||
|
|
||||||
// 本地协议/头 判断正则
|
// 本地协议/头 判断正则
|
||||||
const rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/
|
const rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/
|
||||||
|
@ -24,8 +25,8 @@ try {
|
||||||
let originAnchor = document.createElement('a')
|
let originAnchor = document.createElement('a')
|
||||||
originAnchor.href = location.href
|
originAnchor.href = location.href
|
||||||
|
|
||||||
const NOBODY_METHODS = ['GET', 'HEAD', 'JSONP']
|
const NOBODY_METHODS = ['GET', 'HEAD']
|
||||||
const ERROR = {
|
const ERRORS = {
|
||||||
10001: 'argument url is required',
|
10001: 'argument url is required',
|
||||||
10011: 'Promise required a callback',
|
10011: 'Promise required a callback',
|
||||||
10012: 'Parse error',
|
10012: 'Parse error',
|
||||||
|
@ -58,18 +59,13 @@ const convert = {
|
||||||
},
|
},
|
||||||
script(val) {
|
script(val) {
|
||||||
return Format.parseJS(val)
|
return Format.parseJS(val)
|
||||||
},
|
|
||||||
jsonp(name) {
|
|
||||||
var json = request.cache[name]
|
|
||||||
delete request.cache[name]
|
|
||||||
return json
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Request {
|
class _Request {
|
||||||
constructor(url = '', method = 'GET', param = {}) {
|
constructor(url = '', method = 'GET', param = {}) {
|
||||||
if (!url) {
|
if (!url) {
|
||||||
throw new Error(error[10001])
|
throw new Error(ERRORS[10001])
|
||||||
}
|
}
|
||||||
|
|
||||||
// url规范化
|
// url规范化
|
||||||
|
@ -77,19 +73,13 @@ class _Request {
|
||||||
|
|
||||||
method = method.toUpperCase()
|
method = method.toUpperCase()
|
||||||
|
|
||||||
this.transport = Object.create(null)
|
|
||||||
this.xhr = new XMLHttpRequest()
|
this.xhr = new XMLHttpRequest()
|
||||||
this.defer = Promise.defer()
|
this.defer = Promise.defer()
|
||||||
this.opt = {
|
this.opt = {
|
||||||
url,
|
url,
|
||||||
method,
|
method,
|
||||||
form: null,
|
|
||||||
data: {},
|
data: {},
|
||||||
headers: {},
|
headers: {}
|
||||||
timeoutID: 0,
|
|
||||||
uuid: Math.random()
|
|
||||||
.toString(16)
|
|
||||||
.slice(2)
|
|
||||||
}
|
}
|
||||||
return this.__open__(param)
|
return this.__open__(param)
|
||||||
}
|
}
|
||||||
|
@ -151,32 +141,25 @@ class _Request {
|
||||||
case 'object':
|
case 'object':
|
||||||
// 解析表单DOM
|
// 解析表单DOM
|
||||||
if (param.data.nodeName === 'FORM') {
|
if (param.data.nodeName === 'FORM') {
|
||||||
hasAttach = true
|
this.opt.method = param.data.method.toUpperCase() || 'POST'
|
||||||
this.opt.method = 'POST'
|
|
||||||
|
this.opt.data = Format.parseForm(param.data)
|
||||||
|
hasAttach = this.opt.data.constructor === FormData
|
||||||
|
|
||||||
|
if (hasAttach) {
|
||||||
delete this.opt.headers['content-type']
|
delete this.opt.headers['content-type']
|
||||||
this.opt.data = this.__parseForm__(param.data)
|
}
|
||||||
} else if (param.data.constructor === FormData) {
|
|
||||||
hasAttach = true
|
|
||||||
// 如果是一个 FormData对象
|
// 如果是一个 FormData对象
|
||||||
// 则直接改为POST
|
// 则直接改为POST
|
||||||
|
} else if (param.data.constructor === FormData) {
|
||||||
|
hasAttach = true
|
||||||
this.opt.method = 'POST'
|
this.opt.method = 'POST'
|
||||||
delete this.opt.headers['content-type']
|
delete this.opt.headers['content-type']
|
||||||
this.opt.data = param.data
|
this.opt.data = param.data
|
||||||
} else {
|
} else {
|
||||||
// 有附件,则改为FormData
|
// 有附件,则改为FormData
|
||||||
if (hasAttach) {
|
if (hasAttach) {
|
||||||
let form = new FormData()
|
this.opt.data = Format.mkFormData(param.data)
|
||||||
for (let i in param.data) {
|
|
||||||
let el = param.data[i]
|
|
||||||
if (Array.isArray(el)) {
|
|
||||||
el.forEach(function(it) {
|
|
||||||
form.append(i + '[]', it)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
form.append(i, param.data[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.opt.data = form
|
|
||||||
} else {
|
} else {
|
||||||
this.opt.data = param.data
|
this.opt.data = param.data
|
||||||
}
|
}
|
||||||
|
@ -196,25 +179,14 @@ class _Request {
|
||||||
this.opt.crossDomain = true
|
this.opt.crossDomain = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6.1»» 进一步处理跨域
|
// 6.1»»
|
||||||
if (this.opt.method === 'JSONP') {
|
// 自动加上一条header信息,用以标识这是ajax请求
|
||||||
// 如果非跨域,则转回 xhr GET
|
|
||||||
if (this.opt.crossDomain) {
|
|
||||||
this.opt.data.callback =
|
|
||||||
this.opt.data.callback || `jsonp${request.cid++}`
|
|
||||||
} else {
|
|
||||||
this.opt.method = 'GET'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6.2»»
|
|
||||||
// 如果不是JSONP,则自动加上一条header信息,用以标识这是ajax请求
|
|
||||||
// 如果是跨域,在支持Cors时, 自动加上支持(这一步会需要服务端额外返回一些headers)
|
// 如果是跨域,在支持Cors时, 自动加上支持(这一步会需要服务端额外返回一些headers)
|
||||||
if (this.opt.method !== 'JSONP') {
|
|
||||||
this.opt.headers['X-Requested-With'] = 'XMLHttpRequest'
|
this.opt.headers['X-Requested-With'] = 'XMLHttpRequest'
|
||||||
}
|
|
||||||
if (this.opt.crossDomain) {
|
if (this.opt.crossDomain) {
|
||||||
supportCors && (this.xhr.withCredentials = true)
|
this.xhr.withCredentials = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7»» 根据method类型, 处理g表单数据
|
// 7»» 根据method类型, 处理g表单数据
|
||||||
|
@ -222,14 +194,62 @@ class _Request {
|
||||||
// 是否允许发送body
|
// 是否允许发送body
|
||||||
let allowBody = !NOBODY_METHODS.includes(this.opt.method)
|
let allowBody = !NOBODY_METHODS.includes(this.opt.method)
|
||||||
if (allowBody) {
|
if (allowBody) {
|
||||||
if (!hasAttach && typeof this.opt.data === 'object') {
|
if (!hasAttach) {
|
||||||
|
if (param.formType === 'json') {
|
||||||
this.opt.data = JSON.stringify(this.opt.data)
|
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»» 构造请求
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.1»» 初始化xhr
|
||||||
|
this.xhr.open(this.opt.method, this.opt.url, true)
|
||||||
|
|
||||||
|
// 8.2»» 设置头信息
|
||||||
|
for (var i in this.opt.headers) {
|
||||||
|
this.xhr.setRequestHeader(i, this.opt.headers[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8.3»» 发起网络请求
|
||||||
|
this.xhr.send(this.opt.data)
|
||||||
|
|
||||||
|
// 8.4»» 超时处理
|
||||||
|
if (this.opt.timeout && this.opt.timeout > 0) {
|
||||||
|
this.xhr.timeout = this.opt.timeout
|
||||||
|
}
|
||||||
|
|
||||||
// 取消网络请求
|
// 取消网络请求
|
||||||
this.opt.abort = () => {
|
this.opt.abort = () => {
|
||||||
delete this.transport
|
delete this.xhr
|
||||||
if (!this.opt.form) {
|
if (!this.opt.form) {
|
||||||
this.xhr.abort()
|
this.xhr.abort()
|
||||||
}
|
}
|
||||||
|
@ -237,7 +257,7 @@ class _Request {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
this.defer.resolve(this.opt)
|
// this.defer.resolve(this.opt)
|
||||||
return this.defer.promise
|
return this.defer.promise
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,11 +265,83 @@ class _Request {
|
||||||
this.opt.headers['content-type'] = FORM_TYPES[type]
|
this.opt.headers['content-type'] = FORM_TYPES[type]
|
||||||
}
|
}
|
||||||
|
|
||||||
_jsonp(cb) {
|
__dispatch__(isTimeout) {
|
||||||
window[cb] = function(val) {
|
let result = {
|
||||||
delete window[cb]
|
response: {
|
||||||
request.cache[cb] = val
|
url: this.opt.url,
|
||||||
|
headers: { 'content-type': '' }
|
||||||
|
},
|
||||||
|
request: {
|
||||||
|
url: this.opt.url,
|
||||||
|
headers: this.opt.headers
|
||||||
|
},
|
||||||
|
status: isTimeout === null ? 504 : 200,
|
||||||
|
statusText: isTimeout === null ? 'Connected timeout' : 'ok',
|
||||||
|
text: '',
|
||||||
|
body: '',
|
||||||
|
error: null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//成功的回调
|
||||||
|
let isSucc = isTimeout
|
||||||
|
? false
|
||||||
|
: this.xhr.status >= 200 && this.xhr.status < 400
|
||||||
|
|
||||||
|
let headers =
|
||||||
|
(!isTimeout && this.xhr.getAllResponseHeaders().split('\n')) || []
|
||||||
|
|
||||||
|
//处理返回的Header
|
||||||
|
headers.forEach(function(it, i) {
|
||||||
|
it = it.trim()
|
||||||
|
if (it) {
|
||||||
|
it = it.split(':')
|
||||||
|
result.response.headers[it.shift().toLowerCase()] = it.join(':').trim()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
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 = isTimeout ? 504 : this.xhr.status || 500
|
||||||
|
result.statusText = isTimeout
|
||||||
|
? ERRORS[10504]
|
||||||
|
: this.xhr.statusText || ERRORS[10500]
|
||||||
|
result.error = new Error(result.statusText)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
//处理返回的数据
|
||||||
|
var dataType = result.response.headers['content-type'].match(
|
||||||
|
/json|xml|script|html/i
|
||||||
|
) || ['text']
|
||||||
|
|
||||||
|
dataType = dataType[0].toLowerCase()
|
||||||
|
result.text = isTimeout
|
||||||
|
? ''
|
||||||
|
: this.xhr.responseText || this.xhr.responseXML
|
||||||
|
|
||||||
|
result.body = convert[dataType](
|
||||||
|
result.text,
|
||||||
|
!isTimeout && this.xhr.responseXML
|
||||||
|
)
|
||||||
|
} catch (err) {
|
||||||
|
result.error = err
|
||||||
|
result.statusText = ERRORS[10012]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.status >= 200 && result.status < 400) {
|
||||||
|
this.defer.resolve(result)
|
||||||
|
} else {
|
||||||
|
this.defer.reject(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this.opt
|
||||||
|
delete this.xhr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,14 +357,9 @@ if (!window.request) {
|
||||||
param.formType = 'form-data'
|
param.formType = 'form-data'
|
||||||
return this.post(url, param)
|
return this.post(url, param)
|
||||||
},
|
},
|
||||||
jsonp(url, param) {
|
|
||||||
return new _Request(url, 'JSONP', param)
|
|
||||||
},
|
|
||||||
open(url, method = 'GET', param) {
|
open(url, method = 'GET', param) {
|
||||||
return new _Request(url, method, param)
|
return new _Request(url, method, param)
|
||||||
},
|
},
|
||||||
cache: {},
|
|
||||||
cid: 0,
|
|
||||||
version: '2.0.0-normal'
|
version: '2.0.0-normal'
|
||||||
}
|
}
|
||||||
Anot.ui.request = request.version
|
Anot.ui.request = request.version
|
||||||
|
|
|
@ -142,6 +142,63 @@ export default {
|
||||||
|
|
||||||
return fragment
|
return fragment
|
||||||
},
|
},
|
||||||
|
parseForm: function(form) {
|
||||||
|
let data = {}
|
||||||
|
let hasAttach = false
|
||||||
|
for (let i = 0, field; (field = form.elements[i++]); ) {
|
||||||
|
switch (field.type) {
|
||||||
|
case 'select-one':
|
||||||
|
case 'select-multiple':
|
||||||
|
if (field.name.length && !field.disabled) {
|
||||||
|
for (let j = 0, opt; (opt = field.options[j++]); ) {
|
||||||
|
if (opt.selected) {
|
||||||
|
data[field.name] = opt.value || opt.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'file':
|
||||||
|
if (field.name.length && !field.disabled) {
|
||||||
|
data[field.name] = field.files[0]
|
||||||
|
hasAttach = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case undefined:
|
||||||
|
case 'submit':
|
||||||
|
case 'reset':
|
||||||
|
case 'button':
|
||||||
|
break //按钮啥的, 直接忽略
|
||||||
|
case 'radio':
|
||||||
|
case 'checkbox':
|
||||||
|
// 只处理选中的
|
||||||
|
if (!field.checked) break
|
||||||
|
default:
|
||||||
|
if (field.name.length && !field.disabled) {
|
||||||
|
data[field.name] = field.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果有附件, 改为FormData
|
||||||
|
if (hasAttach) {
|
||||||
|
return this.mkFormData(data)
|
||||||
|
} else {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mkFormData(data) {
|
||||||
|
let form = new FormData()
|
||||||
|
for (let i in data) {
|
||||||
|
let el = data[i]
|
||||||
|
if (Array.isArray(el)) {
|
||||||
|
el.forEach(function(it) {
|
||||||
|
form.append(i + '[]', it)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
form.append(i, data[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return form
|
||||||
|
},
|
||||||
param: function(obj) {
|
param: function(obj) {
|
||||||
if (!obj || typeof obj === 'string' || typeof obj === 'number') {
|
if (!obj || typeof obj === 'string' || typeof obj === 'number') {
|
||||||
return obj
|
return obj
|
||||||
|
@ -164,41 +221,5 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
return arr.join('&')
|
return arr.join('&')
|
||||||
},
|
|
||||||
parseForm: function(form) {
|
|
||||||
let data = {}
|
|
||||||
for (let i = 0, field; (field = form.elements[i++]); ) {
|
|
||||||
switch (field.type) {
|
|
||||||
case 'select-one':
|
|
||||||
case 'select-multiple':
|
|
||||||
if (field.name.length && !field.disabled) {
|
|
||||||
for (let j = 0, opt; (opt = field.options[j++]); ) {
|
|
||||||
if (opt.selected) {
|
|
||||||
data[field.name] = opt.value || opt.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'file':
|
|
||||||
if (field.name.length && !field.disabled) {
|
|
||||||
data[field.name] = field.files[0]
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case undefined:
|
|
||||||
case 'submit':
|
|
||||||
case 'reset':
|
|
||||||
case 'button':
|
|
||||||
break //按钮啥的, 直接忽略
|
|
||||||
case 'radio':
|
|
||||||
case 'checkbox':
|
|
||||||
// 只处理选中的
|
|
||||||
if (!field.checked) break
|
|
||||||
default:
|
|
||||||
if (field.name.length && !field.disabled) {
|
|
||||||
data[field.name] = field.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue