Skip to content

Commit 170896a

Browse files
mromaszewiczclaude
andauthored
Add BindJSONBody to bind the request body value kin-openapi validated (#165)
* Add BindJSONBody to bind the request body value kin-openapi validated Strict net/http servers decode a JSON request body a second time after the validation middleware has validated it, and encoding/json matches member names case-insensitively, so a body such as {"role":"user","ROLE":"admin"} passes validation as role=user and reaches the handler as Role="admin", bypassing the schema's constraints. kin-openapi v0.151.0 added openapi3filter.Options.KeepValidatedValue, which leaves the decoded and validated value on the request body behind a ValidatedValue() any method. BindJSONBody binds that value into the request struct when it is present, and otherwise (or when the value does not fit the destination) falls back to json.NewDecoder(r.Body).Decode as before, so error behaviour is unchanged. BindJSONValue does the binding by reflection, following encoding/json's rules for struct tags, embedded struct promotion, json.Unmarshaler and encoding.TextUnmarshaler, base64 []byte and float64 for any, with exact member-name matching. The destination is left untouched when binding fails. Part of oapi-codegen/oapi-codegen#2605. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Fix lint findings in bindjson_test.go golangci-lint flagged two things in the bindJSONAll fixture struct: - staticcheck SA5008 on the `json:"-,"` tag. That tag is intentional: it names the member "-" (the encoding/json documented way to do so), and TestBindJSONValue_FieldCacheIsStable asserts the binder resolves it that way. Keep the tag and silence the check with an explanatory nolint. - unused on the `unexported` field. The field exists so the fixture exercises the binder skipping unexported fields. Reference it with an assertion that it is never set, which both documents the intent and satisfies the linter. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
1 parent c702e06 commit 170896a

3 files changed

Lines changed: 1060 additions & 0 deletions

File tree

‎README.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,26 @@ correct, media-type-defined behavior.
221221
their original precision, but the round-trip through JSON means struct
222222
field tags are honored (not raw Go field names).
223223

224+
225+
## Request body binding
226+
227+
`BindJSONBody` is what strict net/http servers generated by `oapi-codegen` call
228+
to decode a JSON request body. When the request has passed through a validation
229+
middleware built on kin-openapi's `openapi3filter` with
230+
`Options.KeepValidatedValue` set (for example
231+
`nethttp-middleware.OapiRequestValidatorWithOptions`), the body carries the
232+
value the validator decoded and validated, and `BindJSONBody` binds that value
233+
into the request struct with `BindJSONValue` instead of decoding the body a
234+
second time.
235+
236+
Binding follows `encoding/json`'s rules (struct tags, embedded structs,
237+
`json.Unmarshaler` / `encoding.TextUnmarshaler`, base64 for `[]byte`, `float64`
238+
for `any`), with one deliberate difference: object member names are matched to
239+
struct fields **exactly**, whereas `encoding/json` matches them
240+
case-insensitively. This closes the gap where `{"role":"user","ROLE":"admin"}`
241+
validates as `role: user` but a second `encoding/json` decode binds
242+
`Role: "admin"`.
243+
244+
Without a validated value, or when the value cannot be bound into the
245+
destination type, `BindJSONBody` decodes the body with `encoding/json` exactly
246+
as before, so error responses are unchanged.

0 commit comments

Comments
 (0)