gin阶段学习(入门)
文章目录
- 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阶段学习(入门)相关推荐
- 深度学习入门指北——从硬件到软件
作者:隔壁王大喵 近日,Rachel Thomas在fast.ai上发布了一篇博文<What you need to do deep learning>,他希望通过这篇文章回答一些深度学习 ...
- BP算法双向传_链式求导最缠绵(深度学习入门系列之八)
摘要: 说到BP(Back Propagation)算法,人们通常强调的是反向传播,其实它是一个双向算法:正向传播输入信号,反向传播误差信息.接下来,你将看到的,可能是史上最为通俗易懂的BP图文讲解, ...
- 深度学习入门之PyTorch学习笔记:深度学习介绍
深度学习入门之PyTorch学习笔记:深度学习介绍 绪论 1 深度学习介绍 1.1 人工智能 1.2 数据挖掘.机器学习.深度学习 1.2.1 数据挖掘 1.2.2 机器学习 1.2.3 深度学习 第 ...
- Python学习入门1:Python 新手入门引导
这是一篇 Python 入门指南,针对那些没有任何编程经验,从零开始学习 Python 的同学.不管你学习的出发点是兴趣驱动.拓展思维,还是工作需要.想要转行,都可以此文作为一个参考. 在这个信息爆炸 ...
- 深度学习入门笔记(四):神经网络
专栏--深度学习入门笔记 推荐文章 深度学习入门笔记(一):机器学习基础 深度学习入门笔记(二):神经网络基础 深度学习入门笔记(三):感知机 深度学习入门笔记(四):神经网络 深度学习入门笔记(五) ...
- 转g代码教程_图深度学习入门教程(九)——图滤波神经网络模型
本教程是一个系列免费教程,争取每月更新2到4篇.(由于精力有限,近期停止了一段时间,在此向大家道个歉). 主要是基于图深度学习的入门内容.讲述最基本的基础知识,其中包括深度学习.数学.图神经网络等相关 ...
- 给深度学习入门者的Python快速教程 - 番外篇之Python-OpenCV
转载自:https://zhuanlan.zhihu.com/p/24425116 本篇是前面两篇教程:给深度学习入门者的Python快速教程 - 基础篇 给深度学习入门者的Python快速教程 - ...
- pytorch深度学习入门笔记
Pytorch 深度学习入门笔记 作者:梅如你 学习来源: 公众号: 阿力阿哩哩.土堆碎念 B站视频:https://www.bilibili.com/video/BV1hE411t7RN? 中国大学 ...
- 民法学习入门的“听说读写”之道
民法学习入门的"听说读写"之道 作者:姚明斌 副教授 华东政法大学法律学院民商法教研室 时值防疫停课的特殊时期,考虑到华政今年有千余位本科生同学将开始民法专业课程的学习,教研室主任 ...
最新文章
- OceanBase是如何解决城市级故障容灾的
- :x 和 :wq 的区别
- 当前版本与卡刷包android_Z2 Android 6.0.1卡刷包 23.5.0.486发布,快刷起来!(来自XDA)...
- 计算机电路的基本罗门,模拟电路设计经验12条
- 判断回文和查询最大公共字符串
- ERROR: ld.so: object '/usr/lib64/libtcmalloc.so.4' from LD_PRELOAD cannot be preloaded: ignored
- python之logging模块简单用法
- 【poj1743-Musical Theme】不可重叠最长重复子串-后缀数组
- tftp工具使用说明
- 【开小灶】如何网盘批量转存?
- java中null字符串与字符串长度为0的区别
- 趣味编程入门 Scratch 开发跳一跳小游戏-邵立志-专题视频课程
- 3.2.2	nodeMCU固件烧录
- 感染人数已经超过6千,何时达到峰值?数学统计建模分析告诉你
- 回顾一年的IT学习历程与大学生活
- upp(统一流程平台)项目范围说明书
- 数据结构与算法之美笔记-链表(Linked list)
- 基于人工智能的滚动轴承PHM方法综述
- 接手1号店的京东 更看重与沃尔玛的合作
- 熬之滴水穿石:Servlet--Web万物之源(1)
热门文章
- scrapy实例 ----- 爬取小说
- 23 20210525+0529直播 企业微信接口测试实战1+2
- mac更改管理员密码
- AJ5利用数据源的validation属性对控件进行validation验证
- 咕咕机显示服务器请求异常,咕咕机云服务器状态异常
- win10任务栏显示“中/英“语言标识
- 朴素贝叶斯文本分类(python代码实现)
- 轻松上手Manjaro之Manjaro系统配置
- 解决raw.githubusercontent.com地址DNS污染的方法参考
- 通过python爬取笔趣阁小说,获取图片保存本地,数据保存mysql