封装一个简易 fetch 请求库
大约 2 分钟
/**
* 封装一个 fetch 请求
* url 请求地址
* method 请求方式 * GET POST PUT DELETE OPTIONS
* credentials 携带资源凭证 * include same-origin * omit * credentials
* headers: null 自定义的请求头信息 「格式必须是纯粹对象」
* body:null 请求主体信息「只针对 POST 系列请求, 根据当前服务器要求,如果用户传递的是一个纯粹对象, 则需要把其变为 urlencoded 格式字符串(设定请求头中的 Content-Type) 」
* params: null 设定问号传参信息「格式必须是纯粹对象, 在内部把其拼接到 url 的末尾」
* responseType: 'json' 请求响应的数据类型 * json text blob arrayBuffer
* timeout: 5000 请求超时时间
* signal: 中断请求的信号
*/
const http = function (config) {
if (typeof config !== 'object') config = {}
config = Object.assign(
{
url: '',
method: 'GET',
credentials: 'include',
headers: null,
body: null,
params: null,
responseType: 'json',
timeout: 5000,
signal: null,
},
config
)
// 必要参数判断
if (!config.url) {
throw new Error('url is required')
}
if (typeof config.headers !== 'object') config.headers = {}
if (config.params !== null && typeof config.params !== 'object')
config.params = null
// 处理细节
let {
url,
method,
credentials,
headers,
body,
params,
responseType,
timeout,
} = config
// 处理问号传参
if (params) {
let paramsStr = Object.keys(params)
.map((key) => `${key}=${params[key]}`)
.join('&')
url += `${url.includes('?') ? '&' : '?'}${paramsStr}` // 拼接时是否带问号
}
// 处理请求主体信息
if (typeof body === 'object') {
body = JSON.stringify(body)
headers['Content-Type'] = 'application/json'
}
const token = localStorage.getItem('token')
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
// 超时处理
if (signal === null) {
signal = new AbortController().signal
}
const timeoutId = setTimeout(() => {
signal.abort()
}, timeout)
// 发起请求
method = method.toUpperCase()
config = {
method,
credentials,
headers,
cache: 'no-cache',
signal,
}
if (['POST', 'PUT', 'PATCH'].includes(method)) {
config.body = body
}
return fetch(url, config)
.then((response) => {
const { status, statusText } = response
if (status >= 200 && status < 300) {
// 请求成功
let result
switch (responseType.toLowerCase()) {
case 'json':
result = response.json()
break
case 'text':
result = response.text()
break
case 'blob':
result = response.blob()
break
case 'arraybuffer':
result = response.arrayBuffer()
break
default:
result = response.json()
}
return response.json()
}
// 请求失败: HTTP 状态码失败
return Promise.reject({
code: -100,
status,
statusText,
})
})
.catch((reason) => {
const { code, status } = reason
if (code === -100) {
switch (+status) {
case 401:
console.error('未授权,请重新登录!')
break
case 403:
console.error('禁止访问!')
break
case 404:
console.error('请求的资源不存在!')
break
case 500:
console.error('服务器内部错误,请稍后再试!')
break
default:
console.error('当前网络繁忙,请稍后再试!')
}
}
return Promise.reject(reason)
})
}
// 快捷方法
// ["GET", "HEAD", "DELETE", "OPTIONS"].forEach(method => {
// http[method.toLowerCase()] = function(url, config) {
// if (typeof config !== 'object') {
// config['url'] = url
// config['method'] = method
// return http(config)
// }
// }
// })
// ["POST", "PUT", "PATCH"].forEach(method => {
// http[method.toLowerCase()] = function(url, body, config) {
// if (typeof config !== 'object') {
// config['url'] = url
// config['method'] = method
// config["body"] = body;
// return http(config)
// }
// }
// })
export default http
Loading...