// 自定义html模板渲染器,要指定所有的html路径,不推荐
html := template.Must(template.ParseFiles(
"templates/login.html",
"templates/users/index.html",
"templates/center/index.html",
))
//应用这些模板
router.SetHTMLTemplate(html)
router.GET("/users/index", func(context *gin.Context) {
context.HTML(http.StatusOK, "users/index.html", gin.H{
"title": "users/index.html",
})
})
func formatAsDate(t time.Time) string {
year, month, day := t.Date()
return fmt.Sprintf("%d/%02d/%02d", year, month, day)
}
func main() {
router := gin.Default()
//用于代替模板里的 {{ }}(调用后端变量)的用法
router.Delims("<{", "}>")
//自定义模板函数 注意要把这个函数放在加载模板前
router.SetFuncMap(template.FuncMap{
"formatAsDate": formatAsDate,
})
//加载指定的模板文件
router.LoadHTMLFiles("./templates/raw.html")
router.GET("/raw", func(c *gin.Context) {
c.HTML(http.StatusOK, "raw.html", map[string]interface{}{
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
})
})
router.Run(":9999")
}
raw.html
<body>
date: <{.now | formatAsDate}>
body>
package main
import (
"github.com/gin-gonic/gin"
)
type LoginForm struct {
User string `form:"user" binding:"required"`
Password string `form:"password" binding:"required"`
}
func main() {**加粗样式**
router := gin.Default()
router.POST("/login", func(c *gin.Context) {
// 你可以使用显式绑定声明绑定 multipart form:
//c.ShouldBindWith(&form, binding.Form)
// 或者简单地使用 ShouldBind 方法自动绑定:
var form LoginForm
// 在这种情况下,将自动选择合适的绑定
if c.ShouldBind(&form) == nil {
if form.User == "Winnie-OCEAN" && form.Password == "789" {
c.JSON(200, gin.H{"status": "you are logged in",
"user": form.User,
"password": form.Password,
})
} else {
c.JSON(401, gin.H{"status": "unauthorized"})
}
}
})
router.Run()
}
router.POST("/login", func(c *gin.Context) {
// 或者简单地使用 ShouldBind 方法自动绑定:
//c.ShouldBind(&form)
var form LoginForm
// 你可以使用显式绑定声明绑定 multipart form:
if c.ShouldBindWith(&form, binding.Form) == nil {
if form.User == "OCEAN" && form.Password == "123456789" {
c.JSON(200, gin.H{"status": "you are logged in",
"user": form.User,
"password": form.Password,
})
} else {
c.JSON(401, gin.H{"status": "unauthorized"})
}
}
})
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
engine := gin.Default()
engine.POST("/post_form", func(context *gin.Context) {
//获取post过来的名为user的值
user := context.PostForm("user")
//获取post过来的名为password的值,第二个参数为默认值
password := context.DefaultPostForm("password", "123456")
context.JSON(http.StatusOK, gin.H{
"success": "login in",
"username": user,
"password": password,
})
})
engine.Run()
}
JSON 使用 unicode 替换特殊 HTML 字符,例如 < 变为 \ u003c。如果要按字面对这些字符进行编码,则可以使用 PureJSON。
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
//结果为 {"html":"\u003cb\u003eHello, world!\u003c/b\u003e"}
// 提供 unicode 实体
r.GET("/json", func(c *gin.Context) {
c.JSON(200, gin.H{
"html": "Hello, world!",
})
})
//输出结果{"html":"Hello, world!"}
// 提供字面字符
r.GET("/purejson", func(c *gin.Context) {
c.PureJSON(200, gin.H{
"html": "Hello, world!",
})
})
// 监听并在 0.0.0.0:8080 上启动服务
r.Run(":8080")
}
package main
import "github.com/gin-gonic/gin"
func main() {
engine := gin.Default()
engine.POST("/post_query", func(context *gin.Context) {
username := context.Query("username")
password := context.DefaultQuery("password", "123")
age := context.PostForm("age")
page := context.PostForm("page")
context.JSON(200, gin.H{
"username": username,
"password": password,
"age": age,
"page": page,
})
})
engine.Run(":9999")
}
JSON劫持,其实就是恶意网站,通过标签获取你的JSON数据,因为JSON数组默认为是可执行的JS,所以通过这种方式,可以获得你的敏感数据。最前面有个while(1);前缀,这就可以在
标签执行我们返回的数据时,就可以无限循环,阻止后面数组数据的执行,防止数据被劫持。
func main() {
r := gin.Default()
r.GET("/someJSON", func(c *gin.Context) {
names := []string{"winnie", "ocean", "22"}
// 将输出:while(1);["winnie", "ocean", "22"]
c.SecureJSON(http.StatusOK, names)
})
// 监听并在 0.0.0.0:8080 上启动服务
r.Run(":8080")
}
Gin默认的防JSON劫持的前缀是while(1);我们可以改变,通过r.SecureJsonPrefix方法设置即可,如:
func main() {
r := gin.Default()
//你也可以使用自己的 SecureJSON 前缀
r.SecureJsonPrefix(")]}',\n")
r.GET("/someJSON", func(c *gin.Context) {
names := []string{"winnie", "ocean", "22"}
// 将输出:
//)]}',
//["winnie","ocean","22"]
c.SecureJSON(http.StatusOK, names)
})
r.Run(":8080")
}
package main
import (
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/testdata/protoexample"
"net/http"
)
func main() {
r := gin.Default()
// gin.H 是 map[string]interface{} 的一种快捷方式
r.GET("/someJSON", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/moreJSON", func(c *gin.Context) {
// 使用一个结构体
var msg struct {
// 注意 msg.Name 在 JSON 中变成了 "user"
Name string `json:"user"`
Message string
Number int
}
msg.Name = "Lena"
msg.Message = "hey"
msg.Number = 123
// 将输出:{"user": "Lena", "Message": "hey", "Number": 123}
c.JSON(http.StatusOK, msg)
})
r.GET("/someXML", func(c *gin.Context) {
c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/someYAML", func(c *gin.Context) {
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/someProtoBuf", func(c *gin.Context) {
reps := []int64{int64(1), int64(2)}
label := "test"
// protobuf 的具体定义写在 testdata/protoexample 文件中。
data := &protoexample.Test{
Label: &label,
Reps: reps,
}
// 请注意,数据在响应中变为二进制数据
// 将输出(产生) data被ProtoBuf序列化了 的数据,生成一个二进制文件
// 序列化:将数据结构或者对象转化成二进制串的过程
c.ProtoBuf(http.StatusOK, data)
})
r.Run()
}
func main() {
engine := gin.Default()
// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
// 限制处理该文件所占用的最大内存
engine.MaxMultipartMemory = 8 << 20 // 8 MiB
engine.POST("/post", func(context *gin.Context) {
//单文件
file, err := context.FormFile("picture")
if err != nil {
log.Fatal(err)
}
//打印文件名
log.Println(file.Filename)
//设置上传路径 没有就默认当前目录下
dst := "./" + file.Filename
// 上传文件至指定的完整文件路径
context.SaveUploadedFile(file, dst)
context.String(200, "upload ok!!")
})
engine.Run()
}
func main() {
engine := gin.Default()
engine.MaxMultipartMemory = 8 << 20 //8Mib
engine.POST("/files", func(context *gin.Context) {
//MultipartForm是经过解析的 多部分表单,包括文件上传。
form, _ := context.MultipartForm() //多部份表单
files := form.File["uploads[]"] //收集名为 uploads[]的多个文件
//循环遍历输出文件名
for _, file := range files {
log.Println(file.Filename)
//上传文件 至 指定路径 没有路径,默认根目录。
context.SaveUploadedFile(file, file.Filename)
}
context.String(200, "upload files ok!!")
})
engine.Run()
}
访问http://localhost:8080/test
后,将下载url地址的图片并将其命名为123.png 但现在将其下载却打不开
func main() {
router := gin.Default()
router.GET("/test", func(c *gin.Context) {
response, err := http.Get("https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E5%BC%A0%E7%B4%AB%E5%AE%81%E5%9B%BE%E7%89%87&hs=0&pn=1&spn=0&di=7136437450519347201&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&ie=utf-8&oe=utf-8&cl=2&lm=-1&cs=2643363613%2C2106712228&os=3028774708%2C1970574687&simid=2643363613%2C2106712228&adpicid=0&lpn=0&ln=30&fr=ala&fm=&sme=&cg=&bdtype=0&oriquery=%E5%BC%A0%E7%B4%AB%E5%AE%81%E5%9B%BE%E7%89%87&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fn.sinaimg.cn%2Fsinakd20111%2F539%2Fw1960h2579%2F20210619%2F9e65-74af1bfc3b1873479616e5a37bb490eb.jpg%26refer%3Dhttp%3A%2F%2Fn.sinaimg.cn%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Dauto%3Fsec%3D1666405458%26t%3D02eb7dfb10fad465e8d88e94d0074659&fromurl=ippr_z2C%24qAzdH3FAzdH3Fh_z%26e3Bftgw_z%26e3Bv54_z%26e3BvgAzdH3Fw6ptvsj_0n9lnd0n8n_8kma11c18aa8aapj4p_z%26e3Bip4s&gsm=2&islist=&querylist=&dyTabStr=MCwzLDYsMiwxLDQsNSw4LDcsOQ%3D%3D.jpg")
// StatusCode 响应体的状态码
if err != nil || response.StatusCode != http.StatusOK {
//Status设置HTTP响应码 503
c.Status(http.StatusServiceUnavailable)
return
}
reader := response.Body //响应体
contentLength := response.ContentLength
contentType := response.Header.Get("Content-Type") //获得 响应体数据类型
extraHeaders := map[string]string{
"Content-Disposition": `attachment; filename="123.png"`,
}
// DataFromReader 将指定的读取器写入主体流 并 更新HTTP代码
c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
})
router.Run(":8080")
}
func main() {
r := gin.Default()
// 路由组使用 gin.BasicAuth() 中间件
// gin.Accounts 是 map[string]string 的一种快捷方式
authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
//用户名 密码
"foo": "bar",
"austin": "1234",
"lena": "hello2",
"manu": "4321",
}))
// /admin/secrets 端点
// 触发 "localhost:8080/admin/secrets
authorized.GET("/secrets", func(c *gin.Context) {
// 获取用户,它是由 BasicAuth 中间件设置的
//如果给定键存在,MustGet将返回该键的值,否则将出现panic。
user := c.MustGet(gin.AuthUserKey).(string) //获取输入的用户名
//secret 对应键名为user的值 也就是用户数据
if secret, ok := secrets[user]; ok {
c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
} else {
c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
}
})
// 监听并在 0.0.0.0:8080 上启动服务
r.Run(":8080")
}
func main() {
// 新建一个没有任何默认中间件的路由
r := gin.New()
authorized := r.Group("/")
authorized.Use(readEndpoint)
{
authorized.POST("/login", loginEndpoint)
}
// 嵌套路由组
//访问 http://localhost:8080/testing/analytics 得到结果
testing := authorized.Group("testing")
testing.GET("/analytics", analyticsEndpoint)
// 监听并在 0.0.0.0:8080 上启动服务
r.Run(":8080")
}
ShouldBindQuery 函数只绑定 url 查询参数而忽略 post 数据。
type Person struct {
Name string `form:"name"`
Address string `form:"address"`
}
func main() {
route := gin.Default()
//任何都注册一个与所有HTTP方法匹配的路由。获取,发布,放置,修补,头部,选项,删除,连接,跟踪
route.Any("/testing", startPage)
route.Run()
}
func startPage(c *gin.Context) {
var person Person
//ShouldBindQuery是c.ShouldBindWith(obj, binding.Query)的快捷方式。
if c.ShouldBindQuery(&person) == nil {
log.Println("====== Only Bind By Query String ======")
log.Println(person.Name)
log.Println(person.Address)
}
c.String(200, "Success\n")
c.JSON(200, gin.H{
"address": person.Address,
"name": person.Name,
})
}
当在中间件或 handler 中启动新的 Goroutine 时,不能使用原始的上下文,必须使用只读副本。
func main() {
r := gin.Default()
r.GET("/long_async", func(c *gin.Context) {
// 创建在 goroutine 中使用的副本
cCp := c.Copy()
go func() {
// 用 time.Sleep() 模拟一个长任务。
time.Sleep(5 * time.Second)
// 请注意您使用的是复制的上下文 "cCp",这一点很重要
log.Println("Done! in path " + cCp.Request.URL.Path)
}()
})
r.GET("/long_sync", func(c *gin.Context) {
// 用 time.Sleep() 模拟一个长任务。
time.Sleep(5 * time.Second)
// 因为没有使用 goroutine,不需要拷贝上下文
log.Println("Done! in path " + c.Request.URL.Path)
})
// 监听并在 0.0.0.0:8080 上启动服务
r.Run(":8080")
}
func main() {
// 禁用控制台颜色,将日志写入文件时不需要控制台颜色。
gin.DisableConsoleColor()
// Create 创建一个名为 Winne.log 的文件
f, _ := os.Create("2.log")
// DefaultWriter=os.Stdout 指向系统的标准输出
//将日志记录到文件 f
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")
}
要想多次绑定,可以使用 c.ShouldBindBodyWith. 只有某些格式需要此功能,如 JSON, XML, MsgPack, ProtoBuf。
type formA struct {
Foo string `json:"foo" xml:"foo" binding:"required"`
}
type formB struct {
Bar string `json:"bar" xml:"bar" binding:"required"`
}
func SomeHandler(c *gin.Context) {
objA := formA{}
objB := formB{}
// 读取 c.Request.Body 并将结果存入上下文。
if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
c.String(http.StatusOK, `the body should be formA`)
// 这时, 复用存储在上下文中的 body。
} else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
c.String(http.StatusOK, `the body should be formB JSON`)
// 可以接受其他格式
} else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {
c.String(http.StatusOK, `the body should be formB XML`)
} else {
...
}
}
func main() {
router := gin.Default()
router.POST("/post", func(c *gin.Context) {
ids := c.QueryMap("ids") //url参数
names := c.PostFormMap("names")//post过来的数据
fmt.Printf("ids: %v; names: %v", ids, names)
c.JSON(200, ids)
c.JSON(200, names)
})
router.Run()
}
func main() {
router := gin.Default()
// 使用现有的基础请求对象解析查询字符串参数。
// 示例 URL: /welcome?firstname=winnie&lastname=jine
router.GET("/welcome", func(c *gin.Context) {
firstname := c.DefaultQuery("firstname", "Guest")
lastname := c.Query("lastname") // c.Request.URL.Query().Get("lastname") 的一种快捷方式
c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})
router.Run(":8080")
}
// 绑定 JSON
type Login struct {
User string `form:"user" json:"user" xml:"user" binding:"required"`
Password string `form:"password" json:"password" xml:"password" binding:"required"`
}
func main() {
router := gin.Default()
// 绑定 JSON ({"user": "winnie", "password": "123"})
router.POST("/login", func(c *gin.Context) {
var json Login
if err := c.ShouldBindJSON(&json); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if json.User != "winnie" || json.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
c.JSON(200, json)
})
router.Run()
}
// 绑定 JSON
type Login struct {
User string `form:"user" json:"user" xml:"user" binding:"required"`
Password string `form:"password" json:"password" xml:"password" binding:"required"`
}
func main() {
router := gin.Default()
router.POST("/loginXML", func(c *gin.Context) {
var xml Login
if err := c.ShouldBindXML(&xml); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if xml.User != "OCEAN" || xml.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
c.JSON(200, xml)
})
router.Run()
}
type Login struct {
User string `form:"user" json:"user" xml:"user" binding:"required"`
Password string `form:"password" json:"password" xml:"password" binding:"required"`
}
func main() {
router := gin.Default()
// 绑定 HTML 表单 (user=manu&password=123)
router.POST("/loginForm", func(c *gin.Context) {
var form Login
// 根据 Content-Type Header 推断使用哪个绑定器。
if err := c.ShouldBind(&form); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if form.User != "manu" || form.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
c.JSON(200, form)
})
router.Run()
}
type Person struct {
ID string `uri:"id" binding:"required"`
Name string `uri:"name" binding:"required"`
}
func main() {
route := gin.Default()
route.GET("/:name/:id", func(c *gin.Context) {
var person Person
if err := c.ShouldBindUri(&person); err != nil {
c.JSON(400, gin.H{"msg": err.Error()})
return
}
c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
})
route.Run()
}
type Person struct {
Name string `form:"name"`
Address string `form:"address"`
Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
}
func main() {
route := gin.Default()
route.GET("/testing", startPage)
route.Run()
}
func startPage(c *gin.Context) {
var person Person
// 如果是 `GET` 请求,只使用 `Form` 绑定引擎(`query`)。
// 如果是 `POST` 请求,首先检查 `content-type` 是否为 `JSON` 或 `XML`,然后再使用 `Form`(`form-data`)。
// 查看更多:https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L88
if c.ShouldBind(&person) == nil {
log.Println(person.Name)
log.Println(person.Address)
log.Println(person.Birthday)
}
c.JSON(200, person)
c.String(200, "Success")
}
func main() {
r := gin.Default()
r.GET("/getb", func(context *gin.Context) {
var b StructB
//shouldbind与bind作用一致,但是 shouldbind相比较于bind能更好的控制绑定
context.ShouldBind(&b)
context.JSON(200, gin.H{
"f_a": b.NestedStruct.FieldA,
"f_b": b.FiledB,
})
})
r.GET("/getc", func(context *gin.Context) {
var c StructC
context.ShouldBind(&c)
context.JSON(200, gin.H{
//"f_a": (*(c.NestedStructPointer)).FieldA
"f_a": c.NestedStructPointer.FieldA,
"f_c": c.FieldC,
})
})
r.GET("/getd", func(context *gin.Context) {
var d StructD
context.ShouldBind(&d)
context.JSON(200, gin.H{
"f_x": d.NestedAnnoyStruct.FieldX,
"f_d": d.FieldD,
})
})
r.Run()
}
目前仅支持没有 form 的嵌套结构体 ,例如 下列 不支持以下格式的结构体
type StructX struct {
X struct {} `form:"name_x"` // 有 form
}
type StructY struct {
Y StructX `form:"name_y"` // 有 form
}
type StructZ struct {
Z *StructZ `form:"name_z"` // 有 form
}
func main() {
router := gin.Default()
router.GET("/", func(context *gin.Context) {
context.String(200, "自定义配置成功")
})
http.ListenAndServe(":8080", router)
}
func main() {
router := gin.Default()
router.GET("/", func(context *gin.Context) {
context.String(200, "配置成功")
})
s := &http.Server{
Addr: ":9999",
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
// 设置 example 变量
c.Set("example", "12345")
// 请求前
c.Next()
// 请求后
// Since返回从 t 开始经过的时间。
latency := time.Since(t)
log.Print(latency)
// 获取发送的 status Status返回当前请求的HTTP响应状态码。
status := c.Writer.Status()
log.Println(status)
}
}
func main() {
r := gin.New()
//或者 r.Use(Logger())
r.GET("/test", Logger(), func(c *gin.Context) {
//如果给定键存在,MustGet将返回该键的值,
example := c.MustGet("example").(string)
// 打印:"12345"
log.Println(example)
})
// 监听并在 0.0.0.0:8080 上启动服务
r.Run(":9999")
}
func main() {
router := gin.New()
// LoggerWithFormatter 中间件会写入日志到 gin.DefaultWriter
// 默认 gin.DefaultWriter = os.Stdout
//LoggerWithFormatter实例 是 一个Logger中间件,具有 指定的日志格式功能。
//LogFormatterParams是任何格式化程序在需要记录日志时都会提交的结构
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")
}
这个自定义验证器 验证流程有两个要素
一:需要传入两次时间 分别是 checkin与checkout 每传入一次, bookableDate 都需要验证一次
二:gtfield=CheckIn 用来约束 check_out时间 大于 check_in时间。
// Booking 包含绑定和验证的数据。
type Booking struct {
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
// gtfield 是一个默认规则,意思是要大于某个字段的值. gtfield=CheckIn表明 check_out的值要大于check_in的值
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn,bookabledate" time_format:"2006-01-02"`
}
// 定义一个验证方法,用来验证时间是否合法
// 验证方法返回值应该是个布尔值
// type Func func(fl FieldLevel) bool
// FieldLevel 包含验证字段的所有信息和帮助函数
var bookableDate validator.Func = func(fl validator.FieldLevel) bool {
// Field 返回当前字段进行验证
// 将值以 interface{} 类型返回
date, ok := fl.Field().Interface().(time.Time)
if ok {
today := time.Now()
//判断today是否在date之后 如果是 返回 true,否则返回 false
if today.After(date) {
return false
}
}
return true
}
func main() {
route := gin.Default()
// Validator 是实现 StructValidator 接口的 默认验证器.
// Engine 返回为StructValidator实现提供动力 的 底层验证器引擎。
// Validate 包含 验证器设置和缓存
// if语句 表示 是否验证器设置成功
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
//注册一个自定义验证方法 bookabledate
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()})
}
}
func main() {
router := gin.Default()
router.GET("/cookie", func(c *gin.Context) {
// 获取cookie
cookie, err := c.Cookie("gin_cookie")
//表示 获取出错
if err != nil {
cookie = "NotSet"
c.SetCookie("gin_cookie", "winnie", 3600, "/", "localhost", false, true)
}
fmt.Printf("Cookie value: %s \n", cookie)
})
router.Run()
}
第一次的 Cookie value: NotSet 原因:刚开始没获取到,所以cookie 值设为NotSet,并且重新设置了cookie,因为重新设置后并没有获取cookie,所以 cookie的值仍然是NotSet。输出为 Cookie value: NotSet
第二次 获取到cookie了 所以打印成功。
func main() {
router := gin.Default()
// 此 handler 将匹配 /user/john 但不会匹配 /user/ 或者 /user
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})
// 此 handler 将匹配 /user/john/ 和 /user/john/send
// 如果没有其他路由匹配过/user/john,它将重定向到 /user/john/
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")
}
下面这个代码是只能运行80端口,不能运行90端口。
func main() {
router := gin.Default()
router.GET("/", func(c *gin.Context) {
c.JSON(200, "我是8080")
})
router.Run(":8080") // data services
routerAdmin := gin.Default()
routerAdmin.GET("/", func(c *gin.Context) {
c.JSON(200, "我是8090")
})
routerAdmin.Run(":8090") // admin and monitor services
}
func main() {
router := gin.Default()
router.GET("/", func(c *gin.Context) {
c.JSON(200, "我是80端口")
})
go router.Run(":8080") // 开启一个携程
routerAdmin := gin.Default()
routerAdmin.GET("/", func(c *gin.Context) {
c.JSON(200, "我是90端口")
})
routerAdmin.Run(":8090") // admin and monitor services
}
func main() {
server01 := &http.Server{
Addr: ":8080",
Handler: router01(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
server02 := &http.Server{
Addr: ":8081",
Handler: router02(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
g.Go(func() error {
return server01.ListenAndServe()
})
g.Go(func() error {
return server02.ListenAndServe()
})
// Wait 等待阻塞,直到所有来自Go方法的函数调用都返回,然后从它们返回第一个非nil错误(如果有的话)。
if err := g.Wait(); err != nil {
log.Fatal(err)
}
}