From b86106b34e6ca90b80dfaf3c5f0707c94cd8adb2 Mon Sep 17 00:00:00 2001 From: "Cui.Jiaqing" <291527070@qq.com> Date: Mon, 22 Jan 2024 17:25:20 +0800 Subject: [PATCH] openapi --- .../simpleweb/contollers/usercontroller.go | 15 ++-- examples/simpleweb/main.go | 5 +- pkg/swagger/openapi.go | 7 ++ pkg/swagger/swagger_path.go | 1 + web/endpoints/swagger.go | 89 +++++++++++++++++-- 5 files changed, 100 insertions(+), 17 deletions(-) create mode 100644 pkg/swagger/openapi.go diff --git a/examples/simpleweb/contollers/usercontroller.go b/examples/simpleweb/contollers/usercontroller.go index 2c7b66a..f545037 100644 --- a/examples/simpleweb/contollers/usercontroller.go +++ b/examples/simpleweb/contollers/usercontroller.go @@ -41,11 +41,10 @@ func (controller UserController) Register(ctx *context.HttpContext, request *Reg } type PostUserInfoRequest struct { - mvc.RequestBody //`route:"/{id}"` - - UserName string `form:"userName" json:"userName"` - Password string `form:"password" json:"password"` - Token string `header:"Authorization" json:"token"` + mvc.RequestBody //`route:"/{id}"` + UserName string `form:"userName" json:"userName"` + Password string `form:"password" json:"password"` + Token string `header:"Authorization" json:"token"` } func (controller UserController) PostUserInfo(ctx *context.HttpContext, request *PostUserInfoRequest) actionresult.IActionResult { @@ -105,7 +104,7 @@ type UserInfo struct { Image *multipart.FileHeader `form:"file"` } -//FromBody +// FromBody func (controller UserController) DefaultBinding(ctx *context.HttpContext) mvc.ApiResult { userInfo := &UserInfo{} err := ctx.Bind(userInfo) @@ -115,7 +114,7 @@ func (controller UserController) DefaultBinding(ctx *context.HttpContext) mvc.Ap return controller.OK(userInfo) } -//FromBody +// FromBody func (controller UserController) JsonBinding(ctx *context.HttpContext) mvc.ApiResult { userInfo := &UserInfo{} err := ctx.BindWith(userInfo, binding.JSON) @@ -125,7 +124,7 @@ func (controller UserController) JsonBinding(ctx *context.HttpContext) mvc.ApiRe return controller.OK(userInfo) } -//FromQuery +// FromQuery func (controller UserController) GetQueryBinding(ctx *context.HttpContext) mvc.ApiResult { fmt.Println("进入方法") fmt.Println(controller.config.Get("env")) diff --git a/examples/simpleweb/main.go b/examples/simpleweb/main.go index 58e7009..c0f5f31 100644 --- a/examples/simpleweb/main.go +++ b/examples/simpleweb/main.go @@ -9,6 +9,7 @@ import ( _ "github.com/yoyofx/yoyogo/pkg/datasources/mysql" _ "github.com/yoyofx/yoyogo/pkg/datasources/redis" "github.com/yoyofx/yoyogo/pkg/servicediscovery/nacos" + "github.com/yoyofx/yoyogo/pkg/swagger" "github.com/yoyofx/yoyogo/web" "github.com/yoyofx/yoyogo/web/context" "github.com/yoyofx/yoyogo/web/endpoints" @@ -104,7 +105,9 @@ func registerEndpointRouterConfig(rb router.IRouterBuilder) { endpoints.UseLiveness(rb) endpoints.UseJwt(rb) endpoints.UseRouteInfo(rb) - endpoints.UseSwaggerUI(rb) + endpoints.UseSwaggerUI(rb, func() swagger.Info { + return swagger.Info{} + }) rb.GET("/error", func(ctx *context.HttpContext) { panic("http get error") diff --git a/pkg/swagger/openapi.go b/pkg/swagger/openapi.go new file mode 100644 index 0000000..ca29e21 --- /dev/null +++ b/pkg/swagger/openapi.go @@ -0,0 +1,7 @@ +package swagger + +type OpenApi struct { + Openapi string `json:"openapi"` + Info Info `json:"info"` + Paths map[string]map[string]Path `json:"paths"` +} diff --git a/pkg/swagger/swagger_path.go b/pkg/swagger/swagger_path.go index 0327942..ba4d7fa 100644 --- a/pkg/swagger/swagger_path.go +++ b/pkg/swagger/swagger_path.go @@ -3,6 +3,7 @@ package swagger type Path struct { Tags []string `json:"tags"` Summary string `json:"summary"` + Description string `json:"description"` OperationId string `json:"operationId"` Parameters []Parameters `json:"parameters"` RequestBody RequestBody `json:"requestBody"` diff --git a/web/endpoints/swagger.go b/web/endpoints/swagger.go index 9999fc7..02d8b17 100644 --- a/web/endpoints/swagger.go +++ b/web/endpoints/swagger.go @@ -1,22 +1,95 @@ package endpoints import ( - "fmt" "github.com/yoyofx/yoyogo/abstractions/xlog" + "github.com/yoyofx/yoyogo/pkg/swagger" "github.com/yoyofx/yoyogo/web/actionresult" "github.com/yoyofx/yoyogo/web/context" + "github.com/yoyofx/yoyogo/web/mvc" "github.com/yoyofx/yoyogo/web/router" + "github.com/yoyofxteam/reflectx" + "strings" ) -func UseSwaggerUI(router router.IRouterBuilder) { - xlog.GetXLogger("Endpoint").Debug("loaded swagger ui endpoint.") +func GetAllController(router router.IRouterBuilder) { + builder := router.GetMvcBuilder() + controllerList := builder.GetControllerDescriptorList() + for _, controller := range controllerList { + FilterValidParams(controller) + } +} + +func FilterValidParams(controller mvc.ControllerDescriptor) map[string]swagger.Path { + pathMap := make(map[string]swagger.Path) + suf := len(controller.ControllerName) - 10 + basePath := controller.ControllerName[0:suf] + for _, act := range controller.GetActionDescriptors() { + if len(act.MethodInfo.Parameters) > 0 { + for _, param := range act.MethodInfo.Parameters { + // 跳过HttpContext + if param.Name == "HttpContext" { + continue + } + // 跳过控制器 + if strings.HasSuffix(param.Name, "Controller") { + continue + } + // 拼接api路径 + actPath := basePath + "/" + act.ActionName[len(act.ActionMethod):] + pathInfo := swagger.Path{} + pathMap[actPath] = pathInfo + paramSourceData := param.ParameterType.Elem() + fieldNum := paramSourceData.NumField() + if act.MethodInfo.Name == "post" { + pathInfo.RequestBody = RequestBody(param) + } + for i := 0; i < fieldNum; i++ { + // 获取方法注释 + filed := paramSourceData.Field(i) + if filed.Type.Name() == "RequestBody" { + pathInfo.Description = filed.Tag.Get("note") + pathInfo.Summary = filed.Tag.Get("note") + } + } + } + } + + } + return pathMap +} + +func RequestBody(param reflectx.MethodParameterInfo) swagger.RequestBody { + paramSourceData := param.ParameterType.Elem() + fieldNum := paramSourceData.NumField() + // 获取BODY参数注释 + requestBody := swagger.RequestBody{} + contentType := make(map[string]swagger.ContentType) + requestBody.Content = contentType + schema := swagger.Schema{} + schema.Type = "object" + schemaProperties := make(map[string]swagger.Property) + schema.Properties = schemaProperties + for i := 0; i < fieldNum; i++ { + filed := paramSourceData.Field(i) + property := swagger.Property{} + property.Type = filed.Type.Name() + property.Description = filed.Tag.Get("note") + schemaProperties[filed.Name] = property + } + content := swagger.ContentType{} + content.Schema = schema + contentType["application/json"] = content + return requestBody +} + +func UseSwaggerUI(router router.IRouterBuilder, f func() swagger.Info) { + xlog.GetXLogger("Endpoint").Debug("loaded swagger ui endpoint.") + openapi := swagger.OpenApi{} + openapi.Openapi = "3.0.3" + openapi.Info = f() router.GET("/swagger.json", func(ctx *context.HttpContext) { - builder := router.GetMvcBuilder() - con := builder.GetControllerDescriptorByName("usercontroller") - action, _ := con.GetActionDescriptorByName("register") - info := action.MethodInfo.Parameters[2] - fmt.Println(info.ParameterType.Field(2).Tag.Get("json")) + GetAllController(router) swaggerJson := `{ "swagger": "2.0", "info": {