JSON Toolkit

Klíčové body návrhu datových kontraktů JSON API

Shrnutí: Šest věcí se po nasazení mění velmi těžko: obálky odpovědí, formáty chyb, tvar stránkování, reprezentace ID a času, sémantika null versus chybějící pole a vývoj verzí.

Středně pokročilý~13 min čteníPublikováno 2026-10-30Aktualizováno 2026-10-30
Titulní obrázek článku Klíčové body návrhu datových kontraktů JSON API

Stručně: zamkněte obálku odpovědi, formát chyb, reprezentaci ID a času a sémantiku null versus chybějící pole ve smlouvě dřív, než začnete diskutovat o stránkování a detailech pojmenování; nejvyšší úroveň vždy obalte obálkou data, aby stránkování a trasovací ID šla později přidat bez rozbití stávajících volajících.

V návrhu API neexistují standardní odpovědi, ale existují některá rozhodnutí „těžko vzít zpět po volbě“, která je nejlepší promyslet před napsáním prvního řádku kódu.

1. Obalovat odpověď, nebo ne

Tři styly:

// Styl A: samotná odpověď
{ "id": 1, "name": "张三" }

// Styl B: obálka data
{ "data": { "id": 1, "name": "张三" } }

// Styl C: data + metadata
{ "data": { "id": 1, "name": "张三" }, "meta": { "requestId": "a1b2" } }
Styl Plusy Mínusy
Samotná odpověď Nejjednodušší, o vrstvu méně pro frontend Nelze připojit metadata; pole na nejvyšší úrovni nese rizika JSON hijacking a evoluce
Obálka data Snadné přidání meta / errors / links O úroveň vnoření více, frontend musí psát res.data.data
Data + metadata Nejlepší pozorovatelnost Vyžaduje striktní dohodu, která pole patří do meta

Doporučení: jednotně používejte obálku data pro seznamy i detaily a nikdy nedělejte nejvyšší úroveň polem. Tím se pozdější přidání stránkování nebo trasovacích ID nikdy nedotkne smlouvy.

2. Formát chyb: zkopírujte RFC 9457

Vymýšlet vlastní strukturu chyb je obrovský zdroj nákladů na spolupráci. RFC 9457 (které nahrazuje RFC 7807) definuje application/problem+json:

{
  "type": "https://bbdrug.com/problems/insufficient-balance",
  "title": "余额不足",
  "status": 422,
  "detail": "账户余额 12.00 元,本次扣款需要 30.00 元",
  "instance": "/accounts/A1001/payments/p-889",
  "balance": "12.00",
  "required": "30.00"
}
Pole Význam Povinné
type Stabilní URI typu problému Doporučeno
title Krátké shrnutí čitelné pro lidi Doporučeno
status HTTP stavový kód Doporučeno
detail Konkrétní vysvětlení, co se tentokrát stalo Nepovinné
instance Instance zdroje, která měla problém Nepovinné
Rozšiřující pole Libovolná obchodní pole Nepovinné

Rozšiřující pole (například balance / required výše) specifikace povoluje a frontend je může použít pro přesné zprávy, místo aby parsoval text detail.

3. Stránkování: kurzor vítězí nad offsetem

// Stránkování offsetem
{ "data": [], "page": { "offset": 40, "limit": 20, "total": 137 } }

// Stránkování kurzorem
{ "data": [], "page": { "nextCursor": "eyJpZCI6MTAwfQ", "hasMore": true } }
Dimenze Stránkování offsetem Stránkování kurzorem
Obtížnost implementace Nízká (LIMIT/OFFSET) Střední (vyžaduje stabilní klíč řazení)
Výkon na hlubokých stránkách Špatný (čím větší offset, tím pomalejší) Dobrý
Když se data mění Duplikáty nebo chybějící položky Stabilní
Skok na stránku Možný Nemožný
Potřebuje celkový počet Vyžaduje další počítání Nevyžaduje

Doporučení: stránkování offsetem použijte pro administrační tabulky (potřebují skoky na stránky a součty) a stránkování kurzorem pro uživatelské nekonečné feedy.

4. ID a čas

4.1 Pro ID vždy používejte řetězce

Odůvodnění pokrývá článek o typech: na straně JavaScriptu hodnoty nad 2^53 tiše ztrácejí přesnost. I když je aktuální ID malé auto-increment celé číslo, použití řetězců nechává prostor pro pozdější přechod na snowflake ID.

{ "id": "1000000000000000001" }

4.2 Pro čas používejte řetězce RFC 3339

{
  "createdAt": "2026-10-30T10:41:45+08:00",
  "updatedAt": "2026-10-30T02:41:45Z"
}
  • Nepoužívejte číselná časová razítka v milisekundách: jsou špatně čitelná a jednotka (sekundy/milisekundy) se často plete;
  • Uveďte časové pásmo explicitně: buď Z, nebo offset; nikdy nepoužívejte „místní čas bez časového pásma“;
  • Pole pojmenujte jednotně jako xxxAt, aby bylo na první pohled zřejmé, že jde o bod v čase.

5. null a chybějící pole

Musíte zvolit jedno a zapsat to do dokumentace:

Přístup Vyjádření „žádná hodnota“ Vyjádření „neměnit“
Přístup A Hodnota pole je null Vynechat pole v požadavku
Přístup B Pole neexistuje Vynechat pole v požadavku

Problém přístupu A je, že „vynecháno“ a „posláno jako null“ znamenají v PATCH různé věci, což se snadno chybně interpretuje; problém přístupu B je, že frontendový JSON.stringify zahazuje pole s hodnotou undefined, což náhodou odpovídá sémantice.

Praktické doporučení:

  • Pro těla odpovědí použijte přístup A (null jasně znamená žádnou hodnotu a frontend nemusí rozlišovat undefined);
  • Pro těla požadavků (zejména PATCH) použijte přístup B v kombinaci s Merge Patch nebo JSON Patch, aby byla sémantika explicitní.

6. Pojmenování polí a struktura

Rozhodnutí Běžné volby Pozor
Styl pojmenování camelCase (ekosystém JS) / snake_case (Python, databáze) Jednotně v projektu, nemíchat
Hodnoty výčtů Malá písmena s podtržítky, "pending_review" Nepoužívejte čísla; špatně se čtou
Booleovská pole isActive / enabled Nepoužívejte status: 0/1
Pole polí Množná podstatná jména, items Pro prázdná pole vracejte [], nikdy null
Hloubka vnoření Maximálně 3 úrovně Hlubší rozdělte nebo nabídněte parametr expand

Vracení [] místo null u prázdných polí hodně záleží: data.items.map(...) na frontendu nespadne kvůli null.

7. Strategie vývoje

Změna Rozbíjí kompatibilitu Jak řešit
Přidání volitelného pole Ne Nasaďte přímo
Přidání povinného pole Ano Přidejte výchozí hodnotu, nebo uvolněte po verzích
Odebrání pole Ano Nejprve označte deprecated, sledujte volající, poté odeberte
Změna typu pole Ano Přidejte nový název pole a během přechodu zapisujte do obou
Změna množiny výčtových hodnot Záleží Klient musí mít fallback pro neznámé hodnoty
Změna sémantiky pole Ano Považujte to za nové pole

Tři pravidla disciplíny:

  1. Klienti musí být tolerantní k neznámým polím (neuzavírejte se do pasti additionalProperties: false);
  2. Klienti musí mít fallback větev pro neznámé výčtové hodnoty;
  3. Zastaralá pole držte alespoň jednu hlavní verzi a označte je Deprecation v hlavičkách odpovědi nebo v dokumentaci.

8. Úplný příklad

{
  "data": [
    {
      "id": "1001",
      "name": "JSON 权威指南",
      "price": "89.50",
      "tags": ["json", "spec"],
      "publishedAt": "2026-09-01T00:00:00+08:00",
      "author": null
    }
  ],
  "page": { "nextCursor": null, "hasMore": false },
  "meta": { "requestId": "req-7f3a", "tookMs": 12 }
}

Tato odpověď odráží všechny konvence výše: obálku data, řetězcová ID, částky jako řetězce, časová razítka s časovým pásmem, null znamenající chybějící, prázdné pole, stránkování kurzorem a meta pro informace o pozorovatelnosti.

9. Kontrolní seznam

  • [ ] Nejvyšší úroveň není pole; jednotně používejte obálku data;
  • [ ] Chybové odpovědi následují RFC 9457 a obsahují stabilní type;
  • [ ] Tvar stránkování odpovídá obchodnímu scénáři;
  • [ ] ID jsou řetězce a čas používá RFC 3339;
  • [ ] Sémantika null a chybějícího pole je zapsána v dokumentaci;
  • [ ] Styl pojmenování je v projektu jednotný a výčty používají řetězce s malými písmeny;
  • [ ] Klient má fallbacky pro neznámá pole a neznámé výčtové hodnoty;
  • [ ] Rozbíjející změny procházejí verzováním nebo přechodem s dvojím zápisem.

Časté dotazy

Jak navrhnout vyvíjející se JSON API?

Seznamy i detaily jednotně obalte obálkou data a nikdy nedělejte nejvyšší úroveň polem, aby stránkování a trasovací ID šla přidat bez rozbití smlouvy. Používejte řetězcová ID a časová razítka RFC 3339 a sémantiku null versus chybějící pole zapište do dokumentace. Nová pole držte volitelná a sémanticky nezávislá a klientům dejte fallbacky pro neznámá pole a výčtové hodnoty.

Proč nevymýšlet vlastní formát chyb?

Vymýšlet vlastní strukturu chyb je obrovský zdroj nákladů na spolupráci. Zkopírujte application/problem+json z RFC 9457, identifikujte problémy stabilním type a povolte rozšiřující pole jako balance a required, aby frontend mohl stavět přesné zprávy bez parsování textu detail.

Má každý endpoint používat stránkování kurzorem?

Ne. Administrační tabulky potřebují skoky na stránky a součty, takže se lépe hodí stránkování offsetem; uživatelské nekonečné feedy mají používat stránkování kurzorem, které zůstává stabilní na hlubokých stránkách a vyhýbá se duplikátům nebo mezerám při změnách dat. Vybírejte podle toho, zda scénář potřebuje skoky na stránky a součty.

Zdroje

Vhodné nástroje

Související články

Šest typů, přesnost čísel a úskalí kódování

JSON má jen šest typů, ale každý z nich může vaše data tiše přetvořit. Tento článek v jednom průchodu pokrývá úskalí čísel, řetězců a null.

Začátečník~12 min čteníAktualizováno 2026-10-30

JSON Pointer, Patch a Merge Patch

Tři krátké specifikace tvoří protokol změn: Pointer lokalizuje, Patch upravuje přesně, Merge Patch slučuje částečně. Špatná volba škodí u polí a souběžnosti.

Středně pokročilý~12 min čteníAktualizováno 2026-10-30

Správa JSON pro konfiguraci, fixtures a lokální data

Konfigurace musí být vrstvená a validovaná; fixtures musí být deterministické, čitelné a diffovatelné. Pokud se tyto dva druhy souborů spravují špatně, náklady na diagnostiku zůstávají dlouho vysoké.

Začátečník~10 min čteníAktualizováno 2026-10-30