Compare commits

..

6 Commits

Author SHA1 Message Date
yutent f134e367b4 超时增加reson参数 2025-01-07 10:43:13 +08:00
yutent 763792d7f1 fixed undefined 2024-10-28 17:16:12 +08:00
yutent 5b403fb269 优化表单类型的判断 2024-07-15 15:26:05 +08:00
yutent 33e4ffd659 修复readme语法 2024-05-06 18:25:59 +08:00
yutent c286cdf2f2 优化表单解析, 支持多文件数组上传 2023-06-25 19:10:25 +08:00
yutent 4d97503ee0 优化content-type判断 2023-02-17 17:08:53 +08:00
7 changed files with 94 additions and 58 deletions

3
.gitignore vendored
View File

@ -1,6 +1,9 @@
*.min.js *.min.js
*.min.css *.min.css
demo.html
.httpserver
node_modules/ node_modules/
dist/ dist/

View File

@ -55,7 +55,7 @@ f1('/get_list', {body: {page: 1}})
### APIs ### APIs
#### 1. fetch(url[, options<Object>]) #### 1. fetch(url[, options`<Object>`])
> 发起一个网络请求, options的参数如下。 同时支持配置公共域名, 公共参数。 > 发起一个网络请求, options的参数如下。 同时支持配置公共域名, 公共参数。
+ method`<String>` 默认GET, 可选GET/POST/PUT/DELETE... + method`<String>` 默认GET, 可选GET/POST/PUT/DELETE...

View File

@ -55,7 +55,7 @@ f1('/get_list', {body: {page: 1}})
### APIs ### APIs
#### 1. fetch(url[, options<Object>]) #### 1. fetch(url[, options`<Object>`])
> 发起一个网络请求, options的参数如下。 同时支持配置公共域名, 公共参数。 > 发起一个网络请求, options的参数如下。 同时支持配置公共域名, 公共参数。
+ method`<String>` 默认GET, 可选GET/POST/PUT/DELETE... + method`<String>` 默认GET, 可选GET/POST/PUT/DELETE...

View File

@ -1,8 +1,8 @@
{ {
"name": "@bytedo/fetch", "name": "@bytedo/fetch",
"version": "2.1.3", "version": "2.1.9",
"description": "全新的ajax封装。分2个版本, 一个基于XMLHttpRequest, 一个基于window.fetch", "description": "全新的ajax封装。分2个版本, 一个基于XMLHttpRequest, 一个基于window.fetch",
"main": "dist/index.js", "main": "dist/next.js",
"files": [ "files": [
"dist/*" "dist/*"
], ],

View File

@ -4,7 +4,7 @@
* @date 2020/08/03 17:05:10 * @date 2020/08/03 17:05:10
*/ */
import { Format, toS } from './lib/format.js' import { Format, getType } from './lib/format.js'
const NOBODY_METHODS = ['GET', 'HEAD'] const NOBODY_METHODS = ['GET', 'HEAD']
const FORM_TYPES = { const FORM_TYPES = {
@ -25,7 +25,7 @@ const ERRORS = {
} }
Promise.defer = function () { Promise.defer = function () {
var _ = {} let _ = {}
_.promise = new Promise(function (y, n) { _.promise = new Promise(function (y, n) {
_.resolve = y _.resolve = y
_.reject = n _.reject = n
@ -66,7 +66,7 @@ class _Request {
} }
if (!options.signal) { if (!options.signal) {
var control = new AbortController() let control = new AbortController()
options.signal = control.signal options.signal = control.signal
} }
this.defer.promise.abort = function () { this.defer.promise.abort = function () {
@ -90,10 +90,10 @@ class _Request {
} }
__next__() { __next__() {
var options = this.options let options = this.options
var params = null let params = null
var hasAttach = false // 是否有附件 let hasAttach = false // 是否有附件
var noBody = NOBODY_METHODS.includes(options.method) let noBody = NOBODY_METHODS.includes(options.method)
/* ------------------------ 1»» 处理signal ---------------------- */ /* ------------------------ 1»» 处理signal ---------------------- */
options.signal.onabort = _ => { options.signal.onabort = _ => {
@ -103,7 +103,7 @@ class _Request {
/* -------------------------- 2»» 请求的内容 --------------------- */ /* -------------------------- 2»» 请求的内容 --------------------- */
if (options.body) { if (options.body) {
var type = typeof options.body let type = typeof options.body
switch (type) { switch (type) {
case 'number': case 'number':
case 'string': case 'string':
@ -111,6 +111,11 @@ class _Request {
params = options.body params = options.body
break break
case 'object': case 'object':
let _type = getType(options.body)
if (_type === 'ArrayBuffer' || _type === 'Uint8Array') {
break
}
// 解析表单DOM // 解析表单DOM
if (options.body.nodeName === 'FORM') { if (options.body.nodeName === 'FORM') {
options.method = options.body.method.toUpperCase() || 'POST' options.method = options.body.method.toUpperCase() || 'POST'
@ -127,14 +132,19 @@ class _Request {
params = options.body params = options.body
} else { } else {
for (let k in options.body) { for (let k in options.body) {
if ( if (Array.isArray(options.body[k]) || getType(options.body[k]) === 'FileList') {
toS.call(options.body[k]) === '[object File]' || options.body[k] = Array.from(options.body[k])
toS.call(options.body[k]) === '[object Blob]' hasAttach = options.body[k].some(
) { it => getType(it) === 'File' || getType(it) === 'Blob'
)
} else {
if (getType(options.body[k]) === 'File' || getType(options.body[k]) === 'Blob') {
hasAttach = true hasAttach = true
break break
} }
} }
}
// 有附件,则改为FormData // 有附件,则改为FormData
if (hasAttach) { if (hasAttach) {
if (noBody) { if (noBody) {
@ -176,16 +186,17 @@ class _Request {
if (noBody) { if (noBody) {
params = Format.param(params) params = Format.param(params)
if (params) { if (params) {
options.url += (~options.url.indexOf('?') ? '&' : '?') + params options.url += (options.url.includes('?') ? '&' : '?') + params
} }
if (options.cache === 'no-store') { if (options.cache === 'no-store') {
options.url += (~options.url.indexOf('?') ? '&' : '?') + '_t_=' + Date.now() options.url += (options.url.includes('?') ? '&' : '?') + '_t_=' + Date.now()
} }
} else { } else {
if (!hasAttach) { if (options.body && !hasAttach) {
if (~options.headers['content-type'].indexOf('json')) { if (options.headers['content-type'].includes('json')) {
params = JSON.stringify(params) params = JSON.stringify(params)
} else { }
if (options.headers['content-type'].includes('form')) {
params = Format.param(params) params = Format.param(params)
} }
} }
@ -283,19 +294,19 @@ class _Request {
} }
__success__(isSucc, result) { __success__(isSucc, result) {
var { body, status, statusText, headers } = result let { body, status, statusText, headers } = result
var response = new Response(body, { status, statusText, headers }) let response = new Response(body, { status, statusText, headers })
var _type let _type
if (this._owner._inject_res) { if (this._owner._inject_res) {
response = this._owner._inject_res(response) response = this._owner._inject_res(response)
_type = toS.call(response) _type = getType(it)(response)
} }
if (isSucc) { if (isSucc) {
this.defer.resolve(response) this.defer.resolve(response)
} else { } else {
if (_type === '[object Promise]') { if (_type === 'Promise') {
return response.then(_ => this.defer.reject(_)).catch(_ => this.defer.reject(_)) return response.then(_ => this.defer.reject(_)).catch(_ => this.defer.reject(_))
} else { } else {
this.defer.reject(response) this.defer.reject(response)
@ -307,7 +318,7 @@ class _Request {
} }
__cancel__(result) { __cancel__(result) {
var response = new Response('', { let response = new Response('', {
status: 0, status: 0,
statusText: ERRORS[10100] statusText: ERRORS[10100]
}) })
@ -320,7 +331,7 @@ class _Request {
} }
__timeout__(result) { __timeout__(result) {
var response = new Response('', { let response = new Response('', {
status: 504, status: 504,
statusText: ERRORS[10504] statusText: ERRORS[10504]
}) })
@ -348,7 +359,7 @@ const _fetch = function (url, options) {
} }
_fetch.create = function () { _fetch.create = function () {
var another = function (url, options) { let another = function (url, options) {
return new _Request(url, options, another) return new _Request(url, options, another)
} }
inject(another) inject(another)

View File

@ -5,15 +5,17 @@
* *
*/ */
export const toS = Object.prototype.toString
export const encode = encodeURIComponent export const encode = encodeURIComponent
export const decode = decodeURIComponent export const decode = decodeURIComponent
export function getType(val) {
return Object.prototype.toString.call(val).slice(8, -1)
}
/** /**
* 表单序列化 * 表单序列化
*/ */
function serialize(p, obj, query) { function serialize(p, obj, query) {
var k let k
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
obj.forEach(function (it, i) { obj.forEach(function (it, i) {
k = p ? `${p}[${Array.isArray(it) ? i : ''}]` : i k = p ? `${p}[${Array.isArray(it) ? i : ''}]` : i
@ -82,13 +84,17 @@ export const Format = {
mkFormData(data) { mkFormData(data) {
let form = new FormData() let form = new FormData()
for (let i in data) { for (let i in data) {
let el = data[i] let val = data[i]
if (Array.isArray(el)) { if (val === void 0) {
el.forEach(function (it) { val = ''
}
if (Array.isArray(val)) {
val.forEach(function (it) {
form.append(i + '[]', it) form.append(i + '[]', it)
}) })
} else { } else {
form.append(i, data[i]) form.append(i, val)
} }
} }
return form return form
@ -104,8 +110,14 @@ export const Format = {
return return
} }
if (v === void 0) {
v = ''
}
let _type = getType(v)
v = typeof v === 'function' ? v() : v v = typeof v === 'function' ? v() : v
v = toS.call(v) === '[object File]' || toS.call(v) === '[object Blob]' ? v : encode(v) v = _type === 'File' || _type === 'Blob' ? v : encode(v)
arr.push(encode(k) + '=' + v) arr.push(encode(k) + '=' + v)
} }

View File

@ -4,7 +4,7 @@
* @date 2020/07/31 18:59:47 * @date 2020/07/31 18:59:47
*/ */
import { Format, toS } from './lib/format.js' import { Format, getType } from './lib/format.js'
const nativeFetch = window.fetch const nativeFetch = window.fetch
const NOBODY_METHODS = ['GET', 'HEAD'] const NOBODY_METHODS = ['GET', 'HEAD']
@ -75,19 +75,25 @@ class _Request {
} }
__next__() { __next__() {
var options = this.options let options = this.options
var hasAttach = false // 是否有附件 let hasAttach = false // 是否有附件
var noBody = NOBODY_METHODS.includes(options.method) let noBody = NOBODY_METHODS.includes(options.method)
/* -------------------------- 1»» 请求的内容 --------------------- */ /* -------------------------- 1»» 请求的内容 --------------------- */
if (options.body) { if (options.body) {
var type = typeof options.body let type = typeof options.body
switch (type) { switch (type) {
case 'number': case 'number':
case 'string': case 'string':
this.__type__('text') this.__type__('text')
break break
case 'object': case 'object':
let _type = getType(options.body)
if (_type === 'ArrayBuffer' || _type === 'Uint8Array') {
break
}
// 解析表单DOM // 解析表单DOM
if (options.body.nodeName === 'FORM') { if (options.body.nodeName === 'FORM') {
options.method = options.body.method.toUpperCase() || 'POST' options.method = options.body.method.toUpperCase() || 'POST'
@ -104,14 +110,18 @@ class _Request {
} }
} else { } else {
for (let k in options.body) { for (let k in options.body) {
if ( if (Array.isArray(options.body[k]) || getType(options.body[k]) === 'FileList') {
toS.call(options.body[k]) === '[object File]' || options.body[k] = Array.from(options.body[k])
toS.call(options.body[k]) === '[object Blob]' hasAttach = options.body[k].some(
) { it => getType(it) === 'File' || getType(it) === 'Blob'
)
} else {
if (getType(options.body[k]) === 'File' || getType(options.body[k]) === 'Blob') {
hasAttach = true hasAttach = true
break break
} }
} }
}
// 有附件,则改为FormData // 有附件,则改为FormData
if (hasAttach) { if (hasAttach) {
if (noBody) { if (noBody) {
@ -138,14 +148,14 @@ class _Request {
if (noBody) { if (noBody) {
let tmp = Format.param(options.body) let tmp = Format.param(options.body)
if (tmp) { if (tmp) {
options.url += (~options.url.indexOf('?') ? '&' : '?') + tmp options.url += (options.url.includes('?') ? '&' : '?') + tmp
} }
delete options.body delete options.body
} else { } else {
if (!hasAttach) { if (options.body && !hasAttach) {
if (~options.headers['content-type'].indexOf('json')) { if (options.headers['content-type'].includes('json')) {
options.body = JSON.stringify(options.body) options.body = JSON.stringify(options.body)
} else { } else if (options.headers['content-type'].includes('form')) {
options.body = Format.param(options.body) options.body = Format.param(options.body)
} }
} }
@ -161,7 +171,7 @@ class _Request {
} }
/* ----------------- 5»» 构造请求 ------------------- */ /* ----------------- 5»» 构造请求 ------------------- */
var url = options.url let url = options.url
delete options.url delete options.url
for (let k in options) { for (let k in options) {
if (options[k] === null || options[k] === undefined || options[k] === '') { if (options[k] === null || options[k] === undefined || options[k] === '') {
@ -175,12 +185,12 @@ class _Request {
let _type let _type
if (this._owner._inject_res) { if (this._owner._inject_res) {
r = this._owner._inject_res(r) r = this._owner._inject_res(r)
_type = toS.call(r) _type = getType(r)
} }
if (isSucc) { if (isSucc) {
return r return r
} else { } else {
if (_type === '[object Promise]') { if (_type === 'Promise') {
return r.then(_ => Promise.reject(_)) return r.then(_ => Promise.reject(_))
} else { } else {
return Promise.reject(r) return Promise.reject(r)
@ -194,7 +204,7 @@ class _Request {
} }
abort() { abort() {
this.control.abort() this.control.abort(`Request timeout: ${~~(this.options.timeout / 1000)}s`)
} }
__type__(type) { __type__(type) {
@ -218,7 +228,7 @@ const _fetch = function (url, options) {
} }
_fetch.create = function () { _fetch.create = function () {
var another = function (url, options) { let another = function (url, options) {
return new _Request(url, options, another) return new _Request(url, options, another)
} }
inject(another) inject(another)