Fejlformatet: DAWA-stier og /api/v1
Læs fejlsvar fra begge dele af API'et med én hjælpefunktion, og reager på den stabile code i stedet for fejlteksten.
API'et har to fejlformater. De DAWA-kompatible stier (/adresser, /autocomplete, /postnumre og resten uden /api/v1) svarer i DAWA's RFC 7807-format, så eksisterende DAWA-klienter kan læse fejlene. Vores egne endpoints under /api/v1 bruger en error-konvolut. Begge formater har et felt code, som du kan bygge din fejlhåndtering på.
DAWA-stier: type, title, details og code
{
"type": "UnauthorizedError",
"title": "The provided API key is invalid or revoked.",
"details": null,
"code": "invalid_api_key"
}typeer DAWA's fejltype. Den afhænger kun af statuskoden.titleer en tekst til mennesker. Den kan blive omformuleret, så brug den ikke iif-sætninger.detailsernull, et objekt eller en liste. Ved valideringsfejl er det en liste over de parametre, der var forkerte.codefindes ikke i DAWA. Vi har tilføjet den, så du kan skelne fxkey_blockedfrainvalid_api_keyuden at læse teksten. Felter, en DAWA-klient ikke kender, bliver normalt ignoreret, så tilføjelsen bryder ikke eksisterende kode.
| Status | type |
|---|---|
| 400 | QueryParameterFormatError |
| 401 | UnauthorizedError |
| 403 | ForbiddenError |
| 404 | ResourceNotFoundError |
| 429 | RateLimitExceededError |
| 500 | InternalServerError |
| 410 | GoneError |
| 502 | BadGatewayError |
| 503 | ServiceUnavailableError |
En valideringsfejl på en DAWA-sti har altid status 400 og code invalid_query_parameter. details viser hvilken parameter og hvorfor:
{
"type": "QueryParameterFormatError",
"title": "Invalid query parameter",
"details": [
{
"type": "less_than_equal",
"loc": ["query", "per_side"],
"msg": "Input should be less than or equal to 1000",
"input": "5000",
"ctx": {"le": 1000}
}
],
"code": "invalid_query_parameter"
}/api/v1: error-konvolutten
Fejl fra /api/v1 har objektet error med code, message og eventuelt details. Når fejlen kommer fra API'ets egen kontrol (nøgle, kvote, adgang), står det samme objekt også under detail, så klienter skrevet til FastAPI's standardformat stadig virker:
{
"detail": {
"error": {
"code": "browser_key_scope",
"message": "Domain-locked browser keys cover address lookups only. Use a server-side key for enrich, datavask/parse and stats."
}
},
"error": {
"code": "browser_key_scope",
"message": "Domain-locked browser keys cover address lookups only. Use a server-side key for enrich, datavask/parse and stats."
}
}Valideringsfejl på /api/v1 har status 422 og kun error-objektet:
{
"error": {
"code": "validation_error",
"message": "Invalid input",
"details": [
{"type": "missing", "loc": ["query", "betegnelse"], "msg": "Field required", "input": null}
]
}
}Læs altid error.code. Det findes i alle /api/v1-fejl, mens detail kun findes i nogle af dem.
Formatet gælder alle fejl
Formatet følger stien, uanset hvor fejlen opstår:
- 500 svarer med
typeInternalServerErrorogcodeinternal_errorpå DAWA-stierne og med{"error":{"code":"internal_error",…}}på/api/v1. - 403 med
codeforbiddenkommer fra vores misbrugsfilter, før kaldet når API'et. Den har også DAWA-formatet (typeForbiddenError) på DAWA-stierne ogerror-konvolutten på/api/v1. - 404 på en sti, der ikke findes, har
coderoute_not_foundi begge formater.
Alle fejlsvar fra API'et har Access-Control-Allow-Origin: *, så JavaScript i en browser kan læse status og body i stedet for at få en CORS-fejl.
Kun 502 og 504 falder uden for. De kommer fra proxyen foran API'et og har en HTML-side som body. Din kode skal tåle et svar, der ikke er JSON.
Koderne
code | Status | Betydning | Hvad du gør |
|---|---|---|---|
missing_api_key | 401 | Ingen nøgle i header eller URL | Send X-Api-Key. Se Fejl 401 |
invalid_api_key | 401 | Nøglen findes ikke eller er slettet | Tjek stavning og miljøvariabel |
account_suspended | 403 | Kontoen er spærret | Skriv til os via support |
key_blocked | 403 | Nøglen er spærret som lækket | Lav en ny nøgle eller lås den til domæner |
origin_not_allowed | 403 | Browsernøgle brugt fra et andet domæne | Tilføj domænet eller brug en servernøgle |
browser_key_scope | 403 | Browsernøgle på enrich/parse/stats | Kald fra din server med en servernøgle |
sandbox_scope | 403 | Testnøgle på enrich/parse/stats | Brug en live-nøgle |
ip_not_allowed | 403 | IP-låst nøgle brugt fra en anden IP | Tilføj IP'en på nøglen |
forbidden | 403 | Misbrugsfilteret har blokeret din IP | Skriv til os via support |
invalid_query_parameter | 400 | Forkert parameter på en DAWA-sti, fx srid ud over 4326 og 25832 | Læs details[].loc og msg |
invalid_kvh, invalid_kvhx | 400 | Nøglen har forkert længde | 12 tegn for kvh, 19 for kvhx |
invalid_cirkel, invalid_polygon | 400 | Forkert geometri | Se Fejl 400 og 404 |
area_too_large | 400 | cirkel eller polygon dækker for mange adresser | Gør området mindre, eller tilføj postnr eller kommunekode |
validation_error | 422 | Forkert input på /api/v1 | Læs error.details |
not_found | 404 | Id'et eller koden findes ikke | Tjek id-typen |
route_not_found | 404 | Stien findes ikke | Tjek URL'en |
unknown_entity | 404 | Ukendt replikeringsentitet | details.supported viser de gyldige |
quota_exceeded | 429 | Gratis månedskvote (inkl. buffer) eller dit budgetloft er brugt | Vent til den 1., opgradér eller ret budgetloftet. Se details.reason |
rate_limit_per_minute | 429 | For mange kald i minuttet | Vent Retry-After sekunder |
rate_limit_per_visitor | 429 | En besøgende har kaldt for meget via browsernøgle | Vent Retry-After sekunder |
upstream_unavailable | 503 | Et offentligt register svarer ikke | Prøv igen efter Retry-After |
upstream_busy | 503 | Kortvarig spærring af kolde berigelser | Prøv igen efter Retry-After |
internal_error | 500 | Fejl hos os | Prøv igen, og skriv til os via support, hvis den bliver ved |
En hjælpefunktion til begge formater
Funktionen her giver dig status, code, message, details og retry_after, uanset om fejlen kom fra en DAWA-sti, fra /api/v1 eller fra proxyen. Eksemplet slår et id op, der ikke findes:
import os import requests def read_error(resp: requests.Response) -> dict: """Normaliser et fejlsvar fra api.danadresse.dk.""" info = { "status": resp.status_code, "code": None, "message": resp.reason, "details": None, "retry_after": resp.headers.get("Retry-After"), } try: body = resp.json() except ValueError: info["message"] = resp.text[:200] or resp.reason # fx HTML fra en 502 return info if not isinstance(body, dict): return info err = body.get("error") if isinstance(err, dict): # /api/v1 info.update(code=err.get("code"), message=err.get("message"), details=err.get("details")) elif "type" in body or "title" in body: # DAWA-stier (RFC 7807) info.update(code=body.get("code") or body.get("type"), message=body.get("title"), details=body.get("details")) return info r = requests.get( "https://api.danadresse.dk/adresser/00000000-0000-0000-0000-000000000000", headers={"X-Api-Key": os.environ["DANADRESSE_API_KEY"]}, timeout=10, ) if not r.ok: fejl = read_error(r) print(fejl["status"], fejl["code"], "-", fejl["message"])
Output:
404 not_found - Adresse 00000000-0000-0000-0000-000000000000 not found
Samme funktion i JavaScript, til Node 18+ og browsere:
export async function readError(res) { const info = { status: res.status, code: null, message: res.statusText, details: null, retryAfter: res.headers.get("Retry-After"), }; const text = await res.text(); let body; try { body = JSON.parse(text); } catch { info.message = text.slice(0, 200) || res.statusText; // fx HTML fra en 502 return info; } if (body && typeof body.error === "object" && body.error !== null) { info.code = body.error.code ?? null; info.message = body.error.message ?? info.message; info.details = body.error.details ?? null; } else if (body && ("type" in body || "title" in body)) { info.code = body.code ?? body.type ?? null; info.message = body.title ?? info.message; info.details = body.details ?? null; } return info; } const res = await fetch( "https://api.danadresse.dk/adresser/00000000-0000-0000-0000-000000000000", { headers: { "X-Api-Key": process.env.DANADRESSE_API_KEY } }, ); if (!res.ok) { const fejl = await readError(res); console.log(fejl.status, fejl.code, "-", fejl.message); }
Retry-After er en af de headere, API'et gør synlige via CORS, så retryAfter virker også i en browser. Se Grænser, kvoter og 429.
Byg logikken på code
Et eksempel på, hvordan koderne fordeler sig i en klient:
RETRY = {"rate_limit_per_minute", "rate_limit_per_visitor", "upstream_unavailable",
"upstream_busy", "internal_error"}
FIX_CONFIG = {"missing_api_key", "invalid_api_key", "key_blocked", "origin_not_allowed",
"browser_key_scope", "sandbox_scope", "ip_not_allowed", "account_suspended"}
def classify(err: dict) -> str:
"""Returner 'retry', 'config', 'stop' eller 'bug' for et normaliseret fejlsvar."""
if err["code"] in RETRY or err["status"] in (502, 504):
return "retry"
if err["code"] in FIX_CONFIG:
return "config"
if err["code"] == "quota_exceeded":
return "stop"
return "bug"retry: prøv igen med ventetid. Se Grænser, kvoter og 429.config: prøv ikke igen. Nøglen eller dens opsætning skal rettes.stop: stop kaldene indtil kvoten nulstilles.bug: forkerte parametre eller id'er i din kode. Log hele svaret.
Næste skridt
Relaterede guides
Markdown-kilde Fejl i guiden? Skriv til os API-reference (Swagger)