企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] ## Gin安装 配置代理:[https://goproxy.cn,direct](https://goproxy.cn,direct) > go get -u github.com/gin-gonic/gin ## 入门案例 ~~~ package main ​ import ( "github.com/gin-gonic/gin" ) ​ func main() { // 创建一个默认的路由引擎 r := gin.Default() // GET:请求方式;/hello:请求的路径 // 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数 r.GET("/hello", func(c *gin.Context) { // c.JSON:返回JSON格式的数据 c.JSON(200, gin.H{ "message": "Hello world!", }) }) // 默认8080,括号里的参数是端口号 r.Run() } ~~~ ## Gin渲染 ### HTML渲染 我们首先定义一个存放模板文件的`templates`文件夹,然后在其内部按照业务分别定义一个`posts`文件夹和一个`users`文件夹。 `posts/index.html`文件的内容如下: ~~~ {{define "posts/index.html"}} <!DOCTYPE html> <html lang="en"> ​ <head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <meta http-equiv="X-UA-Compatible" content="ie=edge">    <title>posts/index</title> </head> <body>   {{.title}} </body> </html> {{end}} ~~~ `users/index.html`文件的内容如下: ~~~ {{define "users/index.html"}} <!DOCTYPE html> <html lang="en"> <head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <meta http-equiv="X-UA-Compatible" content="ie=edge">    <title>users/index</title> </head> <body>   {{.title}} </body> </html> {{end}} ~~~ Gin框架中使用`LoadHTMLGlob()`或者`LoadHTMLFiles()`方法进行HTML模板渲染。 ~~~ func main() { r := gin.Default() r.LoadHTMLGlob("templates/**/*") //r.LoadHTMLFiles("templates/posts/index.html", "templates/users/index.html") r.GET("/posts/index", func(c *gin.Context) { c.HTML(http.StatusOK, "posts/index.html", gin.H{ "title": "posts/index", }) }) ​ r.GET("users/index", func(c *gin.Context) { c.HTML(http.StatusOK, "users/index.html", gin.H{ "title": "users/index", }) }) ​ r.Run(":8080") } ~~~ ## 获取参数 ### 获取querystring参数 `querystring`指的是URL中`?`后面携带的参数,例如:`/user/search?username=小王子&address=沙河`。 获取请求的querystring参数的方法如下: ~~~ 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(http.StatusOK, gin.H{ "message":  "ok", "username": username, "address":  address, }) }) r.Run() } ~~~ ~~~ http://localhost:8080/user/search?username=小王子&address=沙河 ~~~ ### 获取path参数 请求的参数通过URL路径传递,例如:`/user/search/小王子/沙河`。 获取请求URL路径中的参数的方式如下。 ~~~ 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(http.StatusOK, gin.H{ "message":  "ok", "username": username, "address":  address, }) }) ​ r.Run(":8080") } ~~~ ## 文件上传 ### 单文件上传 写个前端页面,结构 ![](https://img.kancloud.cn/9c/f0/9cf09c185aa2ea0d07a753d11945c385_330x304.png) ~~~ {{define "uploads/upload.html"}}    <!DOCTYPE html>    <html lang="zh-CN">    <head>        <title>上传文件示例</title>    </head>    <body>    <form action="/uploads/upload" method="post" enctype="multipart/form-data">        <input type="file" name="f1">        <input type="submit" value="上传">    </form>    </body>    </html> {{end}} ~~~ 后台: ~~~ func main() { router := gin.Default()    //模板路径    router.LoadHTMLGlob("templates/**/*")        r.GET("/upload/index", func(c *gin.Context) {        //页面路径 c.HTML(200,"uploads/upload.html",nil) }) // 处理multipart forms提交文件时默认的内存限制是32 MiB // 可以通过下面的方式修改 // router.MaxMultipartMemory = 8 << 20 // 8 MiB router.POST("/upload", func(c *gin.Context) { // 单个文件 file, err := c.FormFile("f1") if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "message": err.Error(), }) return } ​ log.Println(file.Filename) dst := fmt.Sprintf("C:/tmp/%s", file.Filename) // 上传文件到指定的目录 c.SaveUploadedFile(file, dst) c.JSON(http.StatusOK, gin.H{ "message": fmt.Sprintf("'%s' uploaded!", file.Filename), }) }) router.Run() } ~~~ ### 多文件上传 input选择框里面加个`multiple`表示你可以多选 ~~~ {{define "uploads/uploads.html"}}    <!DOCTYPE html>    <html lang="zh-CN">    <head>        <title>上传文件示例</title>    </head>    <body>    <form action="/uploads" method="post" enctype="multipart/form-data">        <input type="file" name="files" multiple>        <input type="submit" value="上传">    </form>    </body>    </html> {{end}} ~~~ 后台: ~~~ r.POST("/uploads", func(c *gin.Context) { // Multipart form form, _ := c.MultipartForm() files := form.File["files"] ​ for _, file := range files { log.Println(file.Filename) dst := fmt.Sprintf("D:/%s", file.Filename) // 上传文件到指定的目录 c.SaveUploadedFile(file, dst) } c.JSON(http.StatusOK, gin.H{ "message": fmt.Sprintf("%d files uploaded!", len(files)), }) }) ~~~ 这次只有核心代码,可以结合前面单文件稍加修改,这并不难! ## 重定向 ### HTTP重定向 HTTP 重定向很容易。 内部、外部重定向均支持。 ~~~ r.GET("/test", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/") }) ~~~ ### 路由重定向 路由重定向,使用`HandleContext`: ~~~ r.GET("/test", func(c *gin.Context) {    // 指定重定向的URL    c.Request.URL.Path = "/test2"    r.HandleContext(c) }) r.GET("/test2", func(c *gin.Context) {    c.JSON(http.StatusOK, gin.H{"hello": "world"}) }) ~~~ ## 中间件 Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。 ### 定义中间件 Gin中的中间件必须是一个`gin.HandlerFunc`类型。例如我们像下面的代码一样定义一个统计请求耗时的中间件。 ~~~ // StatCost 是一个统计耗时请求耗时的中间件 func StatCost() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() c.Set("name", "小王子") // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值 // 调用该请求的剩余处理程序 c.Next() // 不调用该请求的剩余处理程序 // c.Abort() // 计算耗时 cost := time.Since(start) log.Println(cost) } } ~~~ ### 注册中间件 ~~~ func main() { // 新建一个没有任何默认中间件的路由 r := gin.New() // 注册一个全局中间件 r.Use(StatCost()) r.GET("/test", func(c *gin.Context) { name := c.MustGet("name").(string) // 从上下文取值 log.Println(name) c.JSON(http.StatusOK, gin.H{ "message": "Hello world!", }) }) r.Run() } ~~~ ### 为某个路由单独注册 ~~~ // 给/test2路由单独注册中间件(可注册多个) r.GET("/test2", StatCost(), func(c *gin.Context) { name := c.MustGet("name").(string) // 从上下文取值 log.Println(name) c.JSON(http.StatusOK, gin.H{ "message": "Hello world!", }) }) ~~~ ### 为路由组注册中间件 写法1: ~~~ shopGroup := r.Group("/shop", StatCost()) {    shopGroup.GET("/index", func(c *gin.Context) {...})    ... } ~~~ 写法2: ~~~ shopGroup := r.Group("/shop") shopGroup.Use(StatCost()) {    shopGroup.GET("/index", func(c *gin.Context) {...})    ... } ~~~ # Gorm框架 ## 安装 ~~~ go get -u github.com/jinzhu/gorm ~~~ ## 连接MySQL ~~~ import (  "github.com/jinzhu/gorm"  _ "github.com/jinzhu/gorm/dialects/mysql" ) ​ func main() {  db, err := gorm.Open("mysql", "user:password@(localhost)/dbname?charset=utf8mb4&parseTime=True&loc=Local")  defer db.Close() } ~~~ ## GORM操作MySQL ### 创建数据库 ~~~ CREATE DATABASE db1; ​ CREATE TABLE `user_info` ( `id` int(30) NOT NULL AUTO_INCREMENT COMMENT 'id', `name` varchar(255) DEFAULT NULL, `gender` varchar(255) DEFAULT NULL COMMENT 'Gender ', `hobby` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; ~~~ ~~~ 表与实体映射的问题搞了我很久,我今天第一次建表的时候字段的首字母都是大写,为了和实体名保持一致。结果反而让自己查不到数据,一直以为是代码的问题,直到把一个配置去了之后,go自动建表对比了下字段名才发现问题! ~~~ ### 基本操作 ~~~ package main ​ import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) ​ // UserInfo 用户信息 type UserInfo struct { ID int Name string Gender string Hobby string } ​ ​ func main() { db, err := gorm.Open("mysql", "root:123456@(127.0.0.1:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local") if err!= nil{ panic(err) } defer db.Close() ​ // 自动迁移 db.AutoMigrate(&UserInfo{}) ​ u1 := UserInfo{1, "七米", "男", "篮球"} u2 := UserInfo{2, "沙河娜扎", "女", "足球"} // 创建记录 db.Create(&u1) db.Create(&u2) // 查询 var u = new(UserInfo) db.First(u) fmt.Printf("%#v\n", u) ​ var uu UserInfo db.Find(&uu, "hobby=?", "足球") fmt.Printf("%#v\n", uu) ​ // 更新 db.Model(&u).Update("hobby", "双色球") // 删除 db.Delete(&u) } ~~~