目录
前言
请先安装好goland,配置好GOPATH
安装
国内环境安装
1、使用goproxy工具代理,从国内下载
在安装第三方包的时候,由于网络问题golang.org在国内无法正常使用,导致我们无法使用第三方包管理工具,因此可以通过修改系统环境变量将golang.org切换至国内CDN。
在你的项目目录下 执行下面几个命令
1、开启代理
1 2 |
go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct |
2、初始化项目
1 |
go mod init 你的项目名 |
会在项目根目录下生成go.mod文件。go.mod是一个go项目的依赖管理文件。
3、下载gin
1 |
go get -u github.com/gin-gonic/gin |
这样就能将 gin以module的方式安装到你的项目的Lib里面了,倘若执行完上面的命令,且没报错,但在goland里import gin时像下面一样飘红
需要把下面这个Enable Go Modules integration
给勾选上:
再运行就可以正常起来了:【注意:main.go的包名必须是package main】
第一个Gin程序
在一个空文件夹里新建文件main.go
。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// geektutu.com // 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, Geektutu") }) r.Run() // listen and serve on 0.0.0.0:8080 } |
- 首先,我们使用了gin.Default()生成了一个实例,这个实例即 WSGI 应用程序。
- 接下来,我们使用r.Get(“/”, …)声明了一个路由,告诉 Gin 什么样的URL 能触发传入的函数,这个函数返回我们想要显示在用户浏览器中的信息。
- 最后用 r.Run()函数来让应用运行在本地服务器上,默认监听端口是 _8080_,可以传入参数设置端口,例如r.Run(“:9999”)即运行在 _9999_端口。
路由(Route)
路由方法有 GET, POST, PUT, PATCH, DELETE 和 OPTIONS,还有Any,可匹配以上任意类型的请求。
无参数
1 2 3 4 |
// 无参数 r.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "Who are you?") }) |
解析路径参数
有时候我们需要动态的路由,如 /user/:name
,通过调用不同的 url 来传入不同的 name。/user/:name/*role
,*
代表可选。
1 2 3 4 5 |
// 匹配 /user/geektutu r.GET("/user/:name", func(c *gin.Context) { name := c.Param("name") c.String(http.StatusOK, "Hello %s", name) }) |
获取Query参数
1 2 3 4 5 6 |
// 匹配users?name=xxx&role=xxx,role可选 r.GET("/users", func(c *gin.Context) { name := c.Query("name") role := c.DefaultQuery("role", "teacher") c.String(http.StatusOK, "%s is a %s", name, role) }) |
获取POST参数
1 2 3 4 5 6 7 8 9 10 |
// POST r.POST("/form", func(c *gin.Context) { username := c.PostForm("username") password := c.DefaultPostForm("password", "000000") // 可设置默认值 c.JSON(http.StatusOK, gin.H{ "username": username, "password": password, }) }) |
Query和POST混合参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// GET 和 POST 混合 r.POST("/posts", func(c *gin.Context) { id := c.Query("id") page := c.DefaultQuery("page", "0") username := c.PostForm("username") password := c.DefaultPostForm("username", "000000") // 可设置默认值 c.JSON(http.StatusOK, gin.H{ "id": id, "page": page, "username": username, "password": password, }) }) |
Map参数(字典参数)
1 2 3 4 5 6 7 8 9 |
r.POST("/post", func(c *gin.Context) { ids := c.QueryMap("ids") names := c.PostFormMap("names") c.JSON(http.StatusOK, gin.H{ "ids": ids, "names": names, }) }) |
重定向(Redirect)
1 2 3 4 5 6 7 8 |
r.GET("/redirect", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "/index") }) r.GET("/goindex", func(c *gin.Context) { c.Request.URL.Path = "/" r.HandleContext(c) }) |
分组路由(Grouping Routes)
如果有一组路由,前缀都是/api/v1
开头,是否每个路由都需要加上/api/v1
这个前缀呢?答案是不需要,分组路由可以解决这个问题。利用分组路由还可以更好地实现权限控制,例如将需要登录鉴权的路由放到同一分组中去,简化权限控制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// group routes 分组路由 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) } |
上传文件
单个文件
1 2 3 4 5 |
r.POST("/upload1", func(c *gin.Context) { file, _ := c.FormFile("file") // c.SaveUploadedFile(file, dst) c.String(http.StatusOK, "%s uploaded!", file.Filename) }) |
多个文件
1 2 3 4 5 6 7 8 9 10 11 |
r.POST("/upload2", func(c *gin.Context) { // Multipart form form, _ := c.MultipartForm() files := form.File["upload[]"] for _, file := range files { log.Println(file.Filename) // c.SaveUploadedFile(file, dst) } c.String(http.StatusOK, "%d files uploaded!", len(files)) }) |
HTML模板(Template)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
type student struct { Name string Age int8 } r.LoadHTMLGlob("templates/*") stu1 := &student{Name: "Geektutu", Age: 20} stu2 := &student{Name: "Jack", Age: 22} r.GET("/arr", func(c *gin.Context) { c.HTML(http.StatusOK, "arr.tmpl", gin.H{ "title": "Gin", "stuArr": [2]*student{stu1, stu2}, }) }) |
1 2 3 4 5 6 7 8 9 |
<!-- templates/arr.tmpl --> <html> <body> <p>hello, {{.title}}</p> {{range $index, $ele := .stuArr }} <p>{{ $index }}: {{ $ele.Name }} is {{ $ele.Age }} years old</p> {{ end }} </body> </html> |
- Gin默认使用模板Go语言标准库的模板text/template和html/template,语法与标准库一致,支持各种复杂场景的渲染。
- 参考官方文档text/template,html/template
中间件(Middleware)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 作用于全局 r.Use(gin.Logger()) r.Use(gin.Recovery()) // 作用于单个路由 r.GET("/benchmark", MyBenchLogger(), benchEndpoint) // 作用于某个组 authorized := r.Group("/") authorized.Use(AuthRequired()) { authorized.POST("/login", loginEndpoint) authorized.POST("/submit", submitEndpoint) } |
如何自定义中间件呢?
1 2 3 4 5 6 7 8 9 10 11 12 |
func Logger() gin.HandlerFunc { return func(c *gin.Context) { t := time.Now() // 给Context实例设置一个值 c.Set("geektutu", "1111") // 请求前 c.Next() // 请求后 latency := time.Since(t) log.Print(latency) } } |
热加载调试 Hot Reload
Python 的 Flask框架,有 debug 模式,启动时传入 debug=True 就可以热加载(Hot Reload, Live Reload)了。即更改源码,保存后,自动触发更新,浏览器上刷新即可。免去了杀进程、重新启动之苦。
Gin 原生不支持,但有很多额外的库可以支持。例如
- github.com/codegangsta/gin
- github.com/pilu/fresh
这次,我们采用 github.com/pilu/fresh
。
1 |
go get -v -u github.com/pilu/fresh |
安装好后,只需要将go run main.go命令换成fresh即可。每次更改源文件,代码将自动重新编译(Auto Compile)。
参考 github.com/pilu/fresh – Github