#1 Gin 框架自学: 基础

2021-05-06

先列一下目前最火的几个 Go 语言框架:

  • gin
    stars num forks num License
    stars
  • beego 国人开发
    stars num forks num License
    stars
  • iris The fastest HTTP/2 Go Web Framework.
    自称是 ExpressJS 和 Laravel 的继承者。
    stars num forks num License
  • echo High performance, minimalist Go web framework
    stars num forks num License
  • go-micro Microservice framework for Go
    stars num forks num License
    stars
  • fiber
    stars num forks num License
  • kratos A Go framework for microservices. B 站出品
    stars num forks num License
  • revel/revel
    stars num forks num License
  • go-zero 国人开发(七牛)
    stars num forks num License
    stars
  • martini Classy web framework for Go
    stars num forks num License
  • GoFrame Classy web framework for Go 国人开发
    stars num forks num License

Gin

http://gin-gonic.com

Gin LOGO

Gin 相比 Beego 非常轻量级,不带 ORM,不支持 Session,正则路由都不支持,需要自行实现。

最新版本 56 个 文件, 算上注释,大概七千行代码。

$ find . -name "*.go" -not -path "*_test.go" | cat | wc -l
56
$ find . -name "*.go" -not -path "*_test.go" | xargs cat | wc -l
6910
Tag CreatedAt ReleasedAt
v1.9.0 2023-02-21 2023-02-21
v1.8.2 2022-12-22 2022-12-22
v1.8.1 2022-06-06 2022-06-06
v1.8.0 2022-05-30 2022-05-30
v1.7.7 2021-11-24 2021-11-24
v1.7.6 2021-08-03 2021-11-23
v1.7.4 2021-08-03 2021-08-15
v1.7.3 2021-08-03 2021-08-03
v1.7.2 2021-05-21 2021-05-21
v1.7.1 2021-04-08 2021-04-08
v1.7.0 2021-04-08 2021-04-08
v1.6.3 2020-05-03 2020-05-03
v1.6.2 2020-03-27 2020-03-27
v1.6.1 2020-03-23 2020-03-23
v1.6.0 2020-03-22 2020-03-22
v1.5.0 2019-11-24 2019-11-28
v1.4.0 2019-05-07 2019-05-08
v1.3.0 2018-08-14 2018-08-14
v1.2 2017-07-02 2017-07-02
v1.1.4 2016-12-04 2016-12-05
v1.1.3 2016-12-03 2016-12-04

HTTP 参数

// c: *gin.Context
// func (c *gin.Context) {
//     balabala...
// }

c.Request.URL.Query()

c.Param(key string) string // 路径参数
c.Params.ByName(key)

c.Query(key string) string // 查询参数
c.Request.URL.Query().Get(key)
c.GetQuery(key)

c.QueryMap(key string) map[string][]string
c.GetQueryMap(key)

c.QueryArray(key string) []string

c.GetQuery(key string) (string, bool)
c.GetQueryArray(key) 的第 0 个元素

c.GetQueryMap(key string) (map[string]string, bool)
c.GetQueryArray(key string) (values []string, ok bool)
c.PostForm(key string) string
c.PostFormMap(key string) map[string][]string
c.PostFormArray(key string) []string
c.GetPostForm(key string) (string, bool)
c.GetPostFormMap(key string) (map[string]string, bool)
c.GetPostFormArray(key string) (values []string, ok bool)
c.FormFile(key string) (*multipart.FileHeader, error)
c.DefaultQuery(key string, defaultValue string) string
c.DefaultPostForm(key string, defaultValue string) string

快速启动

package main

import (
    "io"
    "net/http"
    "os"
    "path/filepath"

    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()

    // 处理 GET 请求中的 URL 参数
    r.GET("/hello", func(c *gin.Context) {
        name := c.Query("name")
        c.String(http.StatusOK, "Hello %s!", name)
    })

    // 处理带路径参数的 GET 请求
    r.GET("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        c.String(http.StatusOK, "User ID: %s", id)
    })

    // 处理 POST 请求中的 JSON 请求体
    r.POST("/data", func(c *gin.Context) {
        var json struct {
            Data string `json:"data"`
        }
        c.BindJSON(&json)
        c.JSON(http.StatusOK, gin.H{"message": "Received data: " + json.Data})
    })

    // 处理文件上传
    r.POST("/upload", func(c *gin.Context) {
        file, err := c.FormFile("file")
        if err != nil {
            c.String(http.StatusBadRequest, "File upload failed: %s", err.Error())
            return
        }

        // 将上传的文件保存到服务器上
        dst := filepath.Join("./uploads", file.Filename)
        if err := c.SaveUploadedFile(file, dst); err != nil {
            c.String(http.StatusInternalServerError, "File save failed: %s", err.Error())
            return
        }

        c.String(http.StatusOK, "File uploaded successfully")
    })

    // 处理返回文件
    r.GET("/download", func(c *gin.Context) {
        filename := "example.txt"
        filepath := "./downloads/" + filename

        // 打开文件
        file, err := os.Open(filepath)
        if err != nil {
            c.String(http.StatusInternalServerError, "Failed to open file: %s", err.Error())
            return
        }
        defer file.Close()

        // 设置响应头
        c.Header("Content-Disposition", "attachment; filename="+filename)
        c.Header("Content-Type", "application/octet-stream")
        c.Header("Content-Transfer-Encoding", "binary")

        // 将文件内容写入响应主体
        _, err = io.Copy(c.Writer, file)
        if err != nil {
            c.String(http.StatusInternalServerError, "Failed to write file: %s", err.Error())
        }
    })

    r.Run(":8080")
}

路由

设置路由不存在的处理方法:

r.NoRoute(func(c *gin.Context) {
    c.JSON(404, gin.H{"message": "page not found"})
})

限制请求大小:

r.MaxMultipartMemory = 10 << 20 // 10MB
// "github.com/gin-contrib/size"
r.Use(size.Check(size.MB * 10))
r.Use(requestSizeLimitMiddleware(10 << 20))
func requestSizeLimitMiddleware(maxSize int64) gin.HandlerFunc {
    return func(c *gin.Context) {
        if c.Request.ContentLength > maxSize {
            c.AbortWithStatusJSON(413, gin.H{"message": "request entity too large"})
            return
        }
        c.Next()
    }
}