OpenAPIv3协议主要使用在规范路由中,阅读接口文档协议介绍之前,请先了解一下规范路由:路由注册-规范路由

一、OpenAPIv3

详细的OpenAPIv3协议介绍请参考:https://swagger.io/specification/

二、g.Meta元数据

接口的元数据信息可以通过为输入结构体 embedded 方式嵌入 g.Meta 结构,并通过 g.Meta 的属性标签方式来实现。

关于元数据组件的介绍,详情请参考章节:元数据-gmeta 

三、常用协议标签

输入输出结构体中的属性的标签完整支持OpenAPIv3协议,因此只要增加了对应的协议标签,那么生成的OpenAPIv3接口信息中将会自动包含该属性。

大部分的标签属性已经被Server组件自动生成,开发者需要手动设置的标签不多。

1、基础标签

常见的基础标签包括:

常见OpenAPIv3标签说明备注
path 结合注册时的前缀共同构成接口URI路径用于g.Meta标识接口元数据
tags 接口所属的标签,用于接口分类用于g.Meta标识接口元数据
method 接口的请求方式:GET/PUT/POST/DELETE...(不区分大小写)用于g.Meta标识接口元数据
deprecated 标记该接口废弃用于g.Meta标识接口元数据
summary 接口/参数概要描述缩写sm
description 接口/参数详细描述缩写dc
in 参数的提交方式header/path/query/cookie
default 参数的默认值缩写d
mime接口的MIME类型,例如multipart/form-data一般是全局设置,默认为application/json用于g.Meta标识接口元数据
type参数的类型,一般不需要设置,特殊参数需要手动设置,例如file仅用于参数属性

更多标签请参考标准的OpenAPIv3协议:https://swagger.io/specification/

2、扩展标签

OpenAPI规范里面,所有名称以x-开头的标签是开发者可自定义的扩展标签。扩展标签可以在任意的接口、属性中以Golang struct tag的形式定义,在接口文档生成时,将会作为独立的字段返回。例如:

package main

import (
	"context"

	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

type GetListReq struct {
	g.Meta `path:"/user" tags:"User" method:"get" x-group:"User/Info" summary:"Get user list with basic info."`
	Page   int `dc:"Page number" d:"1" x-sort:"1"`
	Size   int `dc:"Size for per page." d:"10" x-sort:"2"`
}
type GetListRes struct{}

type Controller struct{}

func (c *Controller) GetList(ctx context.Context, req *GetListReq) (res *GetListRes, err error) {
	return nil, gerror.NewCode(gcode.CodeNotImplemented)
}

func main() {
	s := g.Server()
	s.Group("/", func(group *ghttp.RouterGroup) {
		group.Bind(new(Controller))
	})
	s.SetOpenApiPath("/api.json")
	s.SetSwaggerPath("/swagger")
	s.SetPort(8199)
	s.Run()
}

执行后,访问地址 http://127.0.0.1:8199/swagger 可以查看swagger ui,访问 http://127.0.0.1:8199/api.json 可以查看对应的OpenAPIv3接口文档。其中生成的OpenAPIv3接口文档如下:

{
    "openapi": "3.0.0",
    "components": {
        "schemas": {
            "main.GetListReq": {
                "properties": {
                    "Page": {
                        "default": 1,
                        "description": "Page number",
                        "format": "int",
                        "properties": {},
                        "type": "integer",
                        "x-sort": "1"
                    },
                    "Size": {
                        "default": 10,
                        "description": "Size for per page.",
                        "format": "int",
                        "properties": {},
                        "type": "integer",
                        "x-sort": "2"
                    }
                },
                "type": "object",
                "x-group": "User/Info"
            },
            "main.GetListRes": {
                "properties": {},
                "type": "object"
            }
        }
    },
    "info": {
        "title": "",
        "version": ""
    },
    "paths": {
        "/user": {
            "get": {
                "parameters": [
                    {
                        "description": "Page number",
                        "in": "query",
                        "name": "Page",
                        "schema": {
                            "default": 1,
                            "description": "Page number",
                            "format": "int",
                            "properties": {},
                            "type": "integer",
                            "x-sort": "1"
                        },
                        "x-sort": "1"
                    },
                    {
                        "description": "Size for per page.",
                        "in": "query",
                        "name": "Size",
                        "schema": {
                            "default": 10,
                            "description": "Size for per page.",
                            "format": "int",
                            "properties": {},
                            "type": "integer",
                            "x-sort": "2"
                        },
                        "x-sort": "2"
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/main.GetListRes"
                                }
                            }
                        },
                        "description": ""
                    }
                },
                "summary": "Get user list with basic info.",
                "tags": [
                    "User"
                ],
                "x-group": "User/Info"
            },
            "x-group": "User/Info"
        }
    }
}

可以看到,扩展标签已经生成到了接口文档中。

四、扩展OpenAPIv3信息

核心的接口信息已经自动生成,如果开发者想要更进一步完善接口信息,可以通过s.GetOpenApi()接口获取到OpenAPIv3的结构体对象,并手动填充对应的属性内容即可。我们来看一个示例,在该示例中,我们设计每个接口外层公共的数据结构:

我们可以发现通过通用的OpenAPIv3对象我们可以自定义修改其内容,并且根据它生成其他各种自定义类型的接口文档。

Content Menu

  • No labels

38 Comments

  1. swagger页面在2.0.6版本只能本地访问,这个要怎么设置

    1. 请问解决了吗?

  2. 用配置文件设置的接口文档页面 请求的JS 不能用 https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js  

  3. /store/inventory:
        get:
          tags:
          - store
          summary: Returns pet inventories by status
          description: Returns a map of status codes to quantities
          operationId: getInventory
          responses:
            200:
              description: successful operation
              content:
                application/json:
                  schema:
                    type: object
                    additionalProperties:
                      type: integer
                      format: int32
          security:
          - api_key: []

    像这种security 应该怎么写参数, 还是没有实现这部分?

    1. 请问解决了吗?

      1. type FindUserByIdReq struct {
        g.Meta `path:"/user/find" method:"get" tags:"[\"tag01\", \"tag02\"]" security:"[{\"api_key\":[]}]" summary:"Find user by id"`
        Id int `json:"id" v:"required#请输入id" dc:"主键"`
        }
  4. 如何定义多个 responses 

    目前按照文档定义都是生成 200 状态码的 response,如果想自定义 403,401,500 等状态码,对应 openapiv3 里面的 paths.response.xxx ,如何定义呢?

    1. 嗨,请问解决了吗

  5. 请问能自定义API的排列顺序吗?现在看起来顺序是按path生成的

  6. method方式如何同时支持get和post? 找了半天也没找到例子,有谁知道吗
    1. 应该不定义method就可以了吧你试试

    2. 你解决了吗?不定义就要报错

        1. 新版本应该可以 method:"get,post"

  7. dc和sm对于struct类型的生成swagger不起作用!

  8. type LoginReq struct {
    AppId string `json:"app_id" in:"header" v:"required|length:5,32#应用ID不能为空|应用ID长度无效" dc:"应用ID"`
    Nonce string `json:"nonce" in:"header" v:"required|length:5,32#1公共参数不能为空|公共参数长度无效" dc:"随机数"`
    DateTime gtime.Time `json:"date_time" in:"header" v:"required|datetime#2公共参数不能为空|公共参数长度无效" dc:"请求时间"`
    TimeZone string `json:"time_zone" in:"header" v:"required|length:1,10#3公共参数不能为空|公共参数长度无效" d:"PRC" dc:"请求时区"`
    Sign string `json:"sign" in:"header" v:"required|size:32#4公共参数不能为空|公共参数长度无效" dc:"签名"`
    }
    使用 PostMan header 提交 app_id 参数 提示 应用ID不能为空
    1. 功能暂未实现,暂时在控制器内手动绑定并校验

      1. 后续会支持吗?

  9. group.PUT("/user", controller.User)

    手动注册路由,如果添加到swagger中去呢?

  10. openapi := s.GetOpenApi()
    openapi.Config.CommonResponse = ghttp.DefaultHandlerResponse{}
    openapi.Config.CommonResponseDataField = `Data`

    通过这个设置,可以将文档的返回结果与实际结果对应。
    但是 CommonResponseDataField 设置的字段只关联了类容没有关联 xxxRes 这个类型
    最终 data 的类型直接是Object 导致部分UI无法正常展开接口文档的结构。

    郭强 

  11. 目前的请求/响应结构体前缀路径使用的是 go.mod ,如:github.com-aaa-bbb.api.hello.v1.HelloReq ,如何自定义修改前缀 github.com-aaa-bbb ??? 郭强 

    因为在与第三方对接时,不希望把项目的完整地址展示出去,可能只需要把 github.com-aaa-bbb ==> aaa-bbb 

  12. 默认生成的response的body定义只有自定义返回数据的格式, 请问怎么才能把默认的code,message和data,这3个字段也加到定义里面

    "gopkt.api.records.v1.RecordsListRes": {
                    	"properties": {
                        "pcapFiles": {
                            "description": "pcap数据文件",
                            "format": "[]string",
                            "items": {
                                "format": "string",
                                "properties": {},
                                "type": "string"
                            },
                            "properties": {},
                            "type": "array"
                        },
                        "csvFiles": {
                            "description": "csv数据文件",
                            "format": "[]string",
                            "items": {
                                "format": "string",
                                "properties": {},
                                "type": "string"
                            },
                            "properties": {},
                            "type": "array"
                        }
                    },
                    "type": "object"
                },
  13. cgd

    func (c *cSms) Send(ctx context.Context, req *v1.SmsSendReq) (res *v1.SmsSendRes, err error) {
    err = service.Sms().Send(ctx, service.Params{
    Mobile: req.Mobile,
    HandleName: req.HandleName,
    Params: req.Params,
    AccountKind: req.AccountKind,
    ReceiveType: req.ReceiveType,
    HospitalId: req.HospitalId,
    SourceId: req.SourceId,
    Source: req.Source,
    IsVerifyCode: req.IsVerifyCode,
    })
    return
    }
    controller中调用service层的功能,请问在service层如何获取请求的IP啊?
    1. g.RequestFromCtx(ctx).GetClientIP()

  14. 如何定义一个符合OpenAPIv3标准的上传文件接口tag, 目前

    type MediaUploadReq struct {
    	g.Meta `path:"/media/upload" method:"post" mime:"multipart/form-data" type:"file" tags:"media" summary:"Upload media"`
    	File   *ghttp.UploadFile `json:"file" type:"file"`
    }

    目前这种定义生成的json不是标准的OpenApiV3格式, 导致SwaggerUI 中Try It Out 不能识别到是文件, 无法直接在swagger中做测试

    1. yud

      g.Meta的tags里,加上format:"binary" 试试呢 

      1. 加上format:"binary"还是没有识别为file类型

    2. 该问题已在2.6中解决

  15. 能不能指定某个字段不被解析为参数?如: 

    type tagsSearchReq struct {
       g.Meta `path:"/list" tags:"标签管理" method:"get" summary:"列表"`
       Name string `p:"name"`
       Tenant string 
    }

    中的Tenant 我不想做为参数给用户传,也不显示在openapi文档上。

  16. data能提取一个ref出来吗?我的场景是code-gen自动生成前端代码,由于没有确切类型和名字,全部生成为了xxxInLine名字的文件。

  17. Aii

    能单独隐藏某个路径吗?


  18. type QueryUserReq struct {
    g.Meta `path:"/user" tags:"User" method:"get" summary:"query user by username" type:"http" scheme:"access-token" bearerFormat:"JWT"`
    Username string `json:"username" dc:"username" v:"required#username is required" in:"query"`
    Authorization string `dc:"Authorization" name:"Authorization" in:"header"`
    }

    通过swagger测试jwt的时候,发现请求体里的认证请求体不能被请求带上。

    请问一下是我使用的方式有问题还是说目前swagger不支持这样

  19.  type AuthorizeRes struct {
    	g.Meta                `mime:"application/json"`
    	UserBindPara          map[string]interface{} `json:"user_bind_para,omitempty" dc:"绑定参数包" example:"{\"a\":\"是否支持\"}"`
    }

    想要对此处的 UserBindPara 做一些案例说明,但是如果把 example写成json,在API UI上出来的example是字符串不是kv结构的object, 如果不写example 出来的就是object,但是key是随机的

    因为这边的第三方接口返回的内容,根据供应商的不同 返回的结构也不同,没法写成struct

  20. 可以设置全局header么?