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

update request to 2.x

old
宇天 2019-02-21 00:00:42 +08:00
parent 2fb181bcc2
commit 29cf4e7ace
2 changed files with 207 additions and 99 deletions

View File

@ -7,6 +7,7 @@
'use strict'
import Format from './lib/format'
import format from './lib/format'
// 本地协议/头 判断正则
const rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/
@ -24,8 +25,8 @@ try {
let originAnchor = document.createElement('a')
originAnchor.href = location.href
const NOBODY_METHODS = ['GET', 'HEAD', 'JSONP']
const ERROR = {
const NOBODY_METHODS = ['GET', 'HEAD']
const ERRORS = {
10001: 'argument url is required',
10011: 'Promise required a callback',
10012: 'Parse error',
@ -58,18 +59,13 @@ const convert = {
},
script(val) {
return Format.parseJS(val)
},
jsonp(name) {
var json = request.cache[name]
delete request.cache[name]
return json
}
}
class _Request {
constructor(url = '', method = 'GET', param = {}) {
if (!url) {
throw new Error(error[10001])
throw new Error(ERRORS[10001])
}
// url规范化
@ -77,19 +73,13 @@ class _Request {
method = method.toUpperCase()
this.transport = Object.create(null)
this.xhr = new XMLHttpRequest()
this.defer = Promise.defer()
this.opt = {
url,
method,
form: null,
data: {},
headers: {},
timeoutID: 0,
uuid: Math.random()
.toString(16)
.slice(2)
headers: {}
}
return this.__open__(param)
}
@ -151,32 +141,25 @@ class _Request {
case 'object':
// 解析表单DOM
if (param.data.nodeName === 'FORM') {
hasAttach = true
this.opt.method = 'POST'
delete this.opt.headers['content-type']
this.opt.data = this.__parseForm__(param.data)
} else if (param.data.constructor === FormData) {
hasAttach = true
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) {
let form = new FormData()
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
this.opt.data = Format.mkFormData(param.data)
} else {
this.opt.data = param.data
}
@ -196,25 +179,14 @@ class _Request {
this.opt.crossDomain = true
}
// 6.1»» 进一步处理跨域
if (this.opt.method === 'JSONP') {
// 如果非跨域,则转回 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请求
// 6.1»»
// 自动加上一条header信息用以标识这是ajax请求
// 如果是跨域,在支持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) {
supportCors && (this.xhr.withCredentials = true)
this.xhr.withCredentials = true
}
// 7»» 根据method类型, 处理g表单数据
@ -222,14 +194,62 @@ class _Request {
// 是否允许发送body
let allowBody = !NOBODY_METHODS.includes(this.opt.method)
if (allowBody) {
if (!hasAttach && typeof this.opt.data === 'object') {
this.opt.data = JSON.stringify(this.opt.data)
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»» 构造请求
// 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 = () => {
delete this.transport
delete this.xhr
if (!this.opt.form) {
this.xhr.abort()
}
@ -237,7 +257,7 @@ class _Request {
return this
}
this.defer.resolve(this.opt)
// this.defer.resolve(this.opt)
return this.defer.promise
}
@ -245,11 +265,83 @@ class _Request {
this.opt.headers['content-type'] = FORM_TYPES[type]
}
_jsonp(cb) {
window[cb] = function(val) {
delete window[cb]
request.cache[cb] = val
__dispatch__(isTimeout) {
let result = {
response: {
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'
return this.post(url, param)
},
jsonp(url, param) {
return new _Request(url, 'JSONP', param)
},
open(url, method = 'GET', param) {
return new _Request(url, method, param)
},
cache: {},
cid: 0,
version: '2.0.0-normal'
}
Anot.ui.request = request.version

View File

@ -142,6 +142,63 @@ export default {
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) {
if (!obj || typeof obj === 'string' || typeof obj === 'number') {
return obj
@ -164,41 +221,5 @@ export default {
}
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
}
}