• 11.2Go gin


    11.1 Go gin

    框架一直是敏捷开发中的利器,能让开发者很快的上手并做出应用。

    成长总不会一蹴而就,从写出程序获取成就感,再到精通框架,快速构造应用。

    Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确。

    具有快速灵活,容错方便等特点。

    其实对于golang而言,web框架的依赖要远比Python,Java之类的要小。

    自身的net/http足够简单,性能也非常不错

    框架更像是一些常用函数或者工具的集合。

    借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范。

    1.1. 使用gin

    安装gin

    go get github.com/gin-gonic/gin
    

    ginhello world

    package main
    
    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    func main() {
        //gin.Default方法创建路由handler
        router := gin.Default()
        //绑定路由规则和路由函数,gin支持restful方法
        //gin把request和response都封装到了gin.Context的上下文环境中
        router.GET("/", func(c *gin.Context) {
            //返回字符串
            c.String(http.StatusOK, "Hello World")
        })
        //监听端口
        router.Run(":8000")
    }
    

    运行代码,即可访问http://0.0.0.0:8000/寻找hello world页面

    1.2. restful api

    借助于postman测试restful api

    • GET(SELECT):从服务器取出资源(一项或多项)。
    • POST(CREATE):在服务器新建一个资源。
    • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
    • PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
    • DELETE(DELETE):从服务器删除资源。
    • HEAD:获取资源的元数据。
    • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

    restful案例

    • GET /zoos:列出所有动物园
    • POST /zoos:新建一个动物园
    • GET /zoos/ID:获取某个指定动物园的信息
    • PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
    • PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
    • DELETE /zoos/ID:删除某个动物园
    • GET /zoos/ID/animals:列出某个指定动物园的所有动物
    • DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

    状态码

    • 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
    • 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
    • 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
    • 204 NO CONTENT - [DELETE]:用户删除数据成功。
    • 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
    • 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
    • 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
    • 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
    • 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
    • 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
    • 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
    • 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

    gin代码示例

    用户信息接口设计 restful风格

    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
        //Default返回一个默认的路由引擎
        r := gin.Default()
        //匿名函数可以用有名函数,也可以匿名函数
        r.GET("/user/info", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "get user info succ",
            })
        })
        r.POST("/user/info", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "create user info succ",
            })
        })
        r.PUT("/user/info", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "update user info succ",
            })
        })
        r.DELETE("/user/info", func(c *gin.Context) {
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message": "delete user info succ ",
            })
        })
        r.Run() // listen and serve on 0.0.0.0:8080
    }
    

    非restful风格的API

    /user/info
    /user/create
    /user/delete
    /user/update
    

    1.3. Gin框架参数传递

    实例代码,传入2个get参数

    querystring传递参数

    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
        //Default返回一个默认的路由引擎
        r := gin.Default()
        r.GET("/user/search", func(c *gin.Context) {
            //username := c.DefaultQuery("username", "少林")
            username := c.Query("username")
            address := c.Query("address")
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message":  "pong",
                "username": username,
                "address":  address,
            })
        })
    
        r.Run() // listen and serve on 0.0.0.0:8080
    }
    

    通过postman发送请求,携带参数

    127.0.0.1:8080/user/search?username=超哥&address=沙河
    

    DefaultQuery返回URL参数

    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
        //Default返回一个默认的路由引擎
        r := gin.Default()
        r.GET("/user/search", func(c *gin.Context) {
            //使用默认值参数
            username := c.DefaultQuery("username", "超老板")
            //username := c.Query("username")
            address := c.Query("address")
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message":  "pong",
                "username": username,
                "address":  address,
            })
        })
    
        r.Run() // listen and serve on 0.0.0.0:8080
    }
    

    访问结果

    127.0.0.1:8080/user/search?address=沙河
    

    1.4. 获取路径中的参数

    不建议使用,使用表单或者querystring方式获取参数

    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
        //Default返回一个默认的路由引擎
        r := gin.Default()
        r.GET("/user/search/:username/:address", func(c *gin.Context) {
            username := c.Param("username")
            address := c.Param("address")
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message":  "pong",
                "username": username,
                "address":  address,
            })
        })
    
        r.Run(":8080") // listen and serve on 0.0.0.0:8080
    }
    

    访问方式

    127.0.0.1:8080/user/search/chao/beijing/
    

    1.5. 获取表单的参数

    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
        //Default返回一个默认的路由引擎
        r := gin.Default()
        r.POST("/user/search", func(c *gin.Context) {
            //username := c.DefaultPostForm("username", "少林")
            username := c.PostForm("username")
            address := c.PostForm("address")
            //输出json结果给调用方
            c.JSON(200, gin.H{
                "message":  "pong",
                "username": username,
                "address":  address,
            })
        })
    
        r.Run(":8080") // listen and serve on 0.0.0.0:8080
    }
    

    运行方式,使用post方式提交form

    1.6. Gin文件上传

    上传文件的名字应该由服务端统一文件名规则,防止非法字符

    package main
    
    import (
        "fmt"
        "log"
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    func main() {
        router := gin.Default()
        // Set a lower memory limit for multipart forms (default is 32 MiB)
        // router.MaxMultipartMemory = 8 << 20  // 8 MiB
        router.POST("/upload", func(c *gin.Context) {
            // single file
            file, err := c.FormFile("file")
            if err != nil {
                c.JSON(http.StatusInternalServerError, gin.H{
                    "message": err.Error(),
                })
                return
            }
            //
            log.Println(file.Filename)
            dst := fmt.Sprintf("文件夹路径/%s", file.Filename)
            // Upload the file to specific dst.
            c.SaveUploadedFile(file, dst)
            c.JSON(http.StatusOK, gin.H{
                "message": fmt.Sprintf("'%s' uploaded!", file.Filename),
            })
        })
        router.Run(":8080")
    }
    

    上传单个文件

    上传多个文件

    package main
    
    import (
        "fmt"
        "log"
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    func main() {
        router := gin.Default()
        //文件比较大,默认文件存在内中占32M,大于32M就写入到磁盘
        // Set a lower memory limit for multipart forms (default is 32 MiB)
        //设置上传文件的内存大小
        // router.MaxMultipartMemory = 8 << 20  // 8 MiB
        router.POST("/upload", func(c *gin.Context) {
            // Multipart form
            form, _ := c.MultipartForm()
            files := form.File["file"]
    
            for index, file := range files {
                log.Println(file.Filename)
                dst := fmt.Sprintf("/Users/yuchao/go/src/gostudy/gobook/%s_%d", file.Filename, index)
                // Upload the file to specific dst.
                c.SaveUploadedFile(file, dst)
            }
            c.JSON(http.StatusOK, gin.H{
                "message": fmt.Sprintf("%d files uploaded!", len(files)),
            })
        })
        router.Run(":8080")
    }
    

    上传方式

    1.7. 路由分组

    把前缀一样的url,放入一个组

    package main
    
    import "github.com/gin-gonic/gin"
    
    func login(ctx *gin.Context) {
        ctx.JSON(200, gin.H{
            "message": "success",
        })
    }
    
    func read(ctx *gin.Context) {
        ctx.JSON(200, gin.H{
            "message": "success",
        })
    }
    
    func submit(ctx *gin.Context) {
        ctx.JSON(200, gin.H{
            "message": "success",
        })
    }
    
    func main() {
        //Default返回一个默认的路由引擎
        router := gin.Default()
    
        // Simple group: v1
        //   /v1/login
        //   /v1/submit
        //   /v1/read
        v1 := router.Group("/v1")
        {
            v1.POST("/login", login)
            v1.POST("/submit", submit)
            v1.POST("/read", read)
        }
    
        // Simple group: v2
        //   /v2/login
        //   /v2/submit
        //   /v2/read
        v2 := router.Group("/v2")
        {
            v2.POST("/login", login)
            v2.POST("/submit", submit)
            v2.POST("/read", read)
        }
    
        router.Run(":8080")
    }
    

    运行方式

    1.8. Gin参数绑定

    使用方便,提高开发效率

    通过反射机制,自动提取querystring,from表单,json,xml等参数到struct中

    通过http协议中的context type,识别json、xml或是表单

    package main
    
    import (
        "fmt"
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    // Binding from JSON
    type Login struct {
        User     string `form:"user" json:"user" binding:"required"`
        Password string `form:"password" json:"password" binding:"required"`
    }
    
    func main() {
        router := gin.Default()
    
        // Example for binding JSON ({"user": "manu", "password": "123"})
        router.POST("/loginJSON", func(c *gin.Context) {
            var login Login
    
            if err := c.ShouldBindJSON(&login); err == nil {
                fmt.Printf("login info:%#v
    ", login)
                c.JSON(http.StatusOK, gin.H{
                    "user":     login.User,
                    "password": login.Password,
                })
            } else {
                c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            }
        })
    
        // Example for binding a HTML form (user=manu&password=123)
        router.POST("/loginForm", func(c *gin.Context) {
            var login Login
            // This will infer what binder to use depending on the content-type header.
            if err := c.ShouldBind(&login); err == nil {
                c.JSON(http.StatusOK, gin.H{
                    "user":     login.User,
                    "password": login.Password,
                })
            } else {
                c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            }
        })
    
        // Example for binding a HTML querystring (user=manu&password=123)
        router.GET("/loginForm", func(c *gin.Context) {
            var login Login
            // This will infer what binder to use depending on the content-type header.
            if err := c.ShouldBind(&login); err == nil {
                c.JSON(http.StatusOK, gin.H{
                    "user":     login.User,
                    "password": login.Password,
                })
            } else {
                c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            }
        })
    
        // Listen and serve on 0.0.0.0:8080
        router.Run(":8080")
    }
    

    json方式提交数据

    form方式提交

    1.9. gin框架渲染

    json渲染

    package main
    
    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    func main() {
        r := gin.Default()
    
        // gin.H is a shortcut for map[string]interface{}
        r.GET("/someJSON", func(c *gin.Context) {
            //第一种方式,自己拼json
            c.JSON(http.StatusOK, gin.H{"message": "你大爷", "status": http.StatusOK})
        })
    
        r.GET("/moreJSON", func(c *gin.Context) {
            // You also can use a struct
            var msg struct {
                Name    string `json:"user"`
                Message string
                Number  int
            }
            msg.Name = "大王"
            msg.Message = "大王,唐僧给你捉来了"
            msg.Number = 123
            // Note that msg.Name becomes "user" in the JSON
            c.JSON(http.StatusOK, msg)
        })
        // Listen and serve on 0.0.0.0:8080
        r.Run(":8080")
    }
    

    xml渲染

    package main
    
    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    func main() {
        r := gin.Default()
        r.GET("/moreXML", func(c *gin.Context) {
            // You also can use a struct
            type MessageRecord struct {
                Name    string
                Message string
                Number  int
            }
    
            var msg MessageRecord
            msg.Name = "二王"
            msg.Message = "你大爷"
            msg.Number = 123
            c.XML(http.StatusOK, msg)
        })
        // Listen and serve on 0.0.0.0:8080
        r.Run(":8080")
    }
    

    HTML 模板渲染

    package main
    
    import (
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    func main() {
        router := gin.Default()
        //模板路径,一层就是templates/*
        //两层就是templates/**/*
        router.LoadHTMLGlob("templates/**/*")
        router.GET("/posts/index", func(c *gin.Context) {
            c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
                "title": "我是标题",
            })
        })
        router.GET("/users/index", func(c *gin.Context) {
            c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
                "title": "我是标题",
            })
        })
        router.Run(":8080")
    }
    

    访问路径

    注意先go build main.go 再运行

    http://127.0.0.1:8080/users/index
    http://127.0.0.1:8080/posts/index
    

    1.10. Gin 静态文件服务器

    package main
    
    import (
        "github.com/gin-gonic/gin"
    )
    
    func main() {
        r := gin.Default()
        //在main.go同级准备static文件夹,内含图片
        r.Static("/static", "./static")
        // Listen and serve on 0.0.0.0:8080
        r.Run(":8080")
    }
    

    注意先go build main.go 再运行

    go build main.go 
    ./main.go
    
    http://127.0.0.1:8080/static/1.png
    

    1.11. Go中间件

    golang的net/http设计的一大特点就是特别容易构建中间件。

    gin也提供了类似的中间件。需要注意的是中间件只对注册过的路由函数起作用。

    对于分组路由,嵌套使用中间件,可以限定中间件的作用范围。

    中间件分为全局中间件,单个路由中间件和群组中间件。

    Gin框架允许请求处理过程中,加入用户自己的钩子函数。这个函数就是中间件
    利用中间件可以处理例如耗时统计,日志打印,登录校验等
    

    计算耗时的中间件

    package main
    
    import (
        "log"
        "time"
    
        "net/http"
    
        "github.com/gin-gonic/gin"
    )
    
    func StatCost() gin.HandlerFunc {
        return func(c *gin.Context) {
            t := time.Now()
    
            //可以设置一些公共参数,
            c.Set("example", "12345")
            //等其他中间件先执行
            c.Next()
            //获取耗时
            latency := time.Since(t)
            //打印花费的时间 319微秒
            log.Printf("total cost time:%d us", latency/1000)
        }
    }
    
    func main() {
        //新建一个路由 gin.New(),没有中间件
        // gin.Default()最常用,包含了中间件
        //r := gin.New()
        r := gin.Default()
        //Use是全局中间件,传入一个
        r.Use(StatCost())
    
        r.GET("/test", func(c *gin.Context) {
            example := c.MustGet("example").(string)
    
            // it would print: "12345"
            log.Println(example) //
            c.JSON(http.StatusOK, gin.H{
                "message": "success",
            })
        })
    
        // Listen and serve on 0.0.0.0:8080
        r.Run(":8080")
    }
    

    1.12. Gin框架路由原理

    gin路由部分用的是

    https://github.com/julienschmidt/httprouter
    

    package main
    
    import "github.com/gin-gonic/gin"
    
    func index(ctx *gin.Context) {
        ctx.JSON(200, gin.H{
            "message": "index",
        })
    }
    
    func main() {
        //Defatult返回一个默认的路由引擎
        router := gin.Default()
        router.POST("/", index)
        router.POST("/search", index)
        router.POST("/support", index)
        router.POST("/blog/:post", index)
        router.POST("/about", index)
        router.POST("/contact", index)
        router.POST(":8080", index)
    }
    

    共用url前缀

  • 相关阅读:
    mysql 时间戳
    css优先级
    app横竖屏切换
    文本溢出时显示省略号
    react页面间传递参数
    去掉input获取focus时的边框
    Mac中好用的快捷键
    python 图片处理
    CSS padding margin border属性详解
    python3.x执行post请求时报错“POST data should be bytes or an iterable of bytes...”的解决方法
  • 原文地址:https://www.cnblogs.com/open-yang/p/11256952.html
Copyright © 2020-2023  润新知