Golang uses swaggo to automatically generate Restful API documentation

Time:2022-8-16

#aboutSwaggo

I believe that many programmers do not like to write API documents like me. How comfortable it is to write code, not only does it take a lot of time to write documentation, but sometimes it is not complete. But the API documentation is essential, I believe its importance needless to say, a vague document can even make the front-end and back-end personnel fight. The swaggo introduced in this blog today is a tool that allows you to generate perfect API documentation only by focusing on the code. There is a lot of nonsense, let's read the article directly.

Maybe you have used Swagger, and swaggo replaces the part where you write yaml manually. Converting comments into documentation with a single command allows us to focus more on the code.

At present, swaggo mainly implements the following functions of swagger 2.0:

  • Basic Structure

  • API Host and Base Path

  • Paths and Operations

  • Describing Parameters

  • Describing Request Body

  • Describing Responses

  • MIME Types

  • Authentication

  • Basic Authentication

  • API Keys

  • Adding Examples

  • File Upload

  • Enums

  • Grouping Operations With Tags

  • Extensions (Swagger Extensions)

The following content uses gin-swaggo as an example

Here is the demo address

2020/05/16 Update:

swag has been upgraded to v1.6.5, and the returned data format has been updated.

Please pay attention to the latest documentation on the official website.

At the end of this article, the optimization part can be understood.

# use use

#Install swag-cli- and download related packages for installationswag cliand download related packages

To use swaggo, you first need to installswag cli

go get -u github.com/swaggo/swag/cmd/swag

Then we need two more packages.

# gin-swagger middleware
    go get github.com/swaggo/gin-swagger
    # swagger built-in files
    go get github.com/swaggo/gin-swagger/swaggerFiles

Check out the version you have installed

swag --version
    swag version v1.6.5

#Add a comment in main-gomain.goadd comments inside

package main
    
    import (
    "github.com/gin-gonic/gin"
    	ginSwagger "github.com/swaggo/gin-swagger"
    "github.com/swaggo/gin-swagger/swaggerFiles"
    )
    
    // @title Swagger Example API
    // @version 1.0
    // @description This is a sample server celler server.
    // @termsOfService https://razeen.me
    
    // @contact.name Razeen
    // @contact.url https://razeen.me
    // @contact.email [email protected]
    
    // @license.name Apache 2.0
    // @license.url http://www.apache.org/licenses/LICENSE-2.0.html
    
    // @host 127.0.0.1:8080
    // @BasePath /api/v1
    
    func main() {
    
    	r := gin.Default()
        store := sessions.NewCookieStore([]byte("secret"))
    	r.Use(sessions.Sessions("mysession", store))
    
    	r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
    
    	v1 := r.Group("/api/v1")
    	{
    		v1.GET("/hello", HandleHello)
    		v1.POST("/login", HandleLogin)
    		v1Auth := r.Use(HandleAuth)
    		{
    			v1Auth.POST("/upload", HandleUpload)
    			v1Auth.GET("/list", HandleList)
    		}
    	}
    
    	r.Run(":8080")
    }

As shown above, we need to import

ginSwagger "github.com/swaggo/gin-swagger"
    "github.com/swaggo/gin-swagger/swaggerFiles"

Also, add comments. in:

  • titile: document title
  • version: Version
  • description,termsOfService,contact ...These are some statements, not written.
  • license.nameWell, this is a must.
  • host,BasePath: If you want to debug the API directly with swagger, these two items need to be filled in correctly. The former is the port of the service document, ip. The latter is the base path, like "/api/v1" here.
  • Also in the original documentsecurityDefinitions.basic,securityDefinitions.apikeyWait, these are all used for authentication, I will not expand them here.

here we aremian.goExecute in the same directoryswag initThe documentation can be automatically generated as follows:

➜  swaggo-gin git:(master) ✗ swag init
    2019/01/12 21:29:14 Generate swagger docs....
    2019/01/12 21:29:14 Generate general API Info
    2019/01/12 21:29:14 create docs.go at  docs/docs.go

Then we import this automatically generateddocspackage, run:

package main
    
    import (
    "github.com/gin-gonic/gin"
    	ginSwagger "github.com/swaggo/gin-swagger"
    "github.com/swaggo/gin-swagger/swaggerFiles"
    
    	_ "github.com/razeencheng/demo-go/swaggo-gin/docs"
    )
    
    // @title Swagger Example API
    // @version 1.0
    // ...
➜  swaggo-gin git:(master) ✗ go build
    ➜  swaggo-gin git:(master) ✗ ./swaggo-gin
    [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
    
    [GIN-debug] [WARNING] Running in"debug" mode. Switch to "release" mode in production.
     - using env:   export GIN_MODE=release
     - using code:  gin.SetMode(gin.ReleaseMode)
    
    [GIN-debug] GET    /api/v1/hello             --> main.HandleHello (3 handlers)
    [GIN-debug] POST   /api/v1/login             --> main.HandleLogin (3 handlers)
    [GIN-debug] POST   /upload                   --> main.HandleUpload (4 handlers)
    [GIN-debug] GET    /list                     --> main.HandleList (4 handlers)
    [GIN-debug] GET    /swagger/*any             --> github.com/swaggo/gin-swagger.WrapHandler.func1 (4 handlers)
    [GIN-debug] Listening and serving HTTP on :8080

The browser opens http://127.0.0.1:8080/swagger/index.html, we can see that the following document title has been generated.

#Add a comment on the Handle functionAdd a comment on the Handle function

Next, we need to annotate each route handler function like:

// @Summary test SayHello
    // @Description says Hello to you
    // @Tags test
    // @Accept mpfd
    // @Produce json
    // @Param who query string true "person's name"
    // @Success 200 {string} string "{"msg": "hello Razeen"}"
    // @Failure 400 {string} string "{"msg": "who are you"}"
    // @Router /hello [get]
    funcHandleHello(c *gin.Context) {
    	who := c.Query("who")
    
    if who == "" {
    		c.JSON(http.StatusBadRequest, gin.H{"msg": "who are u?"})
    return
    	}
    
    	c.JSON(http.StatusOK, gin.H{"msg": "hello " + who})
    }

we againswag init, run it.

At this point, the relevant description of the API has been generated, we clickTry it outThe API can also be tested directly.

Is it easy to use, of course, this is not the end, these comment fields, we explain one by one.

These comments correspond to the location of the API documentation, which I have marked in the above figure. Here we mainly discuss the following parameters in detail:

#TagsTags

Tags are used to group APIs.

#AcceptAccept

Received parameter type, supports form (mpfd) , JSON(json), etc., more in the table below.

#ProduceProduce

The returned data structure is generallyjson

#ParamParam

Parameters, from front to back are:

@Param who query string true “person name”

@Param 1. Parameter name `` 2. Parameter type `` 3. Parameter data type `` 4. Is it required `` 5. Parameter description `` 6. Other attributes

1. Parameter name

The parameter name is the name of the parameter we interpret.

2. Parameter type

There are four main types of parameters:

pathThis type of parameter is directly spliced ​​in the URL, such as in DemoHandleGetFile

// @Param id path integer true "File ID"

queryThis type of parameter is generally combined in the URL, such as in DemoHandleHello

// @Param who query string true "person's name"

formDataThe type parameter is generallyPOST,PUTThe method is used, as in DemoHandleLogin

// @Param user formData string true "Username" default(admin)

bodywhenAcceptYesJSONformat, we use this field to specify the type of JSON to receive

// @Param param body main.JSONParams true "JSON to upload"

3. Parameter data type

The data types mainly support the following:

  • string (string)
  • integer (int, uint, uint32, uint64)
  • number (float32)
  • boolean (bool)

Note that if you are uploading files you can usefile, but the parameter type must beformData, as follows:

// @Param file formData file true "File"

4. Is it necessary

Indicates whether the parameter is required or not, the required parameter will be marked in bold in the document, and must be filled in during the test.

5. Parameter description

It is some description of the parameters

6. Other properties

In addition to the above properties, we can also fill in some additional properties for this parameter, such as enumeration, default value, value range, etc. as follows:

enumerate
    // @Param enumstring query string false "string enums" Enums(A, B, C)
    // @Param enumint query int false "int enums" Enums(1, 2, 3)
    // @Param enumnumber query number false "int enums" Enums(1.1, 1.2, 1.3)
    
    range of values
    // @Param string query string false "string valid" minlength(5) maxlength(10)
    // @Param int query int false "int valid" mininum(1) maxinum(10)
    
    set default value
    // @Param default query string false "string default" default(A)

And these parameters can be used in combination, such as:

// @Param enumstring query string false "string enums" Enums(A, B, C) default(A)
#SuccessSuccess

Specifies the data for a successful response. The format is:

// @Success 1. HTTP response code ``{2. Response parameter type}`` 3. Response data type `` 4. Other descriptions

1. HTTP response code

That is 200, 400, 500 those.

2. Response parameter type / 3. Response data type

The returned data type can be a custom type or json.

  • custom type

In normal use, I will return some specified model serialized JSON data. At this time, I can do this

// @Success 200 {object} main.File

Among them, the model directly usespackagename.modelThat's it. You would say, what if I return an array of models? At this point you can write:

// @Success 200 {anrry} main.File

Will as you just return other data format can be written as follows:

// @Success 200 {string} string ""

4. Other description

Some clarification could be added.

#FailureFailure

Same Success.

#RouterRouter

​ Specify the route and HTTP method. The format is:

// @Router /path/to/handle [HTTP method]

​ No need to add the base path.

#Generate documentation and tests Generate documentation and tests

In fact, the introduction has been interspersed above.

existmain.gorun underswag initDocumentation can be generated and updated.

Click in the documentTry it outto test. If some APIs need to be logged in, you can try to log in to the interface.

#optimize optimization

Seeing here, it is basically ready to use. But the documentation is generally only needed when we test. When my product goes online, the interface documentation should not be given to the user, and the package with the interface documentation will be much larger (swaggo is built directly into the binary).

To deal with this situation, we can optimize it at compile time, such as usingbuild tagto control whether the documentation is compiled or not.

existmain.gostatementswagHandler, and add the route only when the parameter is not empty:

package main
    
    //...
    
    var swagHandler gin.HandlerFunc
    
    funcmain(){
    // ...
    
    if swagHandler != nil {
    			r.GET("/swagger/*any", swagHandler)
            }
    
    //...
    }

At the same time, we added this parameter to thebuild taginitialized in the package.

// +build doc
    
    package main
    
    import (
    	_ "github.com/razeencheng/demo-go/swaggo-gin/docs"
    
    	ginSwagger "github.com/swaggo/gin-swagger"
    "github.com/swaggo/gin-swagger/swaggerFiles"
    )
    
    funcinit() {
    	swagHandler = ginSwagger.WrapHandler(swaggerFiles.Handler)
    }

Then we can usego build -tags "doc"to package the package with documentation, directlygo buildto package packages without documentation.

You will find that even though my demo is so small, the compiled size is 19M different!

➜  swaggo-gin git:(master) ✗ go build
    ➜  swaggo-gin git:(master) ✗ ll swaggo-gin
    -rwxr-xr-x  1 xxx  staff    15M Jan 13 00:23 swaggo-gin
    ➜  swaggo-gin git:(master) ✗ go build -tags "doc"
    ➜  swaggo-gin git:(master) ✗ ll swaggo-gin
    -rwxr-xr-x  1 xxx  staff    34M Jan 13 00:24 swaggo-gin

The article ends here, and the complete Demo address is here.

This article is taken from https://razeencheng.com/post/go-swagger.html