Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions huma.go
Original file line number Diff line number Diff line change
Expand Up @@ -1185,9 +1185,22 @@ func processInputType(inputType reflect.Type, op *Operation, registry Registry)
}

var inSchema *Schema
if op.RequestBody != nil && op.RequestBody.Content != nil && op.RequestBody.Content["application/json"] != nil && op.RequestBody.Content["application/json"].Schema != nil {
hasInputBody = true
inSchema = op.RequestBody.Content["application/json"].Schema
if op.RequestBody != nil && op.RequestBody.Content != nil {
// Try to get schema from any available content type.
// Prefer application/json for backwards compatibility, then try others.
if op.RequestBody.Content["application/json"] != nil && op.RequestBody.Content["application/json"].Schema != nil {
hasInputBody = true
inSchema = op.RequestBody.Content["application/json"].Schema
} else {
// Fall back to first available content type with a schema.
for _, mediaType := range op.RequestBody.Content {
if mediaType.Schema != nil {
hasInputBody = true
inSchema = mediaType.Schema
break
}
}
}
}
return inputParams, inputBodyIndex, hasInputBody, rawBodyIndex, rbt, inSchema
}
Expand Down
41 changes: 41 additions & 0 deletions huma_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package huma_test

import (
"bytes"
"context"
"encoding/json"
"errors"
Expand All @@ -24,6 +25,7 @@ import (

"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humago"
_ "github.com/danielgtaylor/huma/v2/formats/cbor"
"github.com/danielgtaylor/huma/v2/humatest"
)

Expand Down Expand Up @@ -3295,3 +3297,42 @@ func TestGenerateFuncsPanicWithDescriptiveMessage(t *testing.T) {
})

}

func TestNonJSONValidation(t *testing.T) {
// Test that validation works when only non-JSON content type is specified.
// This tests the fix for supporting validation schemas from non-JSON content types.
type CBORInput struct {
Body struct {
Name string `json:"name" minLength:"2" doc:"User name"`
} `contentType:"application/cbor"`
}

// Use default config which includes CBOR via the import above.
_, api := humatest.New(t, huma.DefaultConfig("Test", "1.0.0"))

huma.Post(api, "/cbor-test", func(ctx context.Context, input *CBORInput) (*struct{}, error) {
return &struct{}{}, nil
})

// Check that CBOR is in the OpenAPI spec (not JSON).
op := api.OpenAPI().Paths["/cbor-test"].Post
assert.NotNil(t, op.RequestBody)
assert.Contains(t, op.RequestBody.Content, "application/cbor")
assert.NotContains(t, op.RequestBody.Content, "application/json")

// Marshal valid data as CBOR.
var validBuf bytes.Buffer
huma.DefaultFormats["application/cbor"].Marshal(&validBuf, map[string]any{"name": "John"})

// Valid CBOR request should work (name >= 2 chars).
w := api.Post("/cbor-test", "Content-Type: application/cbor", &validBuf)
assert.Equal(t, 204, w.Code)

// Marshal invalid data as CBOR (name < 2 chars).
var invalidBuf bytes.Buffer
huma.DefaultFormats["application/cbor"].Marshal(&invalidBuf, map[string]any{"name": "J"})

// Invalid CBOR request should fail validation.
w = api.Post("/cbor-test", "Content-Type: application/cbor", &invalidBuf)
assert.Equal(t, 422, w.Code)
}