https://www.jianshu.com/p/98965b3ff638/
github.com/gin-gonic/gin是一个轻量级的 WEB 框架,支持 RestFull 风格 API,支持 GET,POST,PUT,PATCH,DELETE,OPTIONS 等 http 方法,支持文件上传,分组路由,Multipart/Urlencoded FORM,以及支持 JsonP,参数处理等等功能,这些都和 WEB 紧密相关,通过提供这些功能,使开发人员更方便地处理 WEB 业务。
使用 GET, POST, PUT, PATCH, DELETE, OPTIONS
func main() {
// Disable Console Color
// gin.DisableConsoleColor()
// 使用默认中间件创建一个gin路由器
// logger and recovery (crash-free) 中间件
router := gin.Default()
router.GET("/someGet", getting)
router.POST("/somePost", posting)
router.PUT("/somePut", putting)
router.DELETE("/someDelete", deleting)
router.PATCH("/somePatch", patching)
router.HEAD("/someHead", head)
router.OPTIONS("/someOptions", options)
// 默认启动的是 8080端口,也可以自己定义启动端口
router.Run()
// router.Run(":3000") for a hard coded port
}
项目中导入
import "github.com/gin-gonic/gin"
导入NET/HTTP(可选)。例如,如果使用诸如HTTP.StasuCK之类的常数,则需要这样做。
import "net/http"
A demo:
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200,gin.H{
"message":"pong",
})
})
r.Run()
}
启动后,在浏览器输入:浏览其会渲染输出:
http://127.0.0.1:8080/ping
还可以用如下的方式,渲染浏览器页面输出字符串:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
router.GET("/someGet",getting)
router.Run()
}
func getting(c *gin.Context) {
c.String(http.StatusOK,"Hello gin")
}
http://127.0.0.1:8080/someGet
我们点开源码发现,不管是渲染输出json格式、还是简单的字符串都是调用了c.Render方法,以下只是列出了2种,还有其他的诸如HTML、IndentedJSON、SecureJSON、JSONP、AsciiJSON、XML、YAML、ProtoBuf等。
func (c *Context) String(code int, format string, values ...interface{}) {
c.Render(code, render.String{Format: format, Data: values})
}
func (c *Context) JSON(code int, obj interface{}) {
c.Render(code, render.JSON{Data: obj})
}
获取路径中的参数:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
// 此规则能够匹配/someGet/aaa这种格式,但不能匹配/usomeGet/ 或 /someGet这种格式
router.GET("/someGet/:name",getting)
// 但是,这个规则既能匹配/someGet/aaa/格式也能匹配/someGet/aaa/send这种格式
// 如果没有其他路由器匹配/someGet/aaa,它将重定向到/someGet/aaa/
router.GET("/someGet/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
message := name + "is" + action
c.String(http.StatusOK,message)
})
router.Run()
}
func getting(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK,"Hello %s",name)
}
所以我们浏览器输入http://0.0.0.0:8080/someGet/aaaa可以
http://0.0.0.0:8080/someGet/或者http://0.0.0.0:8080/someGet都报错误
以上是一个参数,我们还可以定义多级参数。
http://127.0.0.1:8080/someGet/bbb
获取Get参数:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
// 匹配的url格式: /someGet?firstname=Jane&lastname=Doe
router.GET("/someGet",getting)
router.Run()
}
func getting(c *gin.Context) {
firstname := c.DefaultQuery("firstname","Guest")
lastname := c.Query("lastname")
c.String(http.StatusOK,"Hello: %s,%s",firstname,lastname)
}
浏览器输入http://127.0.0.1:8080/someGet?firstname=wyf&lastname=123
渲染页面输出为Hello wyf 123
浏览器输入http://127.0.0.1:8080/someGet?lastname=123,渲染页面输出为Hello Guest 123
http://127.0.0.1:8080/someGet?firstname=aaa&lastname=bbb
http://127.0.0.1:8080/someGet?lastname=123
获取Post 参数
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.POST("/someGet",getting)
router.Run()
}
func getting(c *gin.Context) {
message := c.PostForm("message")
nick := c.DefaultPostForm("nick","anonymous") // 此方法可以设置默认值
c.JSON(200,gin.H{
"status":"posted",
"message":message,
"nick":nick,
})
}
Get+Post 混合形式:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
router.POST("/someGet",getting)
router.Run()
}
func getting(c *gin.Context) {
id := c.Query("id")
page := c.DefaultQuery("page","0")
name := c.PostForm("name")
message := c.PostForm("message")
c.String(http.StatusOK,"id: %s; page: %s; name: %s; message: %s",id,page,name,message)
}
构造Map格式:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
router.POST("/someGet",getting)
router.Run()
}
func getting(c *gin.Context) {
ids := c.QueryMap("ids")
names := c.PostFormMap("names")
fmt.Printf("ids:%v; names:%v\n",ids,names)
c.JSON(200,gin.H{
"ids":ids,
"names":names,
})
c.String(http.StatusOK,"ids:%v; names:%v",ids,names)
}
路由分组:
func main() {
router := gin.Default()
// Simple group: v1
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
// Simple group: v2
v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080")
}
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
v1 := router.Group("/v1")
{
v1.GET("/read",readEndpoint)
}
v2 := router.Group("/v2")
{
v2.GET("/login",loginEndpoint)
}
router.Run(":8080")
}
func readEndpoint(c *gin.Context) {
fmt.Println("read")
}
func loginEndpoint(c *gin.Context) {
fmt.Println("login")
}
浏览器输入http://127.0.0.1:8080/v1/read
后台log输出read
浏览器输入http://127.0.0.1:8080/v2/login
后台log输出login
上传文件
上传文件的文件名可以由用户自定义,所以可能包含非法字符串,为了安全起见,应该由服务端统一文件名规则。
单文件上传:
func main() {
router := gin.Default()
// 给表单限制上传大小 (默认 32 MiB)
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// 单文件
file, _ := c.FormFile("file")
log.Println(file.Filename)
// 上传文件到指定的路径
// c.SaveUploadedFile(file, dst)
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
router.Run(":8080")
}
curl 测试:
curl -X POST http://localhost:8080/upload \
-F "file=@/Users/appleboy/test.zip" \
-H "Content-Type: multipart/form-data"
多文件上传:
func main() {
router := gin.Default()
// 给表单限制上传大小 (默认 32 MiB)
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// 多文件
form, _ := c.MultipartForm()
files := form.File["upload[]"]
for _, file := range files {
log.Println(file.Filename)
// 上传文件到指定的路径
// c.SaveUploadedFile(file, dst)
}
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
})
router.Run(":8080")
}
curl 测试:
curl -X POST http://localhost:8080/upload \
-F "upload[]=@/Users/appleboy/test1.zip" \
-F "upload[]=@/Users/appleboy/test2.zip" \
-H "Content-Type: multipart/form-data"
无中间件启动:
使用r := gin.New()
代替r := gin.Default() // 默认启动方式,包含 Logger、Recovery 中间件
使用中间件:
func main() {
// 创建一个不包含中间件的路由器
r := gin.New()
// 全局中间件
// 使用 Logger 中间件
r.Use(gin.Logger())
// 使用 Recovery 中间件
r.Use(gin.Recovery())
// 路由添加中间件,可以添加任意多个
r.GET("/benchmark", MyBenchLogger(), benchEndpoint)
// 路由组中添加中间件
// authorized := r.Group("/", AuthRequired())
// exactly the same as:
authorized := r.Group("/")
// per group middleware! in this case we use the custom created
// AuthRequired() middleware just in the "authorized" group.
authorized.Use(AuthRequired())
{
authorized.POST("/login", loginEndpoint)
authorized.POST("/submit", submitEndpoint)
authorized.POST("/read", readEndpoint)
// nested group
testing := authorized.Group("testing")
testing.GET("/analytics", analyticsEndpoint)
}
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
写日志文件:
func main() {
// 禁用控制台颜色
gin.DisableConsoleColor()
// 创建记录日志的文件
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
// 如果需要将日志同时写入文件和控制台,请使用以下代码
// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
router.Run(":8080")
}
自定义日志格式
func main() {
router := gin.New()
// LoggerWithFormatter 中间件会将日志写入 gin.DefaultWriter
// By default gin.DefaultWriter = os.Stdout
router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
// 你的自定义格式
return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
param.ClientIP,
param.TimeStamp.Format(time.RFC1123),
param.Method,
param.Path,
param.Request.Proto,
param.StatusCode,
param.Latency,
param.Request.UserAgent(),
param.ErrorMessage,
)
}))
router.Use(gin.Recovery())
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
router.Run(":8080")
}
输出示例:
::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" "