API Document - OpenAPIv3 - 图1tip

The OpenAPIv3 protocol is mainly used in standardized routing. Before reading the introduction to the API documentation protocol, please familiarize yourself with the standardized routing: Standard Router

I. OpenAPIv3

For a detailed introduction to the OpenAPIv3 protocol, please refer to: https://swagger.io/specification/

II. g.Meta Metadata

API metadata information can be implemented by embedding the g.Meta structure in the input struct and using its attribute tags.

For an introduction to the metadata component, please refer to the section: Metadata

III. Common Protocol Tags

The attribute tags in input and output structs fully support the OpenAPIv3 protocol. Once the corresponding protocol tags are added, the generated OpenAPIv3 information will automatically include the attribute.

Most tag attributes are automatically generated by the Server component, and there are not many tags that developers need to set manually.

1. Basic Tags

Commonly used basic tags include:

Common OpenAPIv3 TagsDescriptionRemarks
pathCombined with the prefix at registration to form the API URI pathUsed for g.Meta to mark API metadata
tagsThe tag to which the API belongs for API classificationUsed for g.Meta to mark API metadata
methodThe request method for the API: GET/PUT/POST/DELETE…(case insensitive)Used for g.Meta to mark API metadata
deprecatedMarks the API as deprecatedUsed for g.Meta to mark API metadata
summaryBrief description of the API/parameterAbbreviation sm
descriptionDetailed description of the API/parameterAbbreviation dc
inParameter submission methodheader/path/query/cookie
defaultDefault value for the parameterAbbreviation d
mimeThe MIME type of the API, such as multipart/form-data, generally set globally, defaults to application/json.Used for g.Meta to mark API metadata
typeThe type of the parameter, generally not needed, special parameters may require manual setting, such as fileOnly applicable to parameter attributes

API Document - OpenAPIv3 - 图2tip

For more tags, please refer to the standard OpenAPIv3 protocol: https://swagger.io/specification/

In addition to the above tags, the g.Meta of the response structure also supports additional tags to set more detailed documentation information:

TagDescriptionRemarks
statusSet the default return status code of the responseUsed for g.Meta to mark API metadata, default value is 200
responseExampleSet the json file path of the default return example of the responseUsed for g.Meta to mark API metadata, abbreviation resEg

The json file formats supported by responseExample are as follows:

  • Array
  • Object
  1. [
  2. {
  3. "code": 0,
  4. "message": "Success",
  5. "data": null
  6. },
  7. {
  8. "code": 1,
  9. "message": "Internal Server Error",
  10. "data": null
  11. }
  12. ]
  1. {
  2. "success": {
  3. "code": 0,
  4. "message": "Success",
  5. "data": null
  6. },
  7. "error": {
  8. "code": 1,
  9. "message": "Internal Server Error",
  10. "data": null
  11. }
  12. }

2. Extension Tags

In the OpenAPI specification, all tags prefixed with x- are customizable extension tags by developers. Extension tags can be defined in any API or attribute using Golang struct tag format, and during the generation of API documentation, they will be returned as independent fields. For example:

  1. package main
  2. import (
  3. "context"
  4. "github.com/gogf/gf/v2/errors/gcode"
  5. "github.com/gogf/gf/v2/errors/gerror"
  6. "github.com/gogf/gf/v2/frame/g"
  7. "github.com/gogf/gf/v2/net/ghttp"
  8. )
  9. type GetListReq struct {
  10. g.Meta `path:"/user" tags:"User" method:"get" x-group:"User/Info" summary:"Get user list with basic info."`
  11. Page int `dc:"Page number" d:"1" x-sort:"1"`
  12. Size int `dc:"Size for per page." d:"10" x-sort:"2"`
  13. }
  14. type GetListRes struct{}
  15. type Controller struct{}
  16. func (c *Controller) GetList(ctx context.Context, req *GetListReq) (res *GetListRes, err error) {
  17. return nil, gerror.NewCode(gcode.CodeNotImplemented)
  18. }
  19. func main() {
  20. s := g.Server()
  21. s.Group("/", func(group *ghttp.RouterGroup) {
  22. group.Bind(new(Controller))
  23. })
  24. s.SetOpenApiPath("/api.json")
  25. s.SetSwaggerPath("/swagger")
  26. s.SetPort(8199)
  27. s.Run()
  28. }

After execution, visit the address http://127.0.0.1:8199/swagger to view the swagger ui, and visit http://127.0.0.1:8199/api.json to view the corresponding OpenAPIv3 documentation. The generated OpenAPIv3 documentation is as follows:

  1. {
  2. "openapi": "3.0.0",
  3. "components": {
  4. "schemas": {
  5. "main.GetListReq": {
  6. "properties": {
  7. "Page": {
  8. "default": 1,
  9. "description": "Page number",
  10. "format": "int",
  11. "properties": {},
  12. "type": "integer",
  13. "x-sort": "1"
  14. },
  15. "Size": {
  16. "default": 10,
  17. "description": "Size for per page.",
  18. "format": "int",
  19. "properties": {},
  20. "type": "integer",
  21. "x-sort": "2"
  22. }
  23. },
  24. "type": "object",
  25. "x-group": "User/Info"
  26. },
  27. "main.GetListRes": {
  28. "properties": {},
  29. "type": "object"
  30. }
  31. }
  32. },
  33. "info": {
  34. "title": "",
  35. "version": ""
  36. },
  37. "paths": {
  38. "/user": {
  39. "get": {
  40. "parameters": [
  41. {
  42. "description": "Page number",
  43. "in": "query",
  44. "name": "Page",
  45. "schema": {
  46. "default": 1,
  47. "description": "Page number",
  48. "format": "int",
  49. "properties": {},
  50. "type": "integer",
  51. "x-sort": "1"
  52. },
  53. "x-sort": "1"
  54. },
  55. {
  56. "description": "Size for per page.",
  57. "in": "query",
  58. "name": "Size",
  59. "schema": {
  60. "default": 10,
  61. "description": "Size for per page.",
  62. "format": "int",
  63. "properties": {},
  64. "type": "integer",
  65. "x-sort": "2"
  66. },
  67. "x-sort": "2"
  68. }
  69. ],
  70. "responses": {
  71. "200": {
  72. "content": {
  73. "application/json": {
  74. "schema": {
  75. "$ref": "#/components/schemas/main.GetListRes"
  76. }
  77. }
  78. },
  79. "description": ""
  80. }
  81. },
  82. "summary": "Get user list with basic info.",
  83. "tags": [
  84. "User"
  85. ],
  86. "x-group": "User/Info"
  87. },
  88. "x-group": "User/Info"
  89. }
  90. }
  91. }

As you can see, the extension tags have been included in the API documentation.

IV. Extending Response Structure Information

For requests that require multiple response status codes, the framework provides the IEnhanceResponseStatus interface in the goai component, and developers can extend the information of the response structure by implementing this interface. The related definitions are as follows:

  1. type EnhancedStatusCode = int
  2. type EnhancedStatusType struct {
  3. Response any
  4. Examples any
  5. }
  6. type IEnhanceResponseStatus interface {
  7. EnhanceResponseStatus() map[EnhancedStatusCode]EnhancedStatusType
  8. }

Response is a response structure similar to a normal response structure, you can also add a g.Meta tag to add documentation information, and after setting the mime tag, the structure will also override the content of the general response structure. Examples is a response example, you can use the error code list to automatically generate example content and display it in the document, so as to ensure the synchronization of document content and actual business content. For example:

  1. package main
  2. import (
  3. "context"
  4. "github.com/gogf/gf/v2/errors/gcode"
  5. "github.com/gogf/gf/v2/errors/gerror"
  6. "github.com/gogf/gf/v2/frame/g"
  7. "github.com/gogf/gf/v2/net/ghttp"
  8. "github.com/gogf/gf/v2/net/goai"
  9. )
  10. type StoreMessageReq struct {
  11. g.Meta `path:"/messages" method:"post" summary:"Store a message"`
  12. Content string `json:"content"`
  13. }
  14. type StoreMessageRes struct {
  15. g.Meta `status:"201"`
  16. Id string `json:"id"`
  17. }
  18. type EmptyRes struct {
  19. g.Meta `mime:"application/json"`
  20. }
  21. type CommonRes struct {
  22. Code int `json:"code"`
  23. Message string `json:"message"`
  24. Data interface{} `json:"data"`
  25. }
  26. var StoreMessageErr = map[int]gcode.Code{
  27. 500: gcode.New(1, "Server Dead", nil),
  28. }
  29. func (r StoreMessageRes) EnhanceResponseStatus() (resList map[int]goai.EnhancedStatusType) {
  30. examples := []interface{}{}
  31. example500 := CommonRes{
  32. Code: StoreMessageErr[500].Code(),
  33. Message: StoreMessageErr[500].Message(),
  34. Data: nil,
  35. }
  36. examples = append(examples, example500)
  37. return map[int]goai.EnhancedStatusType{
  38. 403: {
  39. Response: EmptyRes{},
  40. },
  41. 500: {
  42. Response: struct{}{},
  43. Examples: examples,
  44. },
  45. }
  46. }
  47. type Controller struct{}
  48. func (c *Controller) StoreMessage(ctx context.Context, req *StoreMessageReq) (res *StoreMessageRes, err error) {
  49. return nil, gerror.NewCode(gcode.CodeNotImplemented)
  50. }
  51. func main() {
  52. s := g.Server()
  53. s.Group("/", func(group *ghttp.RouterGroup) {
  54. group.Bind(new(Controller))
  55. })
  56. oai := s.GetOpenApi()
  57. oai.Config.CommonResponse = CommonRes{}
  58. oai.Config.CommonResponseDataField = `Data`
  59. s.SetOpenApiPath("/api.json")
  60. s.SetSwaggerPath("/swagger")
  61. s.SetPort(8199)
  62. s.Run()
  63. }

After execution, visit the address http://127.0.0.1:8199/swagger to view the swagger ui, and visit http://127.0.0.1:8199/api.json to view the corresponding OpenAPIv3 documentation. The generated OpenAPIv3 documentation is as follows:

  1. {
  2. "openapi": "3.0.0",
  3. "components": {
  4. "schemas": {
  5. "main.StoreMessageReq": {
  6. "properties": {
  7. "content": {
  8. "format": "string",
  9. "type": "string"
  10. }
  11. },
  12. "type": "object"
  13. },
  14. "main.StoreMessageRes": {
  15. "properties": {
  16. "id": {
  17. "format": "string",
  18. "type": "string"
  19. }
  20. },
  21. "type": "object"
  22. },
  23. "interface": {
  24. "properties": {},
  25. "type": "object"
  26. },
  27. "main.EmptyRes": {
  28. "properties": {},
  29. "type": "object"
  30. },
  31. "struct": {
  32. "properties": {},
  33. "type": "object"
  34. }
  35. }
  36. },
  37. "info": {
  38. "title": "",
  39. "version": ""
  40. },
  41. "paths": {
  42. "/messages": {
  43. "post": {
  44. "requestBody": {
  45. "content": {
  46. "application/json": {
  47. "schema": {
  48. "$ref": "#/components/schemas/main.StoreMessageReq"
  49. }
  50. }
  51. }
  52. },
  53. "responses": {
  54. "201": {
  55. "content": {
  56. "application/json": {
  57. "schema": {
  58. "properties": {
  59. "code": {
  60. "format": "int",
  61. "type": "integer"
  62. },
  63. "message": {
  64. "format": "string",
  65. "type": "string"
  66. },
  67. "data": {
  68. "properties": {
  69. "id": {
  70. "format": "string",
  71. "type": "string"
  72. }
  73. },
  74. "type": "object"
  75. }
  76. },
  77. "type": "object"
  78. }
  79. }
  80. },
  81. "description": ""
  82. },
  83. "403": {
  84. "content": {
  85. "application/json": {
  86. "schema": {
  87. "$ref": "#/components/schemas/main.EmptyRes"
  88. }
  89. }
  90. },
  91. "description": ""
  92. },
  93. "500": {
  94. "content": {
  95. "application/json": {
  96. "schema": {
  97. "properties": {
  98. "code": {
  99. "format": "int",
  100. "type": "integer"
  101. },
  102. "message": {
  103. "format": "string",
  104. "type": "string"
  105. },
  106. "data": {
  107. "properties": {},
  108. "type": "object"
  109. }
  110. },
  111. "type": "object"
  112. },
  113. "examples": {
  114. "example 1": {
  115. "value": {
  116. "code": 1,
  117. "message": "Server Dead",
  118. "data": null
  119. }
  120. }
  121. }
  122. }
  123. },
  124. "description": ""
  125. }
  126. },
  127. "summary": "Store a message"
  128. }
  129. }
  130. }
  131. }

As you can see, the default response status code has been changed to 201, and the response example has also been automatically generated.

V. Expanding OpenAPIv3 Information

The core API information has already been automatically generated. If developers want to further complete the API information, they can obtain the OpenAPIv3 struct object via s.GetOpenApi() and manually fill in the corresponding attribute content. Let’s look at an example where we design a common data structure around each API:

API Document - OpenAPIv3 - 图3

From this, we can see that with the general OpenAPIv3 object, we can customize its content and generate various other types of custom API documentation based on it.

VI、Add api.json(swagger) custom authentication

For situations where api document authentication is required, you can use the ghttp.BindHookHandler method to authenticate the s.GetOpenApiPath() routing binding pre-method. The example is as follows:

  1. func main() {
  2. s := g.Server()
  3. // if api.json requires authentication, add openApiBasicAuth handler
  4. s.BindHookHandler(s.GetOpenApiPath(), ghttp.HookBeforeServe, openApiBasicAuth)
  5. s.Run()
  6. }
  7. func openApiBasicAuth(r *ghttp.Request) {
  8. if !r.BasicAuth("OpenApiAuthUserName", "OpenApiAuthPass", "Restricted") {
  9. r.ExitAll()
  10. return
  11. }
  12. }