Een nummer komt een product op meer dan één plek binnen. Het wordt in een formulier getypt, door een client doorgegeven, naar een service verzonden, opgeslagen en later teruggelezen door een operator. Elk van die punten is een grens, en elke grens heeft een andere reden om de waarde te controleren.
Teams die ze allemaal als dezelfde controle behandelen, eindigen met gedupliceerde logica die uit elkaar loopt, en met het slechtste van twee werelden: trage feedback omdat de gezaghebbende controle op afstand staat, en zwak gezag omdat de snelle controle de enige is die werkelijk draait. De lagen scheiden lost beide op.
Moet validatie op de client of op de server leven?
Op beide, en om verschillende redenen. De client is waar latentie ertoe doet. Een gebruiker die een lange identificator typt wil onmiddellijk weten van een verkeerd getypt teken, en een round trip per toetsaanslag is de verkeerde manier om het hem te vertellen. Lokale, offline controles die geen opzoeking vereisen, horen hier.
De server is waar gezag ertoe doet. Alles wat de client beweert, kan worden vervalst, overgeslagen door het eindpunt rechtstreeks aan te roepen, of geproduceerd door een oudere build met een verouderde regelset. De service moet de controle opnieuw uitvoeren op alles wat hij accepteert, niet omdat hij zijn eigen client wantrouwt maar omdat de client geen deel uitmaakt van zijn vertrouwensgrens.
De twee lagen moeten één specificatie en één implementatie delen, gepubliceerd als een pakket of een gegenereerd artefact, zodat de snelle controle en de gezaghebbende controle het niet oneens kunnen zijn over hoe een geldige waarde eruitziet. Wanneer ze toch uiteenlopen, is het symptoom een invoer die de client accepteert en de server afwijst, wat gebruikers ervaren als een onverklaarbare fout aan het einde van een formulier.
De voorbeelden die hier worden besproken zijn structureel. Geen echt rekening-, identiteits- of kaartnummer hoort in een verzoeklog, een testfixture of een foutpayload te verschijnen, en de waarden die in dit artikel worden beschreven zijn alleen illustratieve vormen.
Wat je aan de grens controleert en wat je doorlaat
De grens moet bevestigen wat hij goedkoop kan bevestigen en de rest als data doorgeven.
Controleer aan de rand: de tekenset, de lengte, de genormaliseerde vorm, en elk gepubliceerd controleteken voor een schema dat de service kent. Wijs vroeg af en met een specifieke reden, want het alternatief is een misvormde waarde die dieper reist in een systeem dat geen vocabulaire heeft om haar te beschrijven.
Laat door: alles wat een registeropzoeking vereist, tenzij de service een contractuele relatie heeft die de opzoeking goedkoop maakt. Een controle op het bestaan van een rekening bij een openbaar eindpunt is tegelijk een aanvalsvector voor denial-of-service en een privacylek, want het maakt van een formulier een orakel om te raden of een waarde live is.
Normaliseer één keer, aan de grens, en sla de genormaliseerde vorm op als de canonieke waarde terwijl je het origineel voor weergave bewaart. Alles stroomafwaarts vergelijkt dan één representatie, en de klasse bugs waarin hetzelfde nummer in drie formaten voorkomt verdwijnt.
Foutresponsen classificeren en hun retrysemantiek
Niet elke afwijzing betekent hetzelfde, en één foutcode voor allemaal dwingt elke aanroeper te raden hoe te reageren.
| Situatie | Betekenis | Herhaalbaar |
|---|---|---|
| Misvormde invoer | De waarde breekt de formaatregel | Nee — de aanroeper moet andere data sturen |
| Mislukt controleteken | De vorm is toegestaan maar de rekenkunde komt niet overeen | Nee — om dezelfde reden |
| Niet-ondersteund schema | Niets dat de service implementeert komt overeen met deze vorm | Nee — tenzij de service dekking toevoegt |
| Tijdelijk onbeschikbaar | Een afhankelijkheid die de controle nodig heeft, ligt eruit of wordt afgeknepen | Ja, met backoff |
| Snelheidsbeperkt | De aanroeper heeft een limiet overschreden | Ja, na het interval dat de respons aangeeft |
De eerste vier samenvatten in een generiek bad request is de meest voorkomende ontwerpfout, want het maakt een permanente invoerfout niet te onderscheiden van een tijdelijke storing. Clients herhalen dan de verkeerde fouten of geven de pogingen op bij degene die zouden zijn gelukt.
Geef een stabiele, machinaal leesbare code terug naast een menselijk leesbare melding, en documenteer welke codes herhaalbaar zijn. Behandel die classificatie als deel van het interfacecontract: die later wijzigen is een brekende wijziging voor iedereen die er retrylogica aan heeft gekoppeld.
Waarom mag een mislukte controle niet als een ontbrekend nummer worden gerapporteerd?
Omdat de twee beweringen verschillende waarheidsvoorwaarden hebben. Een mislukt controleteken zegt dat de reeks het niet met zichzelf eens is, wat de service met zekerheid weet. Een ontbrekend nummer zegt dat zo’n rekening of record niet bestaat, wat de service meestal helemaal niet kan weten.
De verwarring richt in beide richtingen echte schade aan. Een gebruiker met een echte waarde die te horen krijgt dat een nummer niet bestaat, kan een legitieme transactie opgeven of iets anders invoeren. Een gebruiker die te horen krijgt dat een nummer geldig is omdat een controle slaagde, kan geloven dat een rekening is bevestigd terwijl alleen de rekenkunde is bevestigd.
De juiste melding beschrijft de reeks: het formaat klopte, of het controlecijfer hield geen stand, of geen geïmplementeerde regel herkent de invoer. Niets in die lijst beweert iets over de werkelijkheid, en elk item vertelt de aanroeper iets waarop hij kan handelen. Dezelfde discipline geldt binnen batchtaken, waar een verkeerd gelabelde categorie een beoordelaar op jacht kan sturen naar een defect dat niet bestaat; de categorisering voor grote bestanden is op precies dit onderscheid gebouwd.
Snelheidslimieten, time-outs en fallbacks voor externe opzoekingen
Zodra een controle data van buiten het proces nodig heeft, krijgt ze de faalwijzen van een netwerkaanroep. Daarvoor ontwerpen is geen pessimisme; het is het verschil tussen een degradatie en een storing.
Elke externe aanroep heeft een time-out nodig die korter is dan het verzoek dat hem bevat, zodat een trage afhankelijkheid niet het hele budget opslokt. Hij heeft een retrybeleid nodig met backoff en jitter voor de tijdelijke fouten, en een circuit breaker zodat een blijvend falende afhankelijkheid niet meer op volle snelheid wordt aangeroepen.
Hij heeft ook een gedefinieerd gedrag nodig voor het geval waarin de opzoeking niet kan plaatsvinden. Twee antwoorden zijn legitiem, en de keuze is een productbeslissing: het verzoek laten falen, of de waarde voorlopig accepteren en als niet-geverifieerd markeren. Wat niet legitiem is, is een onbereikbare opzoeking stil als een pass behandelen, want dat verandert een storing in een datakwaliteitsprobleem.
Cachen helpt en heeft zijn eigen regels nodig. Cache alleen wat het contract toestaat, sleutel op de genormaliseerde waarde, en geef items een levensduur die is afgestemd op hoe snel het onderliggende feit kan veranderen. Cache een afwijzing nooit alsof het een gecontroleerd feit over het nummer is.
Voor ontwikkelaars: contracten, versies en logs
Behandel de regelset als een versieafhankelijkheid van de API, niet als een implementatiedetail.
Maak zichtbaar welke versie van de regelset een uitspraak heeft opgeleverd, zodat een aanroeper kan zien of een gedragswijziging uit zijn eigen build of uit de service kwam. Versioneer de regelset onafhankelijk van het eindpunt wanneer de dekking verandert, en houd oude versies lang genoeg beschikbaar zodat clients kunnen migreren. Wanneer een schema nieuwe parameters publiceert, moet de wijziging een data-update met een nieuw versienummer zijn, niet een codewijziging waarvan de release notes het gedragsverschil weglaten.
Log uitspraken, nooit volledige waarden. Registreer het schema, de uitspraak, de versie van de regelset en een correlatie-identificator, en houd de waarde volledig buiten de logregel — ook in foutpaden, waar gelekte waarden het vaakst opduiken.
Onthoud ten slotte wat de grens niet kan vaststellen. Een gevalideerd formaat is een bewering over een reeks, zoals het formaat-only-geval duidelijk maakt voor schema’s zonder enige rekenkunde, en het drielaagsmodel van validatie is de referentie om de lagen gescheiden te houden.
Volgende stappen
Neem het eindpunt dat je meest gevoelige identificator ontvangt en som elke controle op die het uitvoert, en markeer elk als lokale rekenkunde of externe opzoeking. Bevestig dan dat de client de lokale controles vroeg draait en dat de server ze allemaal opnieuw draait; de nummervalidatietool toont de tekst van de uitspraak die een client veilig kan hergebruiken zonder meer te claimen dan de rekenkunde ondersteunt.