Go语言Gin-1.路由
1.gin简介
-
镜像配置:
// 安装出现timeout问题可以配置go module镜像,终端执行下面命令,为阿里镜像 go env -w GO111MODULE=on go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
-
安装gin:
import "github.com/gin-gonic/gin"
-
一切始于hello world
package main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { // 1.创建路由 r := gin.Default() // 2.绑定路由规则,执行的函数 // gin.Context,封装了request和response r.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "hello World!") }) // 3.监听端口,默认在8080 // Run("里面不指定端口号默认为8080") r.Run(":8000") }
-
当运行时候会出现如下错误;
can't load package: package gin_demo is not in GOROOT // 可以使用go mod模式,在当前项目目录下初始化mod go mod init
-
此时访问
127.0.0.1:8000
就会返回hello world。
2.gin路由
2.1 基本路由
-
gin框架中采用的路由哭给予httpprouter做的。
packge main import ( "net/http" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "hello word") }) r.POST("/xxxpost",getting) r.PUT("/xxxput") //监听端口默认为8080 r.Run(":8000") }
2.2 Restful风格API
-
Gin 支持Restful风格API。表现层状态转化,如
1.获取文章 /blog/getXxx Get blog/Xxx 2.添加 /blog/addXxx POST blog/Xxx 3.修改 /blog/updateXxx PUT blog/Xxx 4.删除 /blog/delXxxx DELETE blog/Xxx
2.3API参数
-
API参数,可以通过Context 的Param方法来获取API参数。
func main() { r := gin.Default() r.GET("/user/:name/*action", func(context *gin.Context) { // 获取URL上name name := context.Param("name") // 获取URL上action action := context.Param("action") fmt.Println(action) // 去除两边 / action = strings.Trim(action, "/") context.String(http.StatusOK, name+" is "+action) }) r.Run(":8000") }
-
如下图:
![F3A7649C-316C-49E1-84C2-0A5909EC46E7](/Users/xujunkai/Library/Containers/com.tencent.qq/Data/Library/Application Support/QQ/Users/874524334/QQ/Temp.db/F3A7649C-316C-49E1-84C2-0A5909EC46E7.png)
2.4URL参数
-
URL参数可以通过DefaultQuery()或Query()方法获取。
-
DefaultQuery()若参数不对称,则返回默认值。Query()若不存在,返回空串。
r.GET("userinfo", func(c *gin.Context){ name := c.DefaultQuery("name","Tom") c.String(http.StatusOK, fmt.Sprintf("hello %s", name)) })
-
当不加参数访问URL:
http://127.0.0.1:8000/userinfo
,返回是:hello Tom
,当加参数访问URL:http://127.0.0.1:8000/userinfo/?name=Jery
,返回是:hello Jery
。
2.5表单参数
-
表单传输为post请求,http常见的传输格式为:
- application/json
- application/x-www-form-urlencoded
- application/xml
- multipart/form-data
-
表单可以通过如下方式访问,该方法默认解析x-www-form-urlencoded或from-data格式的参数。
<form action="http://localhost:8000/form" method="post" action="application/x-www-form-urlencoded"> 用户名:<input type="text" name="username" placeholder="请输入你的用户名"> <br> 密 码:<input type="password" name="userpassword" placeholder="请输入你的密码"> <br> <input type="submit" value="提交"> </form>
-
post请求处理:
r.POST("/form", func(c *gin.Context) { types := c.DefaultPostForm("type", "post") username := c.PostForm("username") password := c.PostForm("userpassword") c.String(http.StatusOK,fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types)) })
-
如下示例图:
![73478227-4D2D-4890-A7A8-B25BA628A84E](/Users/xujunkai/Library/Containers/com.tencent.qq/Data/Library/Application Support/QQ/Users/874524334/QQ/Temp.db/73478227-4D2D-4890-A7A8-B25BA628A84E.png)
2.6上传单个文件
-
gin文件上传与原生的net/http方法类似,不同在于gin把原声request封装到c.Request中
<!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>Document</title> </head> <body> <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data"> 上传文件:<input type="file" name="file" > <input type="submit" value="提交"> </form> </body> </html>
package main import ( "github.com/gin-gonic/gin" ) func main() { r := gin.Default() //限制上传最大尺寸 r.MaxMultipartMemory = 8 << 20 r.POST("/upload", func(c *gin.Context) { file, err := c.FormFile("file") if err != nil { c.String(500, "上传图片出错") } // c.JSON(200, gin.H{"message": file.Header.Context}) c.SaveUploadedFile(file, file.Filename) c.String(http.StatusOK, file.Filename) }) r.Run() }
-
上传特定文件
// 获取headers _, headers, err := c.Request.FormFile("file") if err != nil { log.Printf("error") } if headers.Size > 1024*1024*2 { fmt.Println("文件太大了") return } if headers.Header.Get("Content-Type") != "image/png" { fmt.Println("只允许上传png图片") return }
2.7上传多个文件
func main() {
// 1.创建路由
// 默认使用了2个中间件Logger(), Recovery()
r := gin.Default()
// 限制表单上传大小 8MB,默认为32MB
r.MaxMultipartMemory = 8 << 20
r.POST("/upload", func(c *gin.Context) {
form, err := c.MultipartForm()
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error()))
}
// 获取所有图片
files := form.File["files"]
// 遍历所有图片
for _, file := range files {
// 逐个存
if err := c.SaveUploadedFile(file, file.Filename); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error()))
return
}
}
c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
})
//默认端口号是8080
r.Run(":8000")
}
2.8路由的routes group
- 为了管理一些相同的URL
func main() {
// 1.创建路由
// 默认使用了2个中间件Logger(), Recovery()
r := gin.Default()
// 路由组1 ,处理GET请求
v1 := r.Group("/v1")
// {} 是书写规范
{
v1.GET("/login", login)
v1.GET("submit", submit)
}
v2 := r.Group("/v2")
{
v2.POST("/login", login)
v2.POST("/submit", submit)
}
r.Run(":8000")
}
2.9路由原理
- httproter会将所有路由规则构造一颗前缀树。比如有root and as at cn com
2.10路由拆分与注册
2.10.1 基本路由注册
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func helloHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "Hello www.topgoer.com!",
})
}
func main() {
r := gin.Default()
// 指定路由函数。
r.GET("/topgoer", helloHandler)
if err := r.Run(); err!=nil{
fmt.Println("startup service failed. err:%v\n", err)
}
}
// 访问http://localhost:8080/topgoer 返回{"message":"Hello www.topgoer.com!"}
2.10.2 路由拆分成单独文件或包
-
当你的项目规模太大,就不适合在项目的main.go文件中去实现路由注册相关逻辑了。我们会倾向于把路由部分的代码拆分出来。形成单独一个文件或包
// 项目目录结构 gin_demo |__go.sum |__main.go |__go.mod |__routers |___routers.go
-
Routers/routers.go
package routers import ( "github.com/gin-gonic/gin" "net/http" ) func helloHandler(c *gin.Context){ c.JSON(http.StatusOK, gin.H{ "message": "Hello www.topgoer.com", }) } // SetupRouter 用于配置路由信息 func SetupRouter() *gin.Engine { r := gin.Default() r.GET("/topgoer", helloHandler) return r }
-
main.go
package main import ( "fmt" "gin_demo/routers" ) func main() { r :=routers.SetupRouter() if err:= r.Run(); err != nil { fmt.Printf("startUp service failed, err:%v\n", err) } }
2.10.3 路由拆分成多个文件
-
当我们业务规模继续膨胀时候,单独的一个routers文件包已经无法满足,我们需求如下的目录
// 项目目录结构 gin_demo |__go.sum |__main.go |__go.mod |__routers |___blog.go |___shop.go
2.10.4路由拆分到不同APP
-
有时候项目实在太大,那么我们倾向于把业务拆分更详细一些,例如把不同业务代码拆分成不同APP
gin_demo |__app | |__blog | | |__handler.go | | |__router.go | |__shop | |__handler.go | |__router.go |__go.sum |__main.go |__go.mod |__routers |___routers.go
-
app/blog/handler.go
package blog
import (
"github.com/gin-gonic/gin"
"net/http"
)
func blogHandler(c *gin.Context){
c.JSON(http.StatusOK, gin.H{
"message": "Hello blog handler",
})
}
-
app/blog/router.go
package blog import "github.com/gin-gonic/gin" func Routers(e *gin.Engine){ e.GET("/blog",blogHandler) }
-
app/shop/handler.go
package shop import ( "github.com/gin-gonic/gin" "net/http" ) func helloHandler(c *gin.Context){ c.JSON(http.StatusOK, gin.H{ "message": "Hello handler", }) } func commentHandler(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "comment handler", }) }
-
app/shop/router.go
package shop import "github.com/gin-gonic/gin" func Routers(e *gin.Engine) { e.GET("/hello",helloHandler) e.GET("/comment",commentHandler) }
-
routers/routers.go
package routers import "github.com/gin-gonic/gin" type Option func(engine *gin.Engine) var options = []Option{} // 注册app路由配置 func Include(opts ...Option) { options = append(options, opts...) } //初始化操作 func Init() *gin.Engine { r := gin.New() for _, opt := range options{ opt(r) } return r }
-
main.go
package main import ( "fmt" "gin_demo/app/blog" "gin_demo/app/shop" "gin_demo/routers" ) func main() { // 家在多个APP的路由配置 routers.Include(shop.Routers,blog.Routers) // 初始化路由 r := routers.Init() if err := r.Run(); err != nil { fmt.Println("startup service failed, err:%v\n", err) } }