+38
-5
@@ -4,10 +4,13 @@ const BASE = '/api/v1'
|
||||
|
||||
export class ApiError extends Error {
|
||||
status: number
|
||||
/** 后端透传的 OCI 错误码(如 OrganizationExclusionError),供调用方做能力适配 */
|
||||
ociCode?: string
|
||||
|
||||
constructor(status: number, message: string) {
|
||||
constructor(status: number, message: string, ociCode?: string) {
|
||||
super(message)
|
||||
this.status = status
|
||||
this.ociCode = ociCode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +34,15 @@ function buildUrl(path: string, query?: RequestOptions['query']): string {
|
||||
|
||||
async function parseError(resp: Response): Promise<never> {
|
||||
let message = `请求失败(${resp.status})`
|
||||
let ociCode: string | undefined
|
||||
try {
|
||||
const body = (await resp.json()) as { error?: string; hint?: string; errors?: unknown[] }
|
||||
const body = (await resp.json()) as {
|
||||
error?: string
|
||||
hint?: string
|
||||
errors?: unknown[]
|
||||
ociCode?: string
|
||||
}
|
||||
ociCode = body.ociCode
|
||||
if (body.error) {
|
||||
message = body.hint ? `${body.hint}|${body.error}` : body.error
|
||||
} else {
|
||||
@@ -49,7 +59,7 @@ async function parseError(resp: Response): Promise<never> {
|
||||
if (resp.status === 429 && !resp.url.includes('/auth/login') && location.pathname !== '/blocked') {
|
||||
location.assign('/blocked')
|
||||
}
|
||||
throw new ApiError(resp.status, message)
|
||||
throw new ApiError(resp.status, message, ociCode)
|
||||
}
|
||||
|
||||
/** 统一请求封装:注入 JWT、401 登出、错误转 ApiError */
|
||||
@@ -63,8 +73,31 @@ export async function request<T>(path: string, opts: RequestOptions = {}): Promi
|
||||
body: opts.body === undefined ? undefined : JSON.stringify(opts.body),
|
||||
})
|
||||
if (!resp.ok) await parseError(resp)
|
||||
if (resp.status === 204) return undefined as T
|
||||
return (await resp.json()) as T
|
||||
// 202/204 等成功响应可能无 body,直接 resp.json() 会抛 Unexpected end of JSON input
|
||||
const text = await resp.text()
|
||||
return (text ? JSON.parse(text) : undefined) as T
|
||||
}
|
||||
|
||||
/** 原始请求:注入 JWT、错误同 request 转 ApiError,返回原始 Response(二进制内容读写用) */
|
||||
export async function rawFetch(
|
||||
path: string,
|
||||
opts: {
|
||||
method?: string
|
||||
query?: RequestOptions['query']
|
||||
headers?: Record<string, string>
|
||||
body?: BodyInit
|
||||
} = {},
|
||||
): Promise<Response> {
|
||||
const auth = useAuthStore()
|
||||
const headers: Record<string, string> = { ...opts.headers }
|
||||
if (auth.token) headers.Authorization = `Bearer ${auth.token}`
|
||||
const resp = await fetch(buildUrl(path, opts.query), {
|
||||
method: opts.method ?? 'GET',
|
||||
headers,
|
||||
body: opts.body,
|
||||
})
|
||||
if (!resp.ok) await parseError(resp)
|
||||
return resp
|
||||
}
|
||||
|
||||
export const mockOn = import.meta.env.VITE_MOCK === '1'
|
||||
|
||||
Reference in New Issue
Block a user