diff --git a/example/httpserver/response_with_json_array/controller.go b/example/httpserver/response_with_json_array/controller.go new file mode 100644 index 000000000..8f53c204a --- /dev/null +++ b/example/httpserver/response_with_json_array/controller.go @@ -0,0 +1,33 @@ +package main + +import ( + "context" + + "github.com/gogf/gf/v2/frame/g" +) + +type Req struct { + g.Meta `path:"/user" method:"get"` +} +type Res []Item + +type Item struct { + Id int64 + Name string +} + +var ( + User = cUser{} +) + +type cUser struct{} + +func (c *cUser) GetList(ctx context.Context, req *Req) (res *Res, err error) { + res = &Res{ + {Id: 1, Name: "john"}, + {Id: 2, Name: "smith"}, + {Id: 3, Name: "alice"}, + {Id: 4, Name: "katyusha"}, + } + return +} diff --git a/example/httpserver/response_with_json_array/main.go b/example/httpserver/response_with_json_array/main.go new file mode 100644 index 000000000..7b2de30d7 --- /dev/null +++ b/example/httpserver/response_with_json_array/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" +) + +func main() { + s := g.Server() + s.Group("/", func(group *ghttp.RouterGroup) { + group.Middleware(ghttp.MiddlewareHandlerResponse) + group.Bind( + User, + ) + }) + oai := s.GetOpenApi() + oai.Config.CommonResponse = ghttp.DefaultHandlerResponse{} + oai.Config.CommonResponseDataField = "Data" + s.SetOpenApiPath("/api") + s.SetSwaggerPath("/swagger") + s.SetPort(8199) + s.Run() +} diff --git a/protocol/goai/goai_path.go b/protocol/goai/goai_path.go index 4f9125f82..d60b912e4 100644 --- a/protocol/goai/goai_path.go +++ b/protocol/goai/goai_path.go @@ -213,9 +213,9 @@ func (oai *OpenApiV3) addPath(in addPathInput) error { contentTypes = oai.Config.ReadContentTypes tagMimeValue = gmeta.Get(outputObject.Interface(), TagNameMime).String() refInput = getResponseSchemaRefInput{ - BusinessStructName: outputStructTypeName, - ResponseObject: oai.Config.CommonResponse, - ResponseDataField: oai.Config.CommonResponseDataField, + BusinessStructName: outputStructTypeName, + CommonResponseObject: oai.Config.CommonResponse, + CommonResponseDataField: oai.Config.CommonResponseDataField, } ) if tagMimeValue != "" { @@ -224,8 +224,8 @@ func (oai *OpenApiV3) addPath(in addPathInput) error { for _, v := range contentTypes { // If customized response mime type, it then ignores common response feature. if tagMimeValue != "" { - refInput.ResponseObject = nil - refInput.ResponseDataField = "" + refInput.CommonResponseObject = nil + refInput.CommonResponseDataField = "" } schemaRef, err := oai.getResponseSchemaRef(refInput) if err != nil { diff --git a/protocol/goai/goai_response.go b/protocol/goai/goai_response.go index e85b04254..a7a73620c 100644 --- a/protocol/goai/goai_response.go +++ b/protocol/goai/goai_response.go @@ -38,33 +38,33 @@ func (r ResponseRef) MarshalJSON() ([]byte, error) { } type getResponseSchemaRefInput struct { - BusinessStructName string // The business struct name. - ResponseObject interface{} // Common response object. - ResponseDataField string // Common response data field. + BusinessStructName string // The business struct name. + CommonResponseObject interface{} // Common response object. + CommonResponseDataField string // Common response data field. } func (oai *OpenApiV3) getResponseSchemaRef(in getResponseSchemaRefInput) (*SchemaRef, error) { - if in.ResponseObject == nil { + if in.CommonResponseObject == nil { return &SchemaRef{ Ref: in.BusinessStructName, }, nil } var ( - dataFieldsPartsArray = gstr.Split(in.ResponseDataField, ".") + dataFieldsPartsArray = gstr.Split(in.CommonResponseDataField, ".") bizResponseStructSchemaRef, bizResponseStructSchemaRefExist = oai.Components.Schemas[in.BusinessStructName] - schema, err = oai.structToSchema(in.ResponseObject) + schema, err = oai.structToSchema(in.CommonResponseObject) ) if err != nil { return nil, err } - if in.ResponseDataField == "" && bizResponseStructSchemaRefExist { + if in.CommonResponseDataField == "" && bizResponseStructSchemaRefExist { for k, v := range bizResponseStructSchemaRef.Value.Properties { schema.Properties[k] = v } } else { structFields, _ := gstructs.Fields(gstructs.FieldsInput{ - Pointer: in.ResponseObject, + Pointer: in.CommonResponseObject, RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag, }) for _, structField := range structFields { @@ -86,9 +86,9 @@ func (oai *OpenApiV3) getResponseSchemaRef(in getResponseSchemaRefInput) (*Schem structFieldInstance = reflect.New(structField.Type().Type).Elem() ) schemaRef, err := oai.getResponseSchemaRef(getResponseSchemaRefInput{ - BusinessStructName: in.BusinessStructName, - ResponseObject: structFieldInstance, - ResponseDataField: gstr.Join(dataFieldsPartsArray[1:], "."), + BusinessStructName: in.BusinessStructName, + CommonResponseObject: structFieldInstance, + CommonResponseDataField: gstr.Join(dataFieldsPartsArray[1:], "."), }) if err != nil { return nil, err diff --git a/protocol/goai/goai_shema.go b/protocol/goai/goai_shema.go index ada8c6e9c..dceebdc5e 100644 --- a/protocol/goai/goai_shema.go +++ b/protocol/goai/goai_shema.go @@ -10,6 +10,7 @@ import ( "reflect" "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/internal/utils" "github.com/gogf/gf/v2/os/gstructs" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" @@ -64,6 +65,8 @@ type Discriminator struct { Mapping map[string]string `json:"mapping,omitempty" yaml:"mapping,omitempty"` } +// addSchema creates schemas with objects. +// Note that the `object` can be array alias like: `type Res []Item`. func (oai *OpenApiV3) addSchema(object ...interface{}) error { for _, v := range object { if err := oai.doAddSchemaSingle(v); err != nil { @@ -104,10 +107,6 @@ func (oai *OpenApiV3) doAddSchemaSingle(object interface{}) error { // structToSchema converts and returns given struct object as Schema. func (oai *OpenApiV3) structToSchema(object interface{}) (*Schema, error) { - structFields, _ := gstructs.Fields(gstructs.FieldsInput{ - Pointer: object, - RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag, - }) var ( tagMap = gmeta.Data(object) schema = &Schema{ @@ -123,6 +122,21 @@ func (oai *OpenApiV3) structToSchema(object interface{}) (*Schema, error) { if schema.Type != "" && schema.Type != TypeObject { return schema, nil } + // []struct. + if utils.IsArray(object) { + schema.Type = TypeArray + subSchemaRef, err := oai.newSchemaRefWithGolangType(reflect.TypeOf(object).Elem(), nil) + if err != nil { + return nil, err + } + schema.Items = subSchemaRef + return schema, nil + } + // struct. + structFields, _ := gstructs.Fields(gstructs.FieldsInput{ + Pointer: object, + RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag, + }) schema.Type = TypeObject for _, structField := range structFields { if !gstr.IsLetterUpper(structField.Name()[0]) {