diff --git a/huma.go b/huma.go index 21d8725f..9ef44a72 100644 --- a/huma.go +++ b/huma.go @@ -572,30 +572,30 @@ func Register[I, O any](api API, op Operation, handler func(context.Context, *I) hasInputBody = true inputBodyIndex = f.Index if op.RequestBody == nil { - required := f.Type.Kind() != reflect.Ptr && f.Type.Kind() != reflect.Interface - if f.Tag.Get("required") == "true" { - required = true - } + op.RequestBody = &RequestBody{} + } - contentType := "application/json" - if c := f.Tag.Get("contentType"); c != "" { - contentType = c - } - hint := getHint(inputType, f.Name, op.OperationID+"Request") - if nameHint := f.Tag.Get("nameHint"); nameHint != "" { - hint = nameHint - } - s := SchemaFromField(registry, f, hint) + required := f.Type.Kind() != reflect.Ptr && f.Type.Kind() != reflect.Interface + if f.Tag.Get("required") == "true" { + required = true + } - op.RequestBody = &RequestBody{ - Required: required, - Content: map[string]*MediaType{ - contentType: { - Schema: s, - }, - }, - } + contentType := "application/json" + if c := f.Tag.Get("contentType"); c != "" { + contentType = c } + hint := getHint(inputType, f.Name, op.OperationID+"Request") + if nameHint := f.Tag.Get("nameHint"); nameHint != "" { + hint = nameHint + } + s := SchemaFromField(registry, f, hint) + + op.RequestBody.Required = required + + if op.RequestBody.Content == nil { + op.RequestBody.Content = map[string]*MediaType{} + } + op.RequestBody.Content[contentType] = &MediaType{Schema: s} if op.BodyReadTimeout == 0 { // 5 second default @@ -615,10 +615,13 @@ func Register[I, O any](api API, op Operation, handler func(context.Context, *I) if op.RequestBody == nil { op.RequestBody = &RequestBody{ Required: true, - Content: map[string]*MediaType{}, } } + if op.RequestBody.Content == nil { + op.RequestBody.Content = map[string]*MediaType{} + } + contentType := "application/octet-stream" if f.Type.String() == "multipart.Form" { diff --git a/huma_test.go b/huma_test.go index 8f11487b..15240e57 100644 --- a/huma_test.go +++ b/huma_test.go @@ -544,6 +544,42 @@ func TestFeatures(t *testing.T) { // Headers: map[string]string{"Content-Type": "application/json"}, Body: `{"name":"foo"}`, }, + { + Name: "request-body-description", + Register: func(t *testing.T, api huma.API) { + huma.Register(api, huma.Operation{ + Method: http.MethodPut, + Path: "/body", + RequestBody: &huma.RequestBody{ + Description: "A description", + }, + }, func(ctx context.Context, input *struct { + Body struct { + Name string `json:"name"` + } + }) (*struct{}, error) { + assert.Equal(t, "foo", input.Body.Name) + return nil, nil + }) + // Note: the description should be set, but *also* the generated + // schema should be present since we didn't set it up ourselves. + b, _ := api.OpenAPI().Paths["/body"].Put.RequestBody.MarshalJSON() + assert.JSONEq(t, `{ + "description": "A description", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Request" + } + } + } + }`, string(b)) + }, + Method: http.MethodPut, + URL: "/body", + Body: `{"name":"foo"}`, + }, { Name: "request-body-nested-struct-readOnly", Register: func(t *testing.T, api huma.API) {