Gin框架
安装gin
下载并安装gin
go get -u github.com/gin-gonic/gin 如果安装失败,首先执行命令:go env -w GOSUMDB=off 如果不可以 ,切换国内源: go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.io,direct 将gin引入到代码 import “github.com/gin-gonic/gin”(可选)导入net/http。例如,如果使用常量,则需要这样做http.StatusOK。
import “net/http”
开始第一个gin
package mainimport ("net/http""github.com/gin-gonic/gin")func main() {// 1.创建路由r := gin.Default()// 2.绑定路由规则,执行的函数// gin.Context,封装了request和responser.GET("/", func(c *gin.Context) {c.String(http.StatusOK, "hello World!")})// 3.监听端口,默认在8080// Run("里面不指定端口号默认为8080")r.Run(":7711")}
游览器访问:http://localhost:7711/
路由
基本路由
gin框架中采用的路由库是基于httprouter的,地址为:https://github.com/julienschmidt/httprouter
r.POST(“/mpost”)
r.PUT(“/mput”)
r.DELETE(“/mdelete”)
API参数
- 可以通过Context的Param方法来获取API参数
localhost:8000/xxx/zhangsan

URL参数
- URL参数可以通过DefaultQuery()或Query()方法获取
- DefaultQuery()若参数不村则,返回默认值,Query()若不存在,返回空串
- API ? name=zs

表单参数
- 表单传输为post请求,http常见的传输格式为四种:
- application/json
- application/x-www-form-urlencoded
- application/xml
- multipart/form-data
- 表单参数可以通过PostForm()方法获取,该方法默认解析的是x-www-form-urlencoded或from-data格式的参数
PostForm()

BindJSON()
用作application/json参数请求关键点:**结构体名称不能与请求json的字段名一致**

上传文件
http://www.topgoer.com/gin%E6%A1%86%E6%9E%B6/gin%E8%B7%AF%E7%94%B1/routesgroup.html
routers group
- routes group是为了管理一些相同的URL

gin数据解析和绑定
// 定义接收数据的结构体type Login struct {// binding:"required"修饰的字段,若接收为空值,则报错,是必须字段User string `form:"username" json:"user" uri:"user" xml:"user" binding:"required"`Pssword string `form:"password" json:"password" uri:"password" xml:"password" binding:"required"`}// 1.创建路由// 默认使用了2个中间件Logger(), Recovery()r := gin.Default()// 将request的body中的数据,自动按照json格式解析到结构体if err := c.ShouldBindJSON(&json); err != nil {// 返回错误信息// gin.H封装了生成json数据的工具c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}// Bind()默认解析并绑定form格式// 根据请求头中content-type自动推断if err := c.Bind(&form); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}// Bind()默认解析并绑定form格式// 根据请求头中content-type自动推断if err := c.ShouldBindUri(&login); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}
gin渲染
各种数据格式的响应
// 1.jsonr.GET("/someJSON", func(c *gin.Context) {c.JSON(200, gin.H{"message": "someJSON", "status": 200})})// 2. 结构体响应r.GET("/someStruct", func(c *gin.Context) {var msg struct {Name stringMessage stringNumber int}msg.Name = "root"msg.Message = "message"msg.Number = 123c.JSON(200, msg)})// 3.XMLr.GET("/someXML", func(c *gin.Context) {c.XML(200, gin.H{"message": "abc"})})// 4.YAML响应r.GET("/someYAML", func(c *gin.Context) {c.YAML(200, gin.H{"name": "zhangsan"})})// 5.protobuf格式,谷歌开发的高效存储读取的工具// 数组?切片?如果自己构建一个传输格式,应该是什么格式?r.GET("/someProtoBuf", func(c *gin.Context) {reps := []int64{int64(1), int64(2)}// 定义数据label := "label"// 传protobuf格式数据data := &protoexample.Test{Label: &label,Reps: reps,}c.ProtoBuf(200, data)})
html模板渲染
gin支持加载HTML模板, 然后根据模板参数进行配置并返回相应的数据,本质上就是字符串替换
LoadHTMLGlob()方法可以加载模板文件
- 如果你需要引入静态文件需要定义一个静态文件目录
r.Static("/assets", "./assets")// 重定向c.Redirect(http.StatusMovedPermanently, "http://www.5lmh.com")// 1.异步r.GET("/long_async", func(c *gin.Context) {// 需要搞一个副本copyContext := c.Copy()// 异步处理go func() {time.Sleep(3 * time.Second)log.Println("异步执行:" + copyContext.Request.URL.Path)}()})// 2.同步r.GET("/long_sync", func(c *gin.Context) {time.Sleep(3 * time.Second)log.Println("同步执行:" + c.Request.URL.Path)})
中间件
全局中间件

使用:r.Use(MiddleWare())
取值:req, _ := c.Get(“request”)
Next()方法

局部中间件

计算程序用时

Cookie
func main() {// 1.创建路由// 默认使用了2个中间件Logger(), Recovery()r := gin.Default()// 服务端要给客户端cookier.GET("cookie", func(c *gin.Context) {// 获取客户端是否携带cookiecookie, err := c.Cookie("key_cookie")if err != nil {cookie = "NotSet"// 给客户端设置cookie// maxAge int, 单位为秒// path,cookie所在目录// domain string,域名// secure 是否智能通过https访问// httpOnly bool 是否允许别人通过js获取自己的cookiec.SetCookie("key_cookie", "value_cookie", 60, "/","localhost", false, true)}fmt.Printf("cookie的值是: %s\n", cookie)})r.Run(":8000")}
参数验证
结构体验证
type Person struct {//不能为空并且大于10Age int `form:"age" binding:"required,gt=10"`Name string `form:"name" binding:"required"`Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`}if err := c.ShouldBind(&person); err != nil {c.String(500, fmt.Sprint(err))return}
自定义验证
package mainimport ("net/http""reflect""github.com/gin-gonic/gin""github.com/gin-gonic/gin/binding""gopkg.in/go-playground/validator.v8")/*对绑定解析到结构体上的参数,自定义验证功能比如我们要对 name 字段做校验,要不能为空,并且不等于 admin ,类似这种需求,就无法 binding 现成的方法需要我们自己验证方法才能实现 官网示例(https://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Custom_Functions)这里需要下载引入下 gopkg.in/go-playground/validator.v8*/type Person struct {Age int `form:"age" binding:"required,gt=10"`// 2、在参数 binding 上使用自定义的校验方法函数注册时候的名称Name string `form:"name" binding:"NotNullAndAdmin"`Address string `form:"address" binding:"required"`}// 1、自定义的校验方法func nameNotNullAndAdmin(v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {if value, ok := field.Interface().(string); ok {// 字段不能为空,并且不等于 adminreturn value != "" && !("5lmh" == value)}return true}func main() {r := gin.Default()// 3、将我们自定义的校验方法注册到 validator中if v, ok := binding.Validator.Engine().(*validator.Validate); ok {// 这里的 key 和 fn 可以不一样最终在 struct 使用的是 keyv.RegisterValidation("NotNullAndAdmin", nameNotNullAndAdmin)}/*curl -X GET "http://127.0.0.1:8080/testing?name=&age=12&address=beijing"curl -X GET "http://127.0.0.1:8080/testing?name=lmh&age=12&address=beijing"curl -X GET "http://127.0.0.1:8080/testing?name=adz&age=12&address=beijing"*/r.GET("/5lmh", func(c *gin.Context) {var person Personif e := c.ShouldBind(&person); e == nil {c.String(http.StatusOK, "%v", person)} else {c.String(http.StatusOK, "person bind err:%v", e.Error())}})r.Run()}
示例2:
package mainimport ("net/http""reflect""time""github.com/gin-gonic/gin""github.com/gin-gonic/gin/binding""gopkg.in/go-playground/validator.v8")// Booking contains binded and validated data.type Booking struct {//定义一个预约的时间大于今天的时间CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`//gtfield=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.Interface().(time.Time)获取参数值并且转换为时间格式if date, ok := field.Interface().(time.Time); ok {today := time.Now()if today.Unix() > date.Unix() {return false}}return true}func main() {route := gin.Default()//注册验证if v, ok := binding.Validator.Engine().(*validator.Validate); ok {//绑定第一个参数是验证的函数第二个参数是自定义的验证函数v.RegisterValidation("bookabledate", bookableDate)}route.GET("/5lmh", getBookable)route.Run()}func getBookable(c *gin.Context) {var b Bookingif 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()})}}
多语言翻译验证
package mainimport ("fmt""github.com/gin-gonic/gin""github.com/go-playground/locales/en""github.com/go-playground/locales/zh""github.com/go-playground/locales/zh_Hant_TW"ut "github.com/go-playground/universal-translator""gopkg.in/go-playground/validator.v9"en_translations "gopkg.in/go-playground/validator.v9/translations/en"zh_translations "gopkg.in/go-playground/validator.v9/translations/zh"zh_tw_translations "gopkg.in/go-playground/validator.v9/translations/zh_tw")var (Uni *ut.UniversalTranslatorValidate *validator.Validate)type User struct {Username string `form:"user_name" validate:"required"`Tagline string `form:"tag_line" validate:"required,lt=10"`Tagline2 string `form:"tag_line2" validate:"required,gt=1"`}func main() {en := en.New()zh := zh.New()zh_tw := zh_Hant_TW.New()Uni = ut.New(en, zh, zh_tw)Validate = validator.New()route := gin.Default()route.GET("/5lmh", startPage)route.POST("/5lmh", startPage)route.Run(":8080")}func startPage(c *gin.Context) {//这部分应放到中间件中locale := c.DefaultQuery("locale", "zh")trans, _ := Uni.GetTranslator(locale)switch locale {case "zh":zh_translations.RegisterDefaultTranslations(Validate, trans)breakcase "en":en_translations.RegisterDefaultTranslations(Validate, trans)breakcase "zh_tw":zh_tw_translations.RegisterDefaultTranslations(Validate, trans)breakdefault:zh_translations.RegisterDefaultTranslations(Validate, trans)break}//自定义错误内容Validate.RegisterTranslation("required", trans, func(ut ut.Translator) error {return ut.Add("required", "{0} must have a value!", true) // see universal-translator for details}, func(ut ut.Translator, fe validator.FieldError) string {t, _ := ut.T("required", fe.Field())return t})//这块应该放到公共验证方法中user := User{}c.ShouldBind(&user)fmt.Println(user)err := Validate.Struct(user)if err != nil {errs := err.(validator.ValidationErrors)sliceErrs := []string{}for _, e := range errs {sliceErrs = append(sliceErrs, e.Translate(trans))}c.String(200, fmt.Sprintf("%#v", sliceErrs))}c.String(200, fmt.Sprintf("%#v", "user"))}
其它
日志文件

Air实时加载
安装:go get -u github.com/cosmtrek/air
直接运行:可以直接使用 air 运行
# 首先在当前目录下查找 `.air.conf`配置文件,如果找不到就使用默认的air -c .air.conf
