From bbbf589d6055579eae0546885fedcd07f223f4d9 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 4 Jul 2026 00:50:17 +0800 Subject: [PATCH] feat(platform-web): surface backend api errors --- .../apps/web/src/api/client.test.ts | 18 +++++++++++++ .../apps/web/src/api/client.ts | 27 ++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/vehicle-data-platform/apps/web/src/api/client.test.ts b/vehicle-data-platform/apps/web/src/api/client.test.ts index 22ab764a..ea7141a2 100644 --- a/vehicle-data-platform/apps/web/src/api/client.test.ts +++ b/vehicle-data-platform/apps/web/src/api/client.test.ts @@ -37,3 +37,21 @@ test('rawFramesQuery posts structured JSON instead of URL query strings', async }) }); }); + +test('api errors include backend message and detail', async () => { + vi.spyOn(globalThis, 'fetch').mockResolvedValue({ + ok: false, + status: 500, + json: async () => ({ + error: { + code: 'INTERNAL', + message: '服务处理失败', + detail: 'TDengine timeout' + }, + traceId: 'trace-error', + timestamp: 1783094400000 + }) + } as Response); + + await expect(api.opsHealth()).rejects.toThrow('服务处理失败: TDengine timeout'); +}); diff --git a/vehicle-data-platform/apps/web/src/api/client.ts b/vehicle-data-platform/apps/web/src/api/client.ts index 620f050b..867cd797 100644 --- a/vehicle-data-platform/apps/web/src/api/client.ts +++ b/vehicle-data-platform/apps/web/src/api/client.ts @@ -28,15 +28,40 @@ export type RawFrameQuery = { offset?: number; }; +type ApiErrorEnvelope = { + error?: { + code?: string; + message?: string; + detail?: string; + }; +}; + async function request(path: string, init?: RequestInit): Promise { const response = await fetch(path, init); if (!response.ok) { - throw new Error(`request failed ${response.status}`); + throw new Error(await responseErrorMessage(response)); } const envelope = (await response.json()) as ApiEnvelope; return envelope.data; } +async function responseErrorMessage(response: Response) { + try { + const envelope = (await response.json()) as ApiErrorEnvelope; + const message = envelope.error?.message?.trim(); + const detail = envelope.error?.detail?.trim(); + if (message && detail) { + return `${message}: ${detail}`; + } + if (message) { + return message; + } + } catch { + // Fall back to status when the server does not return the platform envelope. + } + return `request failed ${response.status}`; +} + export const api = { dashboardSummary: () => request('/api/dashboard/summary'), vehicles: (params = new URLSearchParams()) => request>(`/api/vehicles?${params.toString()}`),