318 lines
11 KiB
TypeScript
318 lines
11 KiB
TypeScript
/*
|
||
|
||
Fetch 接口
|
||
fetch() 包含了 fetch() 方法,用于获取资源。
|
||
Headers 表示响应/请求的标头信息,允许你查询它们,或者针对不同的结果做不同的操作。
|
||
Request 相当于一个资源请求。
|
||
Response 相当于请求的响应
|
||
|
||
fetch 参数:
|
||
method: 请求使用的方法,如 GET、POST。
|
||
headers: 请求的头信息,形式为 Headers 的对象或包含 ByteString 值的对象字面量
|
||
body: 请求的 body 信息:可能是一个 Blob、BufferSource(x禁止)、FormData、URLSearchParams 或者 USVString 对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。
|
||
mode: 请求的模式,如 cors、no-cors 或者 same-origin。
|
||
credentials: 请求的 credentials,如 omit、same-origin 或者 include。为了在当前域名内自动发送 cookie,必须提供这个选项,从 Chrome 50 开始,这个属性也可以接受 FederatedCredential (en-US) 实例或是一个 PasswordCredential (en-US) 实例。
|
||
|
||
*/
|
||
"use client"
|
||
|
||
import { useUserStore } from "@/app/store/user"
|
||
import { deleteCookie, refreshKey } from "@/components/login/api"
|
||
import { isNumber } from "@/libs/util"
|
||
|
||
interface OptionsProps {
|
||
url: string
|
||
method: string
|
||
body?: BodyInit
|
||
data?: any
|
||
needCredentials?: boolean
|
||
isUpload?: boolean
|
||
isDownload?: boolean
|
||
isFormData?: boolean
|
||
isLogin?: boolean
|
||
isRefresh?: boolean
|
||
isDeleteCookie?: boolean
|
||
notJson?: boolean
|
||
headerAttr?: {
|
||
[key: string]: string
|
||
}
|
||
}
|
||
|
||
async function httpfetch<T>(options: OptionsProps): Promise<T> {
|
||
const { removeUserInfo, refreshToken } = useUserStore.getState()
|
||
|
||
const access_token = useUserStore.getState().access_token
|
||
const refresh_token = useUserStore.getState().refresh_token
|
||
|
||
let Message = {
|
||
error: (params: { content: string }) => {
|
||
console.log(params.content)
|
||
},
|
||
}
|
||
|
||
const createSsrRequest: (
|
||
options: OptionsProps,
|
||
token?: string,
|
||
refreshToken?: string
|
||
) => Request = (options) => {
|
||
// 设置请求头
|
||
let myHeaders = new Headers()
|
||
if (!options.isUpload && !options.isFormData && !options.notJson) {
|
||
myHeaders.append("Content-Type", "application/json")
|
||
}
|
||
if (options.isDownload) {
|
||
myHeaders.append("Response-Type", "blob")
|
||
}
|
||
if (options.isFormData) {
|
||
options.body &&
|
||
(options.body as FormData)?.append &&
|
||
(options.body as FormData).append("options", JSON.stringify(options))
|
||
}
|
||
|
||
// 配置常用设置 method 请求方式 headers 请求头
|
||
let myInit: RequestInit = {
|
||
method: "POST",
|
||
headers: myHeaders,
|
||
mode: "cors",
|
||
cache: "default",
|
||
body: options.isFormData
|
||
? options.body
|
||
: (JSON.stringify({ data: options.body, ...options }) as BodyInit),
|
||
}
|
||
const pathList = options.url.split("/")
|
||
const request = new Request(
|
||
`${process.env.NEXT_PUBLIC_BASE_PATH}/api/${options.isFormData ? "transferFormData" : "transfer"}/${pathList[pathList.length - 1]}`,
|
||
myInit
|
||
)
|
||
return request
|
||
}
|
||
|
||
const createCsrRequest: (
|
||
options: OptionsProps,
|
||
token?: string,
|
||
refreshToken?: string
|
||
) => Request = (options, token = "", refreshToken = "") => {
|
||
let request = new Request(options.url)
|
||
// 设置请求头
|
||
let myHeaders = new Headers()
|
||
myHeaders.append("Authorization", `bearer ${token}`)
|
||
myHeaders.append("Token", token)
|
||
myHeaders.append("Refresh-Token", refreshToken)
|
||
if (!options.isUpload && !options.isFormData && !options.notJson) {
|
||
myHeaders.append("Content-Type", "application/json")
|
||
}
|
||
if (options.isDownload) {
|
||
myHeaders.append("Response-Type", "blob")
|
||
}
|
||
if (options.headerAttr) {
|
||
Object.entries(options.headerAttr).map(([key, value]) => {
|
||
myHeaders.append(key, value)
|
||
})
|
||
}
|
||
// myHeaders.append("X-Machine-Code", "machine_code0")
|
||
|
||
// 配置常用设置 method 请求方式 headers 请求头
|
||
let myInit: RequestInit = {
|
||
method: options.method,
|
||
headers: myHeaders,
|
||
mode: "cors",
|
||
cache: "default",
|
||
credentials: options.needCredentials ? "include" : undefined,
|
||
body: options.body as BodyInit,
|
||
}
|
||
|
||
let publicUrl = options.url.includes("http")
|
||
? ""
|
||
: process.env.NEXT_PUBLIC_URL
|
||
if (options.method === "GET") {
|
||
let searchParams = new URLSearchParams()
|
||
Object.entries(options.data || {})?.map((m) => {
|
||
searchParams.append(m[0], m[1] as string)
|
||
})
|
||
let concatenatedURL = searchParams.toString()
|
||
? options.url + "?" + searchParams.toString()
|
||
: options.url
|
||
request = new Request(publicUrl + concatenatedURL, myInit)
|
||
} else {
|
||
request = new Request(publicUrl + options.url, myInit)
|
||
}
|
||
return request
|
||
}
|
||
|
||
const createNewRequest =
|
||
process.env.NEXT_PUBLIC_OUTPUT_TYPE === "standalone"
|
||
? createSsrRequest
|
||
: createCsrRequest
|
||
|
||
const request = createNewRequest(options, access_token, refresh_token)
|
||
|
||
return new Promise((resolved, rejected) => {
|
||
fetch(request)
|
||
.then(async (response) => {
|
||
if (response.status === 200) {
|
||
if (options.isDownload) {
|
||
return response.blob()
|
||
} else {
|
||
return response.headers
|
||
.get("Content-Type")
|
||
?.includes("application/json")
|
||
? response.json()
|
||
: response.text()
|
||
}
|
||
} else {
|
||
const { status } = response
|
||
if (status === 400) {
|
||
if (
|
||
response.headers.get("Content-Type")?.includes("application/json")
|
||
) {
|
||
const data = await response.json()
|
||
const { message: errInfo } = data
|
||
Message.error({ content: errInfo || "" })
|
||
throw new Error(errInfo)
|
||
} else {
|
||
const errInfo = await response.text()
|
||
Message.error({ content: errInfo || "" })
|
||
throw new Error(errInfo)
|
||
}
|
||
} else if (status === 401) {
|
||
try {
|
||
const refreshResponse = await refreshKey()
|
||
let access_token = refreshResponse?.access || ""
|
||
let refresh_token = refreshResponse?.refresh || ""
|
||
refreshToken({
|
||
refresh_token,
|
||
access_token,
|
||
})
|
||
const retry_request = createNewRequest(options, access_token)
|
||
const retry_response = await fetch(retry_request)
|
||
return options.isDownload
|
||
? retry_response.blob()
|
||
: retry_response.json()
|
||
} catch (error) {
|
||
rejected(error)
|
||
try {
|
||
await deleteCookie()
|
||
} catch (error) {
|
||
rejected(error)
|
||
} finally {
|
||
// 清空身份信息并跳转至登录页面
|
||
removeUserInfo()
|
||
window.location.href = "/login"
|
||
}
|
||
}
|
||
} else if (status === 403) {
|
||
if (
|
||
response.headers.get("Content-Type")?.includes("application/json")
|
||
) {
|
||
const data = await response.json()
|
||
const { message: errInfo } = data
|
||
Message.error({ content: errInfo || "" })
|
||
location.href = `${process.env.NEXT_PUBLIC_HOME_URL}`
|
||
throw new Error(errInfo)
|
||
} else {
|
||
const errInfo = await response.text()
|
||
Message.error({ content: errInfo || "" })
|
||
location.href = `${process.env.NEXT_PUBLIC_HOME_URL}`
|
||
throw new Error(errInfo)
|
||
}
|
||
} else if (status === 500) {
|
||
if (
|
||
response.headers.get("Content-Type")?.includes("application/json")
|
||
) {
|
||
const data = await response.json()
|
||
const { message: errInfo } = data
|
||
Message.error({ content: errInfo || "" })
|
||
throw new Error(errInfo)
|
||
} else {
|
||
const errInfo = await response.text()
|
||
Message.error({ content: errInfo || "" })
|
||
throw new Error(errInfo)
|
||
}
|
||
} else if (status === 406) {
|
||
const data = await response.json()
|
||
const { message: errInfo } = data
|
||
Message.error({ content: errInfo || "" })
|
||
// throw new Error(errInfo)
|
||
rejected(errInfo)
|
||
try {
|
||
await deleteCookie()
|
||
} catch (error) {
|
||
rejected(error)
|
||
} finally {
|
||
// 清空身份信息并跳转至登录页面
|
||
removeUserInfo()
|
||
window.location.href = "/login"
|
||
}
|
||
} else if (status >= 510) {
|
||
Message.error({ content: "申请资源不足!" })
|
||
throw new Error("申请资源不足!")
|
||
} else {
|
||
Message.error({ content: "网络服务有误!" })
|
||
throw new Error("Something went wrong on API server!")
|
||
}
|
||
}
|
||
})
|
||
.then(async (response) => {
|
||
const code = response?.code
|
||
const errInfo = response?.message || ""
|
||
if (response) {
|
||
if (isNumber(code)) {
|
||
let status = code
|
||
// 日后做相关处理
|
||
if (status === 401) {
|
||
try {
|
||
const refreshResponse = await refreshKey()
|
||
let access_token = refreshResponse?.access || ""
|
||
let refresh_token = refreshResponse?.refresh || ""
|
||
refreshToken({
|
||
refresh_token,
|
||
access_token,
|
||
})
|
||
const retry_request = createNewRequest(options, access_token)
|
||
const retry_response = await fetch(retry_request)
|
||
return options.isDownload
|
||
? retry_response.blob()
|
||
: retry_response.json()
|
||
} catch (error) {
|
||
rejected(error)
|
||
}
|
||
} else if (status === 400) {
|
||
const { message: errInfo } = response
|
||
Message.error({ content: errInfo || "" })
|
||
throw new Error(errInfo)
|
||
} else if (status === 403) {
|
||
const { message: errInfo } = response
|
||
Message.error({ content: errInfo || "" })
|
||
// location.href = `${process.env.NEXT_PUBLIC_HOME_URL}`
|
||
throw new Error(errInfo)
|
||
} else if (status === 500) {
|
||
const { message: errInfo } = response
|
||
Message.error({ content: errInfo || "" })
|
||
throw new Error(errInfo)
|
||
} else if (status === 406) {
|
||
const { message: errInfo } = response
|
||
Message.error({ content: errInfo || "" })
|
||
throw new Error(errInfo)
|
||
} else if (status >= 510) {
|
||
Message.error({ content: "申请资源不足!" })
|
||
throw new Error("申请资源不足!")
|
||
} else if (status === 200) {
|
||
resolved(response)
|
||
} else {
|
||
Message.error({ content: "网络服务有误!" })
|
||
throw new Error("Something went wrong on API server!")
|
||
}
|
||
}
|
||
resolved(response)
|
||
} else {
|
||
throw new Error(errInfo)
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
rejected(error)
|
||
})
|
||
})
|
||
}
|
||
|
||
export default httpfetch
|