Standard library patterns · 3 MIN READ
Strict JSON input: reject ambiguity before side effects
Separate JSON syntax acceptance, unknown-member policy and domain validation.
Successful decoding proves only that input was accepted under the decoder policy. It does not authorize the caller or establish a valid business transition. V2 rejects duplicate names and invalid UTF-8 by default; exact field-name matching and unknown-member rejection are separate settings.
Strict JSON input: reject ambiguity before side effects · A MAP OF THE IDEA
- Bound input bytes
- Decode a fresh value
- Validate and authorize
- Publish only success
Useful when
- Separate JSON syntax acceptance, unknown-member policy and domain validation.
- Bound input, decode under an explicit policy, validate the domain and authorize the target before performing a side effect. Test each boundary independently.
Watch out
- Strict decoding cannot authorize a resource or make an external write exactly once.
Implementation & operation cost: Choose the policy from the actual contract, then verify its failure path in the target environment.
Make the boundary policy visible
Unknown members are ignored by default in v2; use RejectUnknownMembers when the endpoint contract requires strict commands. A forward-compatible event consumer may need another policy. Bound body bytes before decoding and limit expensive domain work separately.
func decodeName(data []byte) (string, error) {
var request struct {
Name string `json:"name"`
}
if err := jsonv2.Unmarshal(data, &request, jsonv2.RejectUnknownMembers(true)); err != nil {
return "", fmt.Errorf("decode name: %w", err)
}
name := strings.TrimSpace(request.Name)
if name == "" {
return "", errors.New("name required")
}
return name, nil
}The complete helper needs encoding/json/v2 aliased as jsonv2, fmt, strings and errors. Its caller must cap data size before allocation. A fresh local target prevents earlier values or a partial failed decode from becoming a successful published result.
Test syntax and business meaning separately
Use fixtures for duplicate names, differently cased names, an unknown property, invalid UTF-8, a trailing second JSON value and a blank name. Record acceptance, typed value and domain-validation result. Do not decide compatibility solely from a changed error string.
Authorize after decoding, before acting
A valid document or resource ID does not establish tenant access. Bind the caller scope to the authoritative lookup or update predicate. A timeout during a later external write can leave its result unknown, so strict JSON decoding is not a substitute for an idempotency protocol.
Fuzz a meaningful contract
For arbitrary bounded bytes, assert that failed decoding returns no usable partial value. For accepted requests, check required invariants and a typed encode/decode round trip. Seed the ambiguity cases explicitly; a ten-second campaign explores inputs without proving every future input safe.
TAKE THIS WITH YOU
Bound input, decode under an explicit policy, validate the domain and authorize the target before performing a side effect. Test each boundary independently.
Check your understanding
A v2 command endpoint must reject additional properties. What should its decoder do?
Go to the source
Progress stays in this browser. No account needed.