因为本人也是golang初学者,因此本教程停留在gin的使用层面,没有对其实现细节进行剖析,后面有机会再来进行深刻研究。git
gin是目前golang的主要web框架之一,之因此选择这个框架是由于其拥有高效的路由性能,而且有人长期维护,目前github上的star数已经破3W。本教程适用于新手上路。github
这里略过go的安装,直接进入gin的安装环节,本文经过govendor(管理包依赖工具)进行安装:golang
go get github.com/kardianos/govendor
复制代码
mkdir $GOPATH/src/myapp && cd $_
复制代码
govendor init
govendor fetch github.com/gin-gonic/gin
复制代码
到这里已经安装完毕,此时咱们能够看到项目中多了一个vendor目录,此目录下就是本项目所须要的依赖。web
gin的语法很是简单,咱们能够一目了然,下面的示例建立了一个gin router,使用了默认的中间件(logger和recovery)。而后建立了一个"/ping"请求的路由监听。最后启动服务,默认监听8080端口。shell
// main.go
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default() // 使用默认中间件(logger和recovery)
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{ // 返回一个JSON,状态码是200,gin.H是map[string]interface{}的简写
"message": "pong",
})
})
r.Run() // 启动服务,并默认监听8080端口
}
复制代码
执行命令:json
go run main.go
复制代码
请求方法包括:get, post, patch, delete and options。此外还有any,即任何请求方法都会监听到。服务器
func main() {
router := gin.Default()
router.GET("/someGet", handle)
router.POST("/somePost", handle)
router.PUT("/somePut", handle)
router.DELETE("/someDelete", handle)
router.PATCH("/somePatch", handle)
router.HEAD("/someHead", handle)
router.OPTIONS("/someOptions", handle)
router.ANY("/any", handle)
router.Run()
}
func handle(context *gin.Context) {
context.String(http.StatusOK, "hello world")
}
复制代码
分组路由能够经过router.Group:app
func main() {
router := gin.Default()
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080")
}
复制代码
func main() {
router := gin.Default()
// 匹配/user/john
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})
// 匹配/user/john/和/user/john/send
router.GET("/user/: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(":8080")
}
复制代码
func main() {
router := gin.Default()
// welcome?firstname=Jane&lastname=Doe
router.GET("/user", func(c *gin.Context) {
firstname := c.DefaultQuery("name", "kim") // 获取query中的name,没有的话就为kim
lastname := c.Query("age") // 获取query中的age
c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})
router.Run(":8080")
}
复制代码
func main() {
router := gin.Default()
router.POST("/form_post", func(c *gin.Context) {
message := c.PostForm("age")
nick := c.DefaultPostForm("name", "kim")
c.JSON(200, gin.H{
"status": "posted",
"message": message,
"nick": nick,
})
})
router.Run(":8080")
}
复制代码
curl http://127.0.0.1:8080 -X POST -d 'name=john&age=25'
复制代码
咱们已经见识了x-www-form-urlencoded
类型的参数处理,如今愈来愈多的应用习惯使用JSON来通讯,也就是不管返回的response仍是提交的request,其content-type类型都是application/json
的格式。而对于一些旧的web表单页仍是x-www-form-urlencoded
的形式,这就须要咱们的服务器能改hold住这多种content-type的参数了。
因为go是静态语言,须要先实现定义数据模型,这就须要用到gin的model bind功能了。框架
gin使用go-playground/validator.v8验证参数,查看完整文档。curl
须要在绑定的字段上设置tag,好比,绑定格式为json,须要这样设置json:"fieldname"
。
此外,Gin还提供了两套绑定方法:
Bind
, BindJSON
, BindXML
, BindQuery
, BindYAML
MustBindWith
,若是存在绑定错误,请求将被如下指令停止 c.AbortWithError(400, err).SetType(ErrorTypeBind)
,响应状态代码会被设置为400,请求头Content-Type
被设置为text/plain; charset=utf-8
。注意,若是你试图在此以后设置响应代码,将会发出一个警告 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422
,若是你但愿更好地控制行为,请使用ShouldBind
相关的方法ShouldBind
, ShouldBindJSON
, ShouldBindXML
, ShouldBindQuery
, ShouldBindYAML
当咱们使用绑定方法时,Gin会根据Content-Type推断出使用哪一种绑定器,若是你肯定你绑定的是什么,你可使用MustBindWith
或者BindingWith
。
你还能够给字段指定特定规则的修饰符,若是一个字段用binding:"required"
修饰,而且在绑定时该字段的值为空,那么将返回一个错误。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Person struct {
Name string `json:"name" binding:"required"` // json格式从name取值,而且该值为必须的
Age int `json:"age" binding:"required,gt=20"` // json格式从age取值,而且该值为必须的,且必须大于20
}
func main() {
router := gin.Default()
router.POST("/test", func(context *gin.Context) {
var person Person
// 这里我肯定传过来的必定是JSON因此用ShouldBindJSON,不然能够用ShouldBind
if err := context.ShouldBindJSON(&person); err != nil {
context.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
context.JSON(http.StatusOK, gin.H{
"success": true,
})
})
router.Run(":3000")
}
复制代码
curl http://localhost:3000/test -X POST -d '{"name":"kim","age": 10}'
复制代码
package main
import (
"net/http"
"reflect"
"time"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"gopkg.in/go-playground/validator.v8"
)
type Booking struct {
// 这里的验证方法为bookabledate
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
// gtfield=CheckIn表示大于的字段为CheckIn
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
}
func bookableDate( v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string, ) bool {
// 这里有两个知识点,映射和断言
// 在这里,field是一个reflect.Type的接口类型变量,经过Interface方法得到field接口类型变量的真实类型,能够理解为reflect.Value的逆操做
// 在这里,断言就是将一个接口类型的变量转化为time.Time,前提是后者必须实现了前者的接口
// 综上,这里就是将field进行了类型转换
if date, ok := field.Interface().(time.Time); ok {
today := time.Now()
if today.Year() > date.Year() || today.YearDay() > date.YearDay() {
return false
}
}
return true
}
func main() {
route := gin.Default()
// 注册自定义验证器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("bookabledate", bookableDate)
}
route.GET("/bookable", getBookable)
route.Run(":8085")
}
func getBookable(c *gin.Context) {
var b Booking
if err := c.ShouldBindWith(&b, binding.Query); err == nil {
c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}
复制代码