From c475605a9d3130086535281a677370144c05ae85 Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Tue, 20 Feb 2024 08:11:24 -0800 Subject: [PATCH] feat: better control over object additionalProperties --- docs/docs/features/request-validation.md | 27 +++++++++++++++++++++++- schema.go | 10 ++++++++- schema_test.go | 17 +++++++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/docs/docs/features/request-validation.md b/docs/docs/features/request-validation.md index 37848a24..d6d239a1 100644 --- a/docs/docs/features/request-validation.md +++ b/docs/docs/features/request-validation.md @@ -6,7 +6,14 @@ description: Built-in JSON Schema validation rules for request parameters and bo ## Request Validation { .hidden } -Go struct tags are used to annotate inputs/output structs with information that gets turned into [JSON Schema](https://json-schema.org/) for documentation and validation. +Go struct tags are used to annotate inputs/output structs with information that gets turned into [JSON Schema](https://json-schema.org/) for documentation and validation. For example: + +```go title="code.go" +type Person struct { + Name string `json:"name" doc:"Person's name" minLength:"1" maxLength:"80"` + Age uint `json:"age,omitempty" doc:"Person's age" maximum:"120"` +} +``` The standard `json` tag is supported and can be used to rename a field and mark fields as optional using `omitempty`. The following additional tags are supported on model fields: @@ -40,6 +47,24 @@ Parameters have some additional validation tags: | -------- | --------------------------------- | --------------- | | `hidden` | Hide parameter from documentation | `hidden:"true"` | +## Strict vs. Loose Field Validation + +By default, Huma is strict about which fields are allowed in an object, making use of the `additionalProperties: false` JSON Schema setting. This means if a client sends a field that is not defined in the schema, the request will be rejected with an error. This can help to prevent typos and other issues and is recommended for most APIs. + +If you need to allow additional fields, for example when using a third-party service which will call your system and you only care about a few fields, you can use the `additionalProperties:"true"` field tag on the struct by assigning it to a dummy `_` field. + +```go title="code.go" +type PartialInput struct { + _ struct{} `json:"-" additionalProperties:"true"` + Field1 string `json:"field1"` + Field2 bool `json:"field2"` +} +``` + +!!! info "Note" + + The use of `struct{}` is optional but efficient. It is used to avoid allocating memory for the dummy field as an empty object requires no space. + ## Advanced Validation When using custom JSON Schemas, i.e. not generated from Go structs, it's possible to utilize a few more validation rules. The following schema fields are respected by the built-in validator: diff --git a/schema.go b/schema.go index 36581af7..1a24ff4e 100644 --- a/schema.go +++ b/schema.go @@ -668,7 +668,15 @@ func SchemaFromType(r Registry, t reflect.Type) *Schema { } } s.Type = TypeObject - s.AdditionalProperties = false + + additionalProps := false + if f, ok := t.FieldByName("_"); ok { + if _, ok := f.Tag.Lookup("additionalProperties"); ok { + additionalProps = boolTag(f, "additionalProperties") + } + } + s.AdditionalProperties = additionalProps + s.Properties = props s.propertyNames = propNames s.Required = required diff --git a/schema_test.go b/schema_test.go index fcfbede2..b6ba6833 100644 --- a/schema_test.go +++ b/schema_test.go @@ -140,6 +140,23 @@ func TestSchema(t *testing.T) { input: map[string]string{"foo": "bar"}, expected: `{"type": "object", "additionalProperties": {"type": "string"}}`, }, + { + name: "additionalProps", + input: struct { + _ struct{} `json:"-" additionalProperties:"true"` + Value string `json:"value"` + }{}, + expected: `{ + "type": "object", + "properties": { + "value": { + "type": "string" + } + }, + "required": ["value"], + "additionalProperties": true + }`, + }, { name: "field-int", input: struct {