文章目录

  • 1.环境配置
  • 2.测试运行
  • 3.请求路由
    • 3.1 多种请求类型
    • 3.2 静态文件夹
    • 3.3 参数作为URL
  • 4.获取请求参数
    • 4.1 获取get请求
    • 4.2 泛绑定
    • 4.3 获取body内容值
    • 4.4 获取bind参数
  • 5.验证请求参数
    • 5.1 结构体验证
    • 5.2 自定义验证器
  • 6.中间件
    • 6.1 使用gin的中间件
    • 6.2 使用自定义的中间件
  • 7.gin的其他补充
    • 7.1 优雅关停服务器
    • 7.2 模版渲染

1.环境配置

假设你的go环境已经配置好了

  • 打开终端进行配置
mkdir -p $GOPATH/src/github.com/zxhjames/Zonst
//在gopath的目录下新建该文件夹(在这里我以自己的github仓库地址命名为主)
cd $_
//进入该项目根目录
go get -v github.com/gin-gonic/gin
//获取gin框架

2.测试运行

  • 进入该项目,新建一个子项目
pwd //显示在当前的项目根目录下 GOPATH/src/github.com/zxhjames/Zonst
mkdir -p gindemo1/main && cd gindemo1/main
vi main.go
  • 编写测试文件
package main
import "github.com/gin-gonic/gin"
func main(){//1.创建gin的实例router:=gin.Default()//2.发送get请求,并声明回调函数router.GET("/ping",func(c *gin.Context){//3.返回一个json格式的数据c.JSON(200,gin.H{"message":"pong",})})//4.启动服务器,默认监听8080端口router.Run()
  • 查看返回数据
{"message": "pong"
}

3.请求路由

请求路由分为

  • 多种请求类型 (get/post/delete/put…等8种请求类型)
  • 绑定静态文件夹 (可以将gin作为静态服务器使用)
  • 参数作为url (多见于restful服务中)
  • 泛绑定 (类似于nginx的路由匹配)

3.1 多种请求类型

package mainimport "github.com/gin-gonic/gin"func main(){r:=gin.Default()//getr.GET("/get", func(context *gin.Context) {context.String(200,"get")})//postr.GET("/post", func(context *gin.Context) {context.String(200,"post")})//deleter.Handle("DELETE","/delete", func(context *gin.Context) {context.String(200,"delete")})//设置多个路由方法,多种请求都可以同时打到any上r.Any("any", func(context *gin.Context) {context.String(200,"any")})r.Run()
}

3.2 静态文件夹

这种方式可以直接将本地的静态资源映射到服务器的URL上

  • 新建项目文件夹,在main文件夹下新建一个assets和static文件夹,分别在两个文件夹下新建html文件,如图:

  • 编写main.go文件

package mainimport ("github.com/gin-gonic/gin""net/http"
)
/**
请求路由,静态文件夹*/
func main(){r:=gin.Default()//设置静态文件夹绑定r.Static("/assets","./assets")//或者另外一种方法r.StaticFS("/static",http.Dir("static"))//设置单个静态资源r.StaticFile("/favicon.ico","./favicon.ico")r.Run()
}
  • 在终端运行
go build -o router_static && ./router_static

注意这里不能直接在goland里运行,会访问不到的,最后使用

curl "http://localhost:8080/assets/a.html"
curl "http://localhost:8080/static/b.html"

可以分别访问到静态资源

3.3 参数作为URL

  • 新建文件夹,main.go写入
package mainimport "github.com/gin-gonic/gin"func main(){r:=gin.Default()r.GET("/:name/:id", func(context *gin.Context) {context.JSON(200,gin.H{"name":context.Param("name"),"id":context.Param("id"),})})r.Run()
}
  • 测试结果
    访问 http://localhost:8080/james/10
    输出
{"id": "10","name": "james"
}

4.获取请求参数

  • 获取get请求
  • 获取post请求
  • 获取body值
  • 获取参数绑定结构体(重要!)

4.1 获取get请求

package mainimport "github.com/gin-gonic/gin"func main(){//声明gin实例r:=gin.Default()r.GET("/test", func(context *gin.Context) {//获取第一个参数firstName := context.Query("first_name")//获取第二个参数,有默认值lastName := context.DefaultQuery("last_name","last_default_name")context.String(200,"%s,%s",firstName,lastName)})r.Run()
}

可以看到,第二个参数传不传值是有区别的

curl -X GET 'http://localhost:8080/test?first_name=james&last_name=vincent' //output james,vincentcurl -X GET 'http://localhost:8080/test?first_name=james' //output james

4.2 泛绑定

package mainimport "github.com/gin-gonic/gin"func main(){r:=gin.Default()// 所有以/user/为前缀的的URL都要打到helloworld上r.GET("/user/*action", func(context *gin.Context) {context.String(200,"hello world")})r.Run()
}
curl -X GET 'http://localhost:8080/user/hhh
curl -X GET 'http://localhost:8080/user/xxx

由于泛绑定了以/user为前缀的地址,可以看到最终都会显示hello world

4.3 获取body内容值

package mainimport ("bytes""github.com/gin-gonic/gin""io/ioutil""net/http"
)
/**
获取body内容*/
func main(){r:=gin.Default()r.POST("/test", func(context *gin.Context) {//拿到body的字节内容bodyByts,err:=ioutil.ReadAll(context.Request.Body)if err != nil {//如果抛异常则打印错误context.String(http.StatusBadRequest,err.Error())context.Abort()}else{/**正确则输出正确内容*/context.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyByts))firstName:=context.PostForm("firstName")lastName:=context.DefaultPostForm("lastName","default")context.String(200,"%s,%s,%s",firstName,lastName,string(bodyByts))}})r.Run()
}

下面是apipost返回的结果

4.4 获取bind参数

package mainimport ("github.com/gin-gonic/gin""time"
)
/**
测试传递结构体*/
type Person struct {/**结构体参数绑定表单参数*/Name string `form:"name"`Address string `form:"address"`Birthday time.Time `form:"birthday" time_format:"2006-01-04"` //设置日期格式
}
func main(){r:=gin.Default()r.GET("/testing",testing)r.POST("/testing",testing)r.Run()
}func testing(context *gin.Context){var person Person/**这里是根据请求的content-type来做不同的bind操作*///绑定结构体if err:=context.ShouldBind(&person);err== nil {//如果请求正确context.String(200,"%v",person)}else{//如果请求参数错误context.String(200,"person bind error:%v",err)}
}

测试post请求

测试get请求

5.验证请求参数

  • 结构体验证
  • 自定义验证(结构体验证不满足时)
  • 升级验证-支持多语言错误信息

5.1 结构体验证

package mainimport ("github.com/gin-gonic/gin"
)/**
验证结构体参数的验证*/
type Person struct {/**结构体参数绑定表单参数*///bind中有连续条件都要满足时,要使用,间隔,如果要求任意一个能满足那么就会用| 隔开Age int `form:"age" binding:"required,gt=10"`  //required表示三个参数必传,其中的age还要大于10adnaAddress string `form:"address" binding:"required"`Name string `form:"name" binding:"required"`
}func main(){r:=gin.Default()r.GET("/testing", func(context *gin.Context) {var person Person//声明验证参数if err:=context.ShouldBind(&person);err!=nil {context.String(500,"%v",err)}context.String(200,"%v",person)})r.Run()
}

如果我们传入的age值小于或等于10,那么就不能满足验证条件,会访问失败

5.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 {//绑定验证器 bookabledate ,同时设置日期格式CheckIn  time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`//要保证checkout时间要大于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 {/**重写验证器,预定时间一定要保证大于今天的日期*/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(":8080")
}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()})}
}

下面进行测试,首先我们输入一个合法的表单,两个日期都大于今天的日期,显然是合法的

下面是不合法的例子,这里的check_in还是昨天的日期,显然是不合法的

6.中间件

  • 使用gin的中间件
  • 自定义ip白名单中间件

6.1 使用gin的中间件

package mainimport ("github.com/gin-gonic/gin""io""os"
)//gin的中间件
func main(){//将日志结果输出到文件f,_:=os.Create("gin.log")gin.DefaultWriter = io.MultiWriter(f) // 将普通日志写入文件gin.DefaultErrorWriter = io.MultiWriter(f) // 将错误日志写入文件r:=gin.New() // New()也是中间件r.Use(gin.Logger(),gin.Recovery()) //使用recovery捕获错误,防止进程挂掉r.GET("/test", func(context *gin.Context) {name:=context.DefaultQuery("name","defaultName")context.String(200,"%s",name)})r.Run()
}

接下来进行测试,每次测试完,我们都可以在项目的根目录下找到该日志文件

6.2 使用自定义的中间件

这次我们定义一个过滤IP地址的中间件,判断请求的IP数组中是否有与主机相同放入IP

package mainimport "github.com/gin-gonic/gin"/**
2.17 自定义白名单中间件*/
func IPAuthMiddleware() gin.HandlerFunc {return func(c *gin.Context) {/**设置白名单服务器列表*/ipList:=[]string{"127.0.0.1",//"127.0.0.2",}flag:=falseclientIP:=c.ClientIP()for _,host:=range ipList{if clientIP == host {flag = truebreak}}if !flag {c.String(401,"%s not in iplist",clientIP)c.Abort()}}
}
func main(){r:=gin.Default()/**使用自己定义的文件*/r.Use(IPAuthMiddleware())r.GET("/test", func(context *gin.Context) {context.String(200,"hello test")})r.Run()
}

本机的ip为127.0.0.1,所以启动后,会显示 “hello test”,然而如果将请求数组中的IP改为127.0.0.2,那么将显示不再iplist中

7.gin的其他补充

  • 优雅关停
  • 模版渲染
  • 自动证书配置

7.1 优雅关停服务器

传统方法(上)和本例方法的区别

package mainimport ("context""github.com/gin-gonic/gin""log""net/http""os""os/signal""syscall""time"
)/**
优雅关停*/func main(){r:=gin.Default()r.GET("/test", func(context *gin.Context) {//休眠5五秒time.Sleep(5*time.Second)context.String(200,"hello world")})/**创建一个server对象*/srv:=&http.Server{Addr:              ":8085",Handler:           r,TLSConfig:         nil,ReadTimeout:       0,ReadHeaderTimeout: 0,WriteTimeout:      0,IdleTimeout:       0,MaxHeaderBytes:    0,TLSNextProto:      nil,ConnState:         nil,ErrorLog:          nil,}/**将server放入协程*/go func() {/**如果报错,或者是服务器没有开启,就报错*/if err:=srv.ListenAndServe();err!=nil && err!=http.ErrServerClosed{log.Fatalf("listen:%s\n",err)}}()//定义一个信号quit:=make(chan os.Signal)//阻塞channalsignal.Notify(quit,syscall.SIGINT,syscall.SIGTERM)<-quitlog.Println("shutdown Server...")//设置超时显示ctx,cancel:=context.WithTimeout(context.Background(),10*time.Second)defer cancel()//关服务器if err:=srv.Shutdown(ctx);err!=nil{log.Fatal("server shutdown:",err)}log.Println("server exiting")
}

运行程序,由于我们给请求的响应时间设定为5秒,所以当我们另外开一个终端
输入curl -X GET "http://127.0.0.1:8085/test",并且马上关闭服务器,这时可以发现,终端仍然可以显示出hello world,同时服务器终端也显示了server shutdown

7.2 模版渲染

这次学习一下模版渲染的技术,首先我们在项目的main文件夹下面新建一个template文件夹,在里面新建一个a.html文件,并且定义一个变量title

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
{{.title}}
</body>
</html>

接下来去创建main.go文件,如下

package main
import "github.com/gin-gonic/gin"
func main(){r:=gin.Default()r.LoadHTMLGlob("template/*")r.GET("/index", func(context *gin.Context) {context.HTML(200,"a.html",gin.H{//绑定到html的属性上"title":"a.html",})})/**注意,这一步要在手动启动服务器,不能直接在golang里面run*/r.Run()
}

接下来去访问http://localhost:8080/index就可以看到我们的静态页面和里面的属性值了

gin阶段学习(入门)相关推荐

  1. 深度学习入门指北——从硬件到软件

    作者:隔壁王大喵 近日,Rachel Thomas在fast.ai上发布了一篇博文<What you need to do deep learning>,他希望通过这篇文章回答一些深度学习 ...

  2. BP算法双向传_链式求导最缠绵(深度学习入门系列之八)

    摘要: 说到BP(Back Propagation)算法,人们通常强调的是反向传播,其实它是一个双向算法:正向传播输入信号,反向传播误差信息.接下来,你将看到的,可能是史上最为通俗易懂的BP图文讲解, ...

  3. 深度学习入门之PyTorch学习笔记:深度学习介绍

    深度学习入门之PyTorch学习笔记:深度学习介绍 绪论 1 深度学习介绍 1.1 人工智能 1.2 数据挖掘.机器学习.深度学习 1.2.1 数据挖掘 1.2.2 机器学习 1.2.3 深度学习 第 ...

  4. Python学习入门1:Python 新手入门引导

    这是一篇 Python 入门指南,针对那些没有任何编程经验,从零开始学习 Python 的同学.不管你学习的出发点是兴趣驱动.拓展思维,还是工作需要.想要转行,都可以此文作为一个参考. 在这个信息爆炸 ...

  5. 深度学习入门笔记(四):神经网络

    专栏--深度学习入门笔记 推荐文章 深度学习入门笔记(一):机器学习基础 深度学习入门笔记(二):神经网络基础 深度学习入门笔记(三):感知机 深度学习入门笔记(四):神经网络 深度学习入门笔记(五) ...

  6. 转g代码教程_图深度学习入门教程(九)——图滤波神经网络模型

    本教程是一个系列免费教程,争取每月更新2到4篇.(由于精力有限,近期停止了一段时间,在此向大家道个歉). 主要是基于图深度学习的入门内容.讲述最基本的基础知识,其中包括深度学习.数学.图神经网络等相关 ...

  7. 给深度学习入门者的Python快速教程 - 番外篇之Python-OpenCV

    转载自:https://zhuanlan.zhihu.com/p/24425116 本篇是前面两篇教程:给深度学习入门者的Python快速教程 - 基础篇 给深度学习入门者的Python快速教程 - ...

  8. pytorch深度学习入门笔记

    Pytorch 深度学习入门笔记 作者:梅如你 学习来源: 公众号: 阿力阿哩哩.土堆碎念 B站视频:https://www.bilibili.com/video/BV1hE411t7RN? 中国大学 ...

  9. 民法学习入门的“听说读写”之道

    民法学习入门的"听说读写"之道 作者:姚明斌 副教授 华东政法大学法律学院民商法教研室 时值防疫停课的特殊时期,考虑到华政今年有千余位本科生同学将开始民法专业课程的学习,教研室主任 ...

最新文章

  1. OceanBase是如何解决城市级故障容灾的
  2. :x 和 :wq 的区别
  3. 当前版本与卡刷包android_Z2 Android 6.0.1卡刷包 23.5.0.486发布,快刷起来!(来自XDA)...
  4. 计算机电路的基本罗门,模拟电路设计经验12条
  5. 判断回文和查询最大公共字符串
  6. ERROR: ld.so: object '/usr/lib64/libtcmalloc.so.4' from LD_PRELOAD cannot be preloaded: ignored
  7. python之logging模块简单用法
  8. 【poj1743-Musical Theme】不可重叠最长重复子串-后缀数组
  9. tftp工具使用说明
  10. 【开小灶】如何网盘批量转存?
  11. java中null字符串与字符串长度为0的区别
  12. 趣味编程入门 Scratch 开发跳一跳小游戏-邵立志-专题视频课程
  13. 3.2.2 nodeMCU固件烧录
  14. 感染人数已经超过6千,何时达到峰值?数学统计建模分析告诉你
  15. 回顾一年的IT学习历程与大学生活
  16. upp(统一流程平台)项目范围说明书
  17. 数据结构与算法之美笔记-链表(Linked list)
  18. 基于人工智能的滚动轴承PHM方法综述
  19. 接手1号店的京东 更看重与沃尔玛的合作
  20. 熬之滴水穿石:Servlet--Web万物之源(1)

热门文章

  1. scrapy实例 ----- 爬取小说
  2. 23 20210525+0529直播 企业微信接口测试实战1+2
  3. mac更改管理员密码
  4. AJ5利用数据源的validation属性对控件进行validation验证
  5. 咕咕机显示服务器请求异常,咕咕机云服务器状态异常
  6. win10任务栏显示“中/英“语言标识
  7. 朴素贝叶斯文本分类(python代码实现)
  8. 轻松上手Manjaro之Manjaro系统配置
  9. 解决raw.githubusercontent.com地址DNS污染的方法参考
  10. 通过python爬取笔趣阁小说,获取图片保存本地,数据保存mysql