• 【Go语言】Gin 框架教程


    Gin 框架教程

    1.第一个 Gin 程序

    1.1 Gin 安装

    # 执行执行如下操作即可,安装Gin前需要安装Go环境
    go get -u -v github.com/gin-gonic/gin
    # -v:打印出被构建的代码包的名字
    # -u:已存在相关的代码包,强行更新代码包及其依赖包
    

    1.2 Gin 项目创建

    在一个空文件夹里新建文件 main.go,参考如下代码编写一个 Gin 程序。

    // blog.euansu.cn
    // main.go
    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
    	r := gin.Default()
    	r.GET("/", func(c *gin.Context) {
    		c.String(200, "Hello, EuanSu")
    	})
    	r.Run() // listen and serve on 0.0.0.0:8080
    }
    

    运行 main.go 程序,如下图所示。

    go run main.go
    

    在这里插入图片描述

    代码说明:

    1. 首先,r := gin.Default()这里生成了一个 Gin 引擎实例,用于处理 HTTP 请求,也即 WSGI 应用程序,这个实例会预先加载一些默认的中间件。
    2. r.GET("/", ...) 则是声明了一个路由,以及路由对应的函数方法。
    3. r.Run() 函数则是运行应用程序,默认的监听端口是 8080,也可以传入参数设置应用程序运行端口,例如 r.Run(":8888"),即应用运行在 8888 端口。

    1.3 网站图标设置

    这里使用一个 Gin 的中间件 github.com/thinkerou/favicon,使用同 gin 框架,首先是安装 github.com/thinkerou/favicon 库。

    go get github.com/thinkerou/favicon
    

    接下来,则是使用该中间件,代码如下所示:

    // blog.euansu.cn
    // main.go
    package main
    
    import (
    	"github.com/gin-gonic/gin"
    	"github.com/thinkerou/favicon"
    )
    
    func main() {
    	// 创建一个服务
    	r := gin.Default()
    	// 使用中间件
    	r.Use(favicon.New("./static/favicon.ico"))
    	// 路由函数
    	r.GET("/", func(c *gin.Context) {
    		c.String(200, "Hello, EuanSu")
    	})
    	r.Run() // listen and serve on 0.0.0.0:8080
    }
    

    项目运行后,就能够在浏览器中看到 favicon.ico 网站图标。

    在这里插入图片描述

    2.Gin 中的路由

    2.1 路由语法

    $router.$method("$router", $handlerFunction)
    # router,Gin 引擎实例
    # method,http路由方法,可选参数为:GET、POST、PUT、PATCH、DELETE、OPTIONS、HEAD以及能够处理任意类型的HTTP请求的Any和处理指定类型的HTTP请求的Match
    # handlerFunction,路由函数,处理路由响应
    

    示例如下:

    router.GET("/path", handlerFunction) 								// 获取资源
    router.POST("/path", handlerFunction) 								// 创建资源	
    router.PUT("/path", handlerFunction) 								// 更新资源
    router.DELETE("/path", handlerFunction) 							// 删除资源
    router.PATCH("/path", handlerFunction) 								// 更新部分资源
    router.OPTIONS("/path", handlerFunction) 							// 获取服务器支持的 HTTP 方法
    router.HEAD("/path", handlerFunction) 								// 获取资源的头部信息
    router.Any("/path", handlerFunction) 								// 处理任意类型的 HTTP 请求
    router.Match([]string{"GET", "POST"}, "/path", handlerFunction) 	// 处理指定类型的 HTTP 请求
    

    2.2 请求参数的处理

    2.2.1 路由参数解析

    参数包含在路由中,如 /user/:name,通过调用不同的 路由参数 传入不同的 name,如下所示:

    r.GET("/user/:name", func(c *gin.Context) {
        name := c.Param("name")
        c.String(http.StatusOK, "%s, Welcome to your clicl.", name)
    })
    

    在这里插入图片描述

    这里需要注意下,如果新增了路由,需要重启 Gin 程序,重启之后,再次访问路由地址,就能够正常解析路由参数了。

    在这里插入图片描述

    2.2.2 Query 参数解析

    Query 参数 与上文的路由参数一样,参数都是路由的一部分,不同的是 Query 参数 是键值对的形式,查询参数通常位于URL的问号(?)之后,以键值对的形式出现,并且多个参数之间用与号(&)分隔。

    r.GET("/users", func(c *gin.Context) {
        name := c.Query("name")
        c.String(http.StatusOK, "%s, Thank you for your click.", name)
    })
    

    在这里插入图片描述

    也可以直接使用 URL 通过浏览器请求该接口。

    在这里插入图片描述

    2.2.3 POST 参数解析(form表单)

    这里使用一个表单参数进行举例,如下所示,输入分数转化为及格、不及格的判断,代码如下所示。

    package main
    
    import (
    	"github.com/gin-gonic/gin"
    	"net/http"
    	"strconv"
    )
    
    func evaluateScore(score int) string {
    	if score >= 90 {
    		return "优秀"
    	} else if score >= 60 {
    		return "及格"
    	} else {
    		return "不及格"
    	}
    }
    
    func main() {
    	r := gin.Default()
    	// POST参数解析
    	// POST
    	r.POST("/form", func(c *gin.Context) {
    		username := c.PostForm("username")
            // DefaultPostForm,只支持string类型,因此需要进行类型的转换
    		scoreStr := c.DefaultPostForm("score", "0")
    		score, err := strconv.Atoi(scoreStr)
    		if err != nil {
    			// 处理错误情况,例如当输入的值不是整数时
    			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid score"})
    			return
    		}
    
    		result := evaluateScore(score)
    		c.JSON(http.StatusOK, gin.H{"username": username, "score": score, "evaluation": result})
    	})
    
    	r.Run() // listen and serve on 0.0.0.0:8080
    }
    r.POST("/form", func(c *gin.Context) {
        username := c.PostForm("username")
        password := c.DefaultPostForm("password", "000000") // 可设置默认值
        fmt.Println(password)
        c.String(http.StatusOK, "%s, Thank you for your login.", username)
    })
    

    请求后端的接口,返回如下。

    在这里插入图片描述

    2.2.4 POST 参数解析(json)
    package main
    
    import (
    	"github.com/gin-gonic/gin"
    	"net/http"
    	"strconv"
    )
    
    func evaluateScore(score int) string {
    	if score >= 90 {
    		return "优秀"
    	} else if score >= 60 {
    		return "及格"
    	} else {
    		return "不及格"
    	}
    }
    
    // 定义结构体,与JSON数据匹配
    type Person struct {
    	Username string `json:"username"`
    	Score    int    `json:"score"`
    }
    
    func main() {
    	r := gin.Default()
    
    	// 处理POST请求,接收JSON参数
    	r.POST("/json", func(c *gin.Context) {
    		var person Person
    
    		// 绑定JSON到结构体
    		if err := c.ShouldBindJSON(&person); err != nil {
    			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    			return
    		}
    
    		result := evaluateScore(person.Score)
    		c.JSON(http.StatusOK, gin.H{"username": person.Username, "score": person.Score, "evaluation": result})
    	})
    
    	r.Run() // listen and serve on 0.0.0.0:8080
    }
    
    

    请求后端的接口,返回如下。

    在这里插入图片描述

    2.3 路由处理

    2.3.1 重定向
    r.GET("/redirect", func(c *gin.Context) {
        c.Redirect(http.StatusMovedPermanently, "/")
    })
    

    如下所示,当后端收到 /redirect 路由的请求,会重定向至 /

    在这里插入图片描述

    2.3.2 分组路由

    分组路由主要是为了处理路由前缀一致的情况,例如有一组路由前缀都是 /api/v1 开头,通过分组路由就能够简化路由的定义,也可以更好的实现路由的权限控制,例如将需要登录的路由放到同一分组中。代码示例如下:

    // 默认的路由方法
    defaultHandler := func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "path": c.FullPath(),
        })
    }
    
    // group: v1
    v1 := r.Group("/v1")
    {
        v1.GET("/posts", defaultHandler)
        v1.GET("/series", defaultHandler)
    }
    // group: v2
    v2 := r.Group("/v2")
    {
        v2.GET("/posts", defaultHandler)
        v2.GET("/series", defaultHandler)
    }
    

    请求不同的路由地址,均能够得到正常的响应。

    在这里插入图片描述

    在这里插入图片描述

    3.RESTful API

    RESTful API 具体概念可以查看 https://blog.euansu.cn/post/djangorestframework/ 这篇文章中关于 RESTful API 的相关介绍 。

    使用 Go 语言能够快速的实现 RESTful API,实现如下:

    r.GET("/user", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"msg": "get user"})
    })
    r.POST("/user", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"msg": "post user"})
    })
    r.PUT("/user", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"msg": "put user"})
    })
    r.DELETE("/user", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"msg": "delete user"})
    })
    

    使用 Apifox 等接口测试工具测试,应用程序能够根据使用的 HTTP 请求方式的不同而使用不同的函数进行处理。

    • GET 请求

      在这里插入图片描述

    • POST 请求

      在这里插入图片描述

    • PUT 请求

      在这里插入图片描述

    • DELETE 请求

      在这里插入图片描述

    如上测试所示,Gin 框架能够快速、简洁的实现 RESTful API

    4.响应页面

    可以通过以下方式,先加载静态页面到 Gin 应用程序中。

    // 加载静态页面
    r.LoadHTMLGlob("templates/*")
    // 加载指定的静态页面(不推荐)
    r.LoadHTMLFiles("templates/index.html")
    

    路由函数

    r.GET("/", func(c *gin.Context) {
        c.HTML(http.StatusOK, "index.html", gin.H{"msg": "index.html", "path": "/"})
    })
    

    打开浏览器,访问 Gin/ 路由,显示如下。

    在这里插入图片描述

    5.中间件

    首先是在项目中,声明一个中间件方法 myHandler(),如下是 myHandler() 的代码方法。

    func myHandler() gin.HandlerFunc {
    	return func(c *gin.Context) {
    		// 设置中间件的值
    		c.Set("usersession", "xxx")
    		if c.Request.URL.Path == "/" {
    			// 阻止
    			fmt.Println("阻止")
    			c.Abort()
    		}
    		// 放行
    		fmt.Println("放行")
    		c.Next()
    	}
    }
    

    Gin 应用程序中使用这个中间件。

    func main() {
    	// 创建一个服务
    	r := gin.Default()
    	// 使用中间件
    	r.Use(myHandler())
    	...
        
    	r.Run() // listen and serve on 0.0.0.0:8080
    }
    

    实际测试效果如下:

    • 请求 / 路由,后端返回为空。

      在这里插入图片描述

    • 请求其他的路由,则不受影响,就如我们在中间件方法所写的判断一样,仅仅只是拦截了 / 的路由请求。

      在这里插入图片描述

    6.数据库

    Gin 项目中使用数据库,涉及以下操作:

    1. 安装数据库驱动和 ORM 库,安详需要使用的数据库驱动和 ORM 库,常见的 ORM 库是 GORM,支持 MySQLPostgreSQLSQLite 等数据库。
    2. 配置数据库的连接信息,需要在 Gin 项目配置中配置数据库连接,通常是 main.go 或者单独的配置文件。
    3. 初始化数据库,在项目启动的时候,进行数据库连接的初始化。
    4. 项目中使用数据库, 在路由关联的函数中使用数据进行增删改查的操作。

    6.1 安装数据库驱动和 ORM 库

    go get -u gorm.io/gorm
    go get -u gorm.io/driver/mysql
    

    6.2 配置数据库的连接信息

    main.go 文件中配置数据库的连接信息。

    // 数据库连接信息
    dsn := "username:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
    var err error
    
    // 连接数据库
    db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        log.Fatalf("无法连接数据库: %v", err)
    }
    

    6.3 数据库初始化

    使用 gorm 库,定义数据库模型,在 main.go 文件中添加 AutoMigrate 方法,进行数据库的迁移操作。

    // 定义一个全局的数据库连接变量
    var db *gorm.DB
    
    // User 定义数据库模型
    type User struct {
    	ID       uint   `json:"id" gorm:"primaryKey"`
    	Username string `json:"username"`
    	Email    string `json:"email"`
    }
    
    func main() {
    	// 创建一个服务
    	r := gin.Default()
    	// 使用中间件
    	r.Use(favicon.New("./static/favicon.ico"))
    	r.Use(myHandler())
    	// 加载静态页面
    	r.LoadHTMLGlob("templates/*")
    	// 数据库连接信息
    	dsn := "username:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
    	var err error
    
    	// 连接数据库
    	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
    	if err != nil {
    		log.Fatalf("无法连接数据库: %v", err)
    	}
    
    	// 自动迁移数据库
    	db.AutoMigrate(&User{})
    
    	r.Run() // listen and serve on 0.0.0.0:8080
    }
    

    项目启动如下,项目启动后,执行 AutoMigrate 方法。

    在这里插入图片描述

    连接配置的数据库,发现 AutoMigrate 初始化生成的表,数据库迁移操作成功。

    在这里插入图片描述

    6.4 数据库使用

    如下是获取用户列表和创建用户的两个函数方法。

    // 获取用户列表的处理函数
    func getUsers(c *gin.Context) {
    	var users []User
    	result := db.Find(&users)
    	if result.Error != nil {
    		c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
    		return
    	}
    	c.JSON(http.StatusOK, users)
    }
    
    // 创建用户的处理函数
    func createUser(c *gin.Context) {
    	var user User
    	if err := c.ShouldBindJSON(&user); err != nil {
    		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    		return
    	}
    	result := db.Create(&user)
    	if result.Error != nil {
    		c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
    		return
    	}
    	c.JSON(http.StatusOK, user)
    }
    

    main.go 中添加路由函数,如下所示:

    // 定义路由和处理函数
    r.GET("/users", getUsers)
    r.POST("/users", createUser)
    

    进行路由请求,如下所示:

    • POST 请求进行用户的创建。

      在这里插入图片描述

    • GET 请求获取用户信息。

      在这里插入图片描述

    数据库操作的完整代码为:

    // blog.euansu.cn
    // main.go
    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"github.com/thinkerou/favicon"
    	"gorm.io/driver/mysql"
    	"gorm.io/gorm"
    	"log"
    	"net/http"
    )
    
    func myHandler() gin.HandlerFunc {
    	return func(c *gin.Context) {
    		// 设置中间件的值
    		c.Set("usersession", "xxx")
    		if c.Request.URL.Path == "/" {
    			// 阻止
    			fmt.Println("阻止")
    			c.Abort()
    		}
    		// 放行
    		fmt.Println("放行")
    		c.Next()
    	}
    }
    
    // 定义一个全局的数据库连接变量
    var db *gorm.DB
    
    // User 定义数据库模型
    type User struct {
    	ID       uint   `json:"id" gorm:"primaryKey"`
    	Username string `json:"username"`
    	Email    string `json:"email"`
    }
    
    func main() {
    	// 创建一个服务
    	r := gin.Default()
    	// 使用中间件
    	r.Use(favicon.New("./static/favicon.ico"))
    	r.Use(myHandler())
    	// 加载静态页面
    	r.LoadHTMLGlob("templates/*")
    	// 数据库连接信息
    	dsn := "username:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
    	var err error
    
    	// 连接数据库
    	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
    	if err != nil {
    		log.Fatalf("无法连接数据库: %v", err)
    	}
    
    	// 自动迁移数据库
    	db.AutoMigrate(&User{})
    
    	// 定义路由和处理函数
    	r.GET("/users", getUsers)
    	r.POST("/users", createUser)
    
    	// 路由函数
    	r.GET("/", func(c *gin.Context) {
    		//c.String(200, "Hello, EuanSu")
    		c.HTML(http.StatusOK, "index.html", gin.H{"msg": "index.html", "path": "/"})
    	})
    
    	r.GET("/home", func(c *gin.Context) {
    		//c.String(200, "Hello, EuanSu")
    		c.HTML(http.StatusOK, "index.html", gin.H{"msg": "index.html", "path": "/home"})
    	})
    
    	r.Run() // listen and serve on 0.0.0.0:8080
    }
    
    func getUsers(c *gin.Context) {
    	var users []User
    	result := db.Find(&users)
    	if result.Error != nil {
    		c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
    		return
    	}
    	c.JSON(http.StatusOK, users)
    }
    
    func createUser(c *gin.Context) {
    	var user User
    	if err := c.ShouldBindJSON(&user); err != nil {
    		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    		return
    	}
    	result := db.Create(&user)
    	if result.Error != nil {
    		c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
    		return
    	}
    	c.JSON(http.StatusOK, user)
    }
    

    7.相关链接

    [1] Go Gin 简明教程 https://geektutu.com/post/quick-go-gin.html

    [2] 【【狂神说】Gin框架一小时上手 | 快速转型GoWeb开发 | Go语言零基础教程】 https://www.bilibili.com/video/BV1Rd4y1C7A1/?share_source=copy_web&vd_source=5fdcc6213ac2d30f16a78fe5d6e8df4d

  • 相关阅读:
    网件r7000梅林系统5g不稳定 5g信号经常掉线解决方法
    【网络协议】聊聊UDP协议
    OpenGL - Parallax Mapping
    数据结构每日亿题(二)
    比较各JAX-RS实现:Jersey,Restlet,CXF,RESTEasy
    【Linux基础】Linux的基本指令使用(超详细解析,小白必看系列)
    点云数据转pnts二进制数据
    MySQL操作合集
    tp3.2和tp5.0的区别
    java集合框架——List集合概述及ArrayList,LinkedList的区别
  • 原文地址:https://blog.csdn.net/suu_an/article/details/139721014