/* 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(options: OptionsProps): Promise { 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) => 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( `/api/${options.isFormData ? "transferFormData" : "transfer"}/${pathList[pathList.length - 1]}`, myInit ) return request } const createCsrRequest: (options: OptionsProps, token?: string) => Request = ( options, token = "" ) => { let request = new Request(options.url) // 设置请求头 let myHeaders = new Headers() myHeaders.append("Authorization", `bearer ${token}`) 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) 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({ token: refresh_token, }) if (refreshResponse.code === 200) { let access_token = refreshResponse.message?.access || "" let refresh_token = refreshResponse.message?.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() } else { const { code } = refreshResponse if (code === 406) { try { await deleteCookie() } catch (error) { rejected(error) } finally { // 清空身份信息并跳转至登录页面 removeUserInfo() window.location.href = "/login" } } else { throw new Error("重新获取token失败!") } } } catch (error) { rejected(error) } } 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) } 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, message: errInfo } = response if (response) { if (isNumber(code)) { let status = code // 日后做相关处理 if (status === 401) { try { const refreshResponse = await refreshKey({ token: refresh_token, }) if (refreshResponse.code === 200) { let access_token = refreshResponse.message?.access || "" let refresh_token = refreshResponse.message?.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() } else { const { code } = refreshResponse if (code === 406) { try { await deleteCookie() } catch (error) { rejected(error) } finally { // 清空身份信息并跳转至登录页面 removeUserInfo() window.location.href = "/login" } } else { throw new Error("重新获取token失败!") } } } 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