fix: support PUT, PATCH, DELETE methods for request body definitions in swagger (#5239)

This commit is contained in:
Kevin Wan
2025-10-12 18:24:11 +08:00
committed by GitHub
parent 80c320b46e
commit 2b10a6a223
2 changed files with 36 additions and 14 deletions

View File

@@ -8,28 +8,37 @@ import (
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
)
func isPostJson(ctx Context, method string, tp apiSpec.Type) (string, bool) {
if !strings.EqualFold(method, http.MethodPost) {
func isRequestBodyJson(ctx Context, method string, tp apiSpec.Type) (string, bool) {
// Support HTTP methods that commonly use request bodies with JSON
// POST, PUT, PATCH are standard methods with bodies
// DELETE can also have a body (though less common)
method = strings.ToUpper(method)
if method != http.MethodPost && method != http.MethodPut &&
method != http.MethodPatch && method != http.MethodDelete {
return "", false
}
structType, ok := tp.(apiSpec.DefineStruct)
if !ok {
return "", false
}
var isPostJson bool
var hasJsonField bool
rangeMemberAndDo(ctx, structType, func(tag *apiSpec.Tags, required bool, member apiSpec.Member) {
jsonTag, _ := tag.Get(tagJson)
if !isPostJson {
isPostJson = jsonTag != nil
if !hasJsonField {
hasJsonField = jsonTag != nil
}
})
return structType.RawName, isPostJson
return structType.RawName, hasJsonField
}
func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Parameter {
if tp == nil {
return []spec.Parameter{}
}
structType, ok := tp.(apiSpec.DefineStruct)
if !ok {
return []spec.Parameter{}
@@ -43,15 +52,13 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para
rangeMemberAndDo(ctx, structType, func(tag *apiSpec.Tags, required bool, member apiSpec.Member) {
headerTag, _ := tag.Get(tagHeader)
hasHeader := headerTag != nil
pathParameterTag, _ := tag.Get(tagPath)
hasPathParameter := pathParameterTag != nil
formTag, _ := tag.Get(tagForm)
hasForm := formTag != nil
jsonTag, _ := tag.Get(tagJson)
hasJson := jsonTag != nil
if hasHeader {
minimum, maximum, exclusiveMinimum, exclusiveMaximum := rangeValueFromOptions(headerTag.Options)
resp = append(resp, spec.Parameter{
@@ -75,6 +82,7 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para
},
})
}
if hasPathParameter {
minimum, maximum, exclusiveMinimum, exclusiveMaximum := rangeValueFromOptions(pathParameterTag.Options)
resp = append(resp, spec.Parameter{
@@ -98,6 +106,7 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para
},
})
}
if hasForm {
minimum, maximum, exclusiveMinimum, exclusiveMaximum := rangeValueFromOptions(formTag.Options)
if strings.EqualFold(method, http.MethodGet) {
@@ -145,8 +154,8 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para
},
})
}
}
if hasJson {
minimum, maximum, exclusiveMinimum, exclusiveMaximum := rangeValueFromOptions(jsonTag.Options)
if required {
@@ -179,9 +188,10 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para
properties[jsonTag.Name] = schema
}
})
if len(properties) > 0 {
if ctx.UseDefinitions {
structName, ok := isPostJson(ctx, method, tp)
structName, ok := isRequestBodyJson(ctx, method, tp)
if ok {
resp = append(resp, spec.Parameter{
ParamProps: spec.ParamProps{
@@ -213,5 +223,6 @@ func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Para
})
}
}
return resp
}

View File

@@ -8,7 +8,7 @@ import (
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
)
func TestIsPostJson(t *testing.T) {
func TestIsRequestBodyJson(t *testing.T) {
tests := []struct {
name string
method string
@@ -18,13 +18,18 @@ func TestIsPostJson(t *testing.T) {
{"POST with JSON", http.MethodPost, true, true},
{"POST without JSON", http.MethodPost, false, false},
{"GET with JSON", http.MethodGet, true, false},
{"PUT with JSON", http.MethodPut, true, false},
{"PUT with JSON", http.MethodPut, true, true},
{"PUT without JSON", http.MethodPut, false, false},
{"PATCH with JSON", http.MethodPatch, true, true},
{"PATCH without JSON", http.MethodPatch, false, false},
{"DELETE with JSON", http.MethodDelete, true, true},
{"DELETE without JSON", http.MethodDelete, false, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
testStruct := createTestStruct("TestStruct", tt.hasJson)
_, result := isPostJson(testingContext(t), tt.method, testStruct)
_, result := isRequestBodyJson(testingContext(t), tt.method, testStruct)
assert.Equal(t, tt.expected, result)
})
}
@@ -41,6 +46,12 @@ func TestParametersFromType(t *testing.T) {
}{
{"POST JSON with definitions", http.MethodPost, true, true, 1, true},
{"POST JSON without definitions", http.MethodPost, false, true, 1, true},
{"PUT JSON with definitions", http.MethodPut, true, true, 1, true},
{"PUT JSON without definitions", http.MethodPut, false, true, 1, true},
{"PATCH JSON with definitions", http.MethodPatch, true, true, 1, true},
{"PATCH JSON without definitions", http.MethodPatch, false, true, 1, true},
{"DELETE JSON with definitions", http.MethodDelete, true, true, 1, true},
{"DELETE JSON without definitions", http.MethodDelete, false, true, 1, true},
{"GET with form", http.MethodGet, false, false, 1, false},
{"POST with form", http.MethodPost, false, false, 1, false},
}