Warnung vor doppelt abgelegten statischen Codes
Ein statischer Code traegt seinen Inhalt selbst — zweimal derselbe Inhalt ergibt zweimal exakt dasselbe Bild. Bisher entstand der zweite Eintrag stillschweigend. Das Anlegen antwortet nun mit 409 und nennt den vorhandenen Eintrag samt Namen und Datum; die Oberflaeche zeigt das als Warnung mit dem Knopf "Trotzdem ablegen". Eine Rueckfrage, keine Sperre — zwei Fassungen desselben Codes koennen gewollt sein. Verglichen wird Typ und Inhalt innerhalb der Gruppe, nicht die Gestaltung. Dynamische Codes bleiben aussen vor: zwei Kurz-Links auf dasselbe Ziel sind der Normalfall, sobald getrennt gezaehlt werden soll. ApiError traegt dafuer jetzt Status und Antwortkoerper — eine gemeldete Dublette ist keine Sackgasse, sondern eine Rueckfrage. Refs #6
This commit is contained in:
+33
-14
@@ -1,23 +1,42 @@
|
||||
/** Kleine Hilfen für Aufrufe der eigenen API aus Client-Komponenten. */
|
||||
|
||||
async function readError(response: Response): Promise<string> {
|
||||
async function readError(response: Response): Promise<ApiError> {
|
||||
let body: unknown = null;
|
||||
try {
|
||||
const body: unknown = await response.json();
|
||||
if (
|
||||
body !== null &&
|
||||
typeof body === "object" &&
|
||||
"error" in body &&
|
||||
typeof (body as { error: unknown }).error === "string"
|
||||
) {
|
||||
return (body as { error: string }).error;
|
||||
}
|
||||
body = await response.json();
|
||||
} catch {
|
||||
// Antwort ohne JSON-Körper — unten kommt die allgemeine Meldung.
|
||||
}
|
||||
return `Die Anfrage ist fehlgeschlagen (HTTP ${response.status}).`;
|
||||
|
||||
const message =
|
||||
body !== null &&
|
||||
typeof body === "object" &&
|
||||
"error" in body &&
|
||||
typeof (body as { error: unknown }).error === "string"
|
||||
? (body as { error: string }).error
|
||||
: `Die Anfrage ist fehlgeschlagen (HTTP ${response.status}).`;
|
||||
|
||||
return new ApiError(message, response.status, body);
|
||||
}
|
||||
|
||||
export class ApiError extends Error {}
|
||||
/**
|
||||
* Fehler einer eigenen API-Antwort.
|
||||
*
|
||||
* Trägt neben der Meldung auch den Status und den Antwortkörper: Manche
|
||||
* Fehler sind keine Sackgasse, sondern eine Rückfrage — etwa die gemeldete
|
||||
* Dublette beim Ablegen, zu der die Oberfläche den vorhandenen Eintrag
|
||||
* anzeigen will.
|
||||
*/
|
||||
export class ApiError extends Error {
|
||||
readonly status: number;
|
||||
readonly details: unknown;
|
||||
|
||||
constructor(message: string, status = 0, details: unknown = null) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
this.details = details;
|
||||
}
|
||||
}
|
||||
|
||||
export async function apiJson<T>(
|
||||
url: string,
|
||||
@@ -34,7 +53,7 @@ export async function apiJson<T>(
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ApiError(await readError(response));
|
||||
throw await readError(response);
|
||||
}
|
||||
if (response.status === 204) {
|
||||
return undefined as T;
|
||||
@@ -55,7 +74,7 @@ export async function downloadFile(
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new ApiError(await readError(response));
|
||||
throw await readError(response);
|
||||
}
|
||||
|
||||
const disposition = response.headers.get("Content-Disposition") ?? "";
|
||||
|
||||
Reference in New Issue
Block a user