package main
import (
"errors"
"fmt"
"github.com/gin-gonic/autotls"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"golang.org/x/crypto/acme/autocert"
"io"
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"net/http/httptest"
"os"
"os/signal"
"strings"
"syscall"
"testing"
"time"
)
func main() {
fmt.Println("go-gin测试")
// 快速启动框架
// router := gin.New() // 默认不带中间件
router := gin.Default() // 默认带了 Logger 和 Recovery 中间件
router.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
//r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
// 使用 GET、POST、PUT、PATCH、DELETE 和 OPTIONS
router.GET("/someGet", getting)
router.POST("/somePost", posting)
router.PUT("/somePut", putting)
router.DELETE("/someDelete", deleting)
router.PATCH("/somePatch", patching)
router.HEAD("/someHead", head)
router.OPTIONS("/someOptions", options)
// 获取路径中的参数
// This handler will match /user/john but will not match /user/ or /user
router.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
})
// However, this one will match /user/john/ and also /user/john/send
// If no other routers match /user/john, it will redirect to /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)
})
// For each matched request Context will hold the route definition
router.POST("/user/:name/*action", func(c *gin.Context) {
b := c.FullPath() == "/user/:name/*action" // true
c.String(http.StatusOK, "%t", b)
})
// This handler will add a new router for /user/groups.
// Exact routes are resolved before param routes, regardless of the order they were defined.
// Routes starting with /user/groups are never interpreted as /user/:name/... routes
router.GET("/user/groups", func(c *gin.Context) {
c.String(http.StatusOK, "The available groups are [...]")
})
// 获取字符串中的参数
// Query string parameters are parsed using the existing underlying request object.
// The request responds to a url matching: /welcome?firstname=Jane&lastname=Doe
router.GET("/welcome", func(c *gin.Context) {
firstname := c.DefaultQuery("firstname", "Guest")
lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname")
c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})
// Multipart/Urlencoded 表格
router.POST("/form_post", func(c *gin.Context) {
message := c.PostForm("message")
nick := c.DefaultPostForm("nick", "anonymous")
c.JSON(200, gin.H{
"status": "posted",
"message": message,
"nick": nick,
})
})
// 查询 提交表单示例
// POST /post?id=1234&page=1 HTTP/1.1
// Content-Type: application/x-www-form-urlencoded
// name=manu&message=this_is_great
router.POST("/post", func(c *gin.Context) {
id := c.Query("id")
page := c.DefaultQuery("page", "0")
name := c.PostForm("name")
message := c.PostForm("message")
fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
})
// 输出的结果: id: 1234; page: 1; name: manu; message: this_is_great
// 映射查询字符串或提交表单参数 示例
// POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
// Content-Type: application/x-www-form-urlencoded
// names[first]=thinkerou&names[second]=tianou
router.POST("/post", func(c *gin.Context) {
ids := c.QueryMap("ids")
names := c.PostFormMap("names")
fmt.Printf("ids: %v; names: %v", ids, names)
})
// 输出结果: ids: map[b:hello a:1234]; names: map[second:tianou first:thinkerou]
// 上传单个文件示例
// curl -X POST http://localhost:8080/upload \
// -F "file=@/Users/appleboy/test.zip" \
// -H "Content-Type: multipart/form-data"
// Set a lower memory limit for multipart forms (default is 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// single file
file, _ := c.FormFile("file")
log.Println(file.Filename)
// Upload the file to specific dst.
c.SaveUploadedFile(file, dst)
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
// 上传多个文件示例
// curl -X POST http://localhost:8080/upload \
// -F "upload[]=@/Users/appleboy/test1.zip" \
// -F "upload[]=@/Users/appleboy/test2.zip" \
// -H "Content-Type: multipart/form-data"
// Set a lower memory limit for multipart forms (default is 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm()
files := form.File["upload[]"]
for _, file := range files {
log.Println(file.Filename)
// Upload the file to specific dst.
c.SaveUploadedFile(file, dst)
}
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
})
// 分组路由
// Simple group: v1
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
// Simple group: v2
v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
// 使用中间件
// Creates a router without any middleware by default
r := gin.New() // 默认不带中间件
// Global middleware
// Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.
// By default gin.DefaultWriter = os.Stdout
r.Use(gin.Logger())
// Recovery middleware recovers from any panics and writes a 500 if there was one.
r.Use(gin.Recovery())
// Per route middleware, you can add as many as you desire.
r.GET("/benchmark", MyBenchLogger(), benchEndpoint)
// Authorization group
// authorized := r.Group("/", AuthRequired())
// exactly the same as:
authorized := r.Group("/")
// per group middleware! in this case we use the custom created
// AuthRequired() middleware just in the "authorized" group.
authorized.Use(AuthRequired())
{
authorized.POST("/login", loginEndpoint)
authorized.POST("/submit", submitEndpoint)
authorized.POST("/read", readEndpoint)
// nested group
testing := authorized.Group("testing")
testing.GET("/analytics", analyticsEndpoint)
}
// 自定义恢复
// Creates a router without any middleware by default
rr := gin.New()
// Global middleware
// Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.
// By default gin.DefaultWriter = os.Stdout
rr.Use(gin.Logger())
// Recovery middleware recovers from any panics and writes a 500 if there was one.
rr.Use(gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
if err, ok := recovered.(string); ok {
c.String(http.StatusInternalServerError, fmt.Sprintf("error: %s", err))
}
c.AbortWithStatus(http.StatusInternalServerError)
}))
rr.GET("/panic", func(c *gin.Context) {
// panic with a string -- the custom middleware could save this to a database or report it to the user
panic("foo")
})
rr.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "ohai")
})
// 写日志文件
// Disable Console Color, you don't need console color when writing the logs to file.
gin.DisableConsoleColor()
// Logging to a file.
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
// Use the following code if you need to write the logs to file and console at the same time.
// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
routerr := gin.Default()
routerr.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
// 自定义日志格式
routerrr := gin.New()
// LoggerWithFormatter middleware will write the logs to gin.DefaultWriter
// By default gin.DefaultWriter = os.Stdout
routerrr.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
// your custom format
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,
)
}))
routerrr.Use(gin.Recovery())
routerrr.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
// 输出: ::1 - [Fri, 07 Dec 2018 17:04:38 JST] "GET /ping HTTP/1.1 200 122.767µs "Mozilla/5.0 (Macintosh;
//Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36" "
// 输出日志的颜色
// Disable log's color
//gin.DisableConsoleColor() // 日志不着色
// Force log's color
gin.ForceConsoleColor() // 日志着色
// Creates a gin router with default middleware:
// logger and recovery (crash-free) middleware
routeer := gin.Default()
routeer.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
// 模型绑定和验证
// 方法- Bind, BindJSON, BindXML, BindQuery, BindYAML,BindHeader
// 方法- ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML,ShouldBindHeader
type Login struct {
User string `form:"user" json:"user" xml:"user" binding:"required"`
//Password string `form:"password" json:"password" xml:"password" binding:"required"` // 会报输出错误
Password string `form:"password" json:"password" xml:"password" binding:"-"`
}
// Example for binding JSON ({"user": "manu", "password": "123"})
router.POST("/loginJSON", 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 != "manu" || json.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
})
// Example for binding XML (
// <?xml version="1.0" encoding="UTF-8"?>
// <root>
// <user>manu</user>
// <password>123</password>
// </root>)
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 != "manu" || xml.Password != "123" {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
})
// Example for binding a HTML form (user=manu&password=123)
router.POST("/loginForm", func(c *gin.Context) {
var form Login
// This will infer what binder to use depending on the 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"})
})
// 请求示例
//$ curl -v -X POST \
//http://localhost:8080/loginJSON \
//-H 'content-type: application/json' \
//-d '{ "user": "manu" }'
//> POST /loginJSON HTTP/1.1
//> Host: localhost:8080
//> User-Agent: curl/7.51.0
//> Accept: */*
//> content-type: application/json
//> Content-Length: 18
//>
//* upload completely sent off: 18 out of 18 bytes
//< HTTP/1.1 400 Bad Request
//< Content-Type: application/json; charset=utf-8
//< Date: Fri, 04 Aug 2017 03:51:31 GMT
//< Content-Length: 100
//<
//{"error":"Key: 'Login.Password' Error:Field validation for 'Password' failed on the 'required' tag"}
// 自定义验证器
//package main
//import (
// "net/http"
//"time"
//"github.com/gin-gonic/gin"
//"github.com/gin-gonic/gin/binding"
//"github.com/go-playground/validator/v10"
//)
//
Booking contains binded and validated data.
//type Booking struct {
// CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
// CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
//}
//
//var bookableDate validator.Func = func(fl validator.FieldLevel) bool {
// date, ok := fl.Field().Interface().(time.Time)
// if ok {
// today := time.Now()
// if today.After(date) {
// 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()})
// }
//}
// 测试
//$ curl "localhost:8085/bookable?check_in=2030-04-16&check_out=2030-04-17"
//{"message":"Booking dates are valid!"}
//$ curl "localhost:8085/bookable?check_in=2030-03-10&check_out=2030-03-09"
//{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"}
//$ curl "localhost:8085/bookable?check_in=2000-03-09&check_out=2000-03-10"
//{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}%
// 只绑定查询字符串
//type Person struct {
// Name string `form:"name"`
// Address string `form:"address"`
//}
//func main() {
// route := gin.Default()
// route.Any("/testing", startPage)
// route.Run(":8085")
//}
//func startPage(c *gin.Context) {
// var person Person
// if c.ShouldBindQuery(&person) == nil {
// log.Println("====== Only Bind By Query String ======")
// log.Println(person.Name)
// log.Println(person.Address)
// }
// c.String(200, "Success")
//}
// 绑定查询字符串或发布数据
//type Person struct {
// Name string `form:"name"`
// Address string `form:"address"`
// Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
// CreateTime time.Time `form:"createTime" time_format:"unixNano"`
// UnixTime time.Time `form:"unixTime" time_format:"unix"`
//}
//func main() {
// route := gin.Default()
// route.GET("/testing", startPage)
// route.Run(":8085")
//}
//func startPage(c *gin.Context) {
// var person Person
// // If `GET`, only `Form` binding engine (`query`) used.
// // If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`).
// // See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48
// if c.ShouldBind(&person) == nil {
// log.Println(person.Name)
// log.Println(person.Address)
// log.Println(person.Birthday)
// log.Println(person.CreateTime)
// log.Println(person.UnixTime)
// }
// c.String(200, "Success")
//}
//
// 绑定Uri
//type Person struct {
// ID string `uri:"id" binding:"required,uuid"`
// 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(":8088")
//}
// 测试
//$ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3
//$ curl -v localhost:8088/thinkerou/not-uuid
// 绑定标题
//type testHeader struct {
// Rate int `header:"Rate"`
// Domain string `header:"Domain"`
//}
//func main() {
// r := gin.Default()
// r.GET("/", func(c *gin.Context) {
// h := testHeader{}
//
// if err := c.ShouldBindHeader(&h); err != nil {
// c.JSON(200, err)
// }
// fmt.Printf("%#v\n", h)
// c.JSON(200, gin.H{"Rate": h.Rate, "Domain": h.Domain})
// })
// r.Run()
// // client
// // curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/
// // output
// // {"Domain":"music","Rate":300}
//}
//
// 绑定 HTML 复选框
//...
//type myForm struct {
// Colors []string `form:"colors[]"`
//}
//...
//func formHandler(c *gin.Context) {
// var fakeForm myForm
// c.ShouldBind(&fakeForm)
// c.JSON(200, gin.H{"color": fakeForm.Colors})
//}
//...
// form.html文件内容
//<form action="/" method="POST">
//<p>Check some colors</p>
//<label for="red">Red</label>
//<input type="checkbox" name="colors[]" value="red" id="red">
//<label for="green">Green</label>
//<input type="checkbox" name="colors[]" value="green" id="green">
//<label for="blue">Blue</label>
//<input type="checkbox" name="colors[]" value="blue" id="blue">
//<input type="submit">
//</form>
// 输出结果: {"color":["red","green","blue"]}
// Multipart/Urlencoded 绑定
//type ProfileForm struct {
// Name string `form:"name" binding:"required"`
// Avatar *multipart.FileHeader `form:"avatar" binding:"required"`
//
// // or for multiple files
// // Avatars []*multipart.FileHeader `form:"avatar" binding:"required"`
//}
//func main() {
// router := gin.Default()
// router.POST("/profile", func(c *gin.Context) {
// // you can bind multipart form with explicit binding declaration:
// // c.ShouldBindWith(&form, binding.Form)
// // or you can simply use autobinding with ShouldBind method:
// var form ProfileForm
// // in this case proper binding will be automatically selected
// if err := c.ShouldBind(&form); err != nil {
// c.String(http.StatusBadRequest, "bad request")
// return
// }
// err := c.SaveUploadedFile(form.Avatar, form.Avatar.Filename)
// if err != nil {
// c.String(http.StatusInternalServerError, "unknown error")
// return
// }
// // db.Save(&form)
// c.String(http.StatusOK, "ok")
// })
// router.Run(":8080")
//}
// 测试 $ curl -X POST -v --form name=user --form "avatar=@./avatar.png" http://localhost:8080/profile
// XML、JSON、YAML 和 ProtoBuf 渲染
//func main() {
// r := gin.Default()
//
// // gin.H is a shortcut for 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) {
// // You also can use a struct
// var msg struct {
// Name string `json:"user"`
// Message string
// Number int
// }
// msg.Name = "Lena"
// msg.Message = "hey"
// msg.Number = 123
// // Note that msg.Name becomes "user" in the JSON
// // Will output : {"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"
// // The specific definition of protobuf is written in the testdata/protoexample file.
// data := &protoexample.Test{
// Label: &label,
// Reps: reps,
// }
// // Note that data becomes binary data in the response
// // Will output protoexample.Test protobuf serialized data
// c.ProtoBuf(http.StatusOK, data)
// })
//
// // Listen and serve on 0.0.0.0:8080
// r.Run(":8080")
//}
//
// 安全的json,防止json劫持
//func main() {
// r := gin.Default()
//
// // You can also use your own secure json prefix
// // r.SecureJsonPrefix(")]}',\n")
//
// r.GET("/someJSON", func(c *gin.Context) {
// names := []string{"lena", "austin", "foo"}
//
// // Will output : while(1);["lena","austin","foo"]
// c.SecureJSON(http.StatusOK, names)
// })
//
// // Listen and serve on 0.0.0.0:8080
// r.Run(":8080")
//}
// JSONP
// 使用 JSONP 从不同域中的服务器请求数据。如果存在查询参数回调,则向响应正文添加回调。
//func main() {
// r := gin.Default()
//
// r.GET("/JSONP", func(c *gin.Context) {
// data := gin.H{
// "foo": "bar",
// }
//
// //callback is x
// // Will output : x({\"foo\":\"bar\"})
// c.JSONP(http.StatusOK, data)
// })
//
// // Listen and serve on 0.0.0.0:8080
// r.Run(":8080")
//
// // client
// // curl http://127.0.0.1:8080/JSONP?callback=x
//}
//
// AsciiJSON
// 使用 AsciiJSON 生成带有转义非 ASCII 字符的纯 ASCII JSON。
//func main() {
// r := gin.Default()
//
// r.GET("/someJSON", func(c *gin.Context) {
// data := gin.H{
// "lang": "GO语言",
// "tag": "<br>",
// }
//
// // will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
// c.AsciiJSON(http.StatusOK, data)
// })
//
// // Listen and serve on 0.0.0.0:8080
// r.Run(":8080")
//}
// 纯JSON
// 通常,JSON 用它们的 unicode 实体替换特殊的 HTML 字符,例如<变成 \u003c. 如果您想按字面意思对此类字符进行编码,则可以改用 PureJSON。此功能在 Go 1.6 及更低版本中不可用。
//func main() {
// r := gin.Default()
//
// // Serves unicode entities
// r.GET("/json", func(c *gin.Context) {
// c.JSON(200, gin.H{
// "html": "<b>Hello, world!</b>",
// })
// })
//
// // Serves literal characters
// r.GET("/purejson", func(c *gin.Context) {
// c.PureJSON(200, gin.H{
// "html": "<b>Hello, world!</b>",
// })
// })
//
// // listen and serve on 0.0.0.0:8080
// r.Run(":8080")
//}
// 提供静态文件
//func main() {
// router := gin.Default()
// router.Static("/assets", "./assets")
// router.StaticFS("/more_static", http.Dir("my_file_system"))
// router.StaticFile("/favicon.ico", "./resources/favicon.ico")
//
// // Listen and serve on 0.0.0.0:8080
// router.Run(":8080")
//}
// 从文件提供数据
//func main() {
// router := gin.Default()
//
// router.GET("/local/file", func(c *gin.Context) {
// c.File("local/file.go")
// })
//
// var fs http.FileSystem = // ...
// router.GET("/fs/file", func(c *gin.Context) {
// c.FileFromFS("fs/file.go", fs)
// })
//}
// 从读取器提供数据
//func main() {
// router := gin.Default()
// router.GET("/someDataFromReader", func(c *gin.Context) {
// response, err := http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png")
// if err != nil || response.StatusCode != http.StatusOK {
// c.Status(http.StatusServiceUnavailable)
// return
// }
//
// reader := response.Body
// defer reader.Close()
// contentLength := response.ContentLength
// contentType := response.Header.Get("Content-Type")
//
// extraHeaders := map[string]string{
// "Content-Disposition": `attachment; filename="gopher.png"`,
// }
//
// c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
// })
// router.Run(":8080")
//}
// HTML 渲染
//func main() {
// router := gin.Default()
// router.LoadHTMLGlob("templates/*")
// //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
// router.GET("/index", func(c *gin.Context) {
// c.HTML(http.StatusOK, "index.tmpl", gin.H{
// "title": "Main website",
// })
// })
// router.Run(":8080")
//}
// templates/index.tmpl 文件内容
//<html>
//<h1>
//{{ .title }}
//</h1>
//</html>
// 在不同目录中使用同名模板
//func main() {
// router := gin.Default()
// router.LoadHTMLGlob("templates/**/*")
// router.GET("/posts/index", func(c *gin.Context) {
// c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
// "title": "Posts",
// })
// })
// router.GET("/users/index", func(c *gin.Context) {
// c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
// "title": "Users",
// })
// })
// router.Run(":8080")
//}
// templates/posts/index.tmpl 文件内容
//{{ define "posts/index.tmpl" }}
//<html><h1>
//{{ .title }}
//</h1>
//<p>Using posts/index.tmpl</p>
//</html>
//{{ end }}
// templates/users/index.tmpl 文件内容
//{{ define "users/index.tmpl" }}
//<html><h1>
//{{ .title }}
//</h1>
//<p>Using users/index.tmpl</p>
//</html>
//{{ end }}
// 自定义模板渲染器
//import "html/template"
//func main() {
// router := gin.Default()
// html := template.Must(template.ParseFiles("file1", "file2"))
// router.SetHTMLTemplate(html)
// router.Run(":8080")
//}
// 自定义分隔符
// 您可以使用自定义 delims
//r := gin.Default()
//r.Delims("{[{", "}]}")
//r.LoadHTMLGlob("/path/to/templates")
// 自定义模板函数
//import (
// "fmt"
//"html/template"
//"net/http"
//"time"
//
//"github.com/gin-gonic/gin"
//)
//
//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("./testdata/template/raw.tmpl")
//
// router.GET("/raw", func(c *gin.Context) {
// c.HTML(http.StatusOK, "raw.tmpl", gin.H{
// "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
// })
// })
//
// router.Run(":8080")
//}
// raw.tmpl 文件内容
// 输出结果: Date: 2017/07/01
// 多模板
//Gin 默认只允许使用一个 html.Template。检查多模板渲染以使用 go 1.6 等功能block template。
//重定向
//发出 HTTP 重定向很容易。支持内部和外部位置。
r.GET("/test", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")
})
// 从 POST 发出 HTTP 重定向
r.POST("/test", func(c *gin.Context) {
c.Redirect(http.StatusFound, "/foo")
})
// 发出路由器重定向,使用HandleContext
r.GET("/test", func(c *gin.Context) {
c.Request.URL.Path = "/test2"
r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
c.JSON(200, gin.H{"hello": "world"})
})
// 自定义中间件
//func Logger() gin.HandlerFunc {
// return func(c *gin.Context) {
// t := time.Now()
//
// // Set example variable
// c.Set("example", "12345")
//
// // before request
//
// c.Next()
//
// // after request
// latency := time.Since(t)
// log.Print(latency)
//
// // access the status we are sending
// status := c.Writer.Status()
// log.Println(status)
//}
//}
//
//func main() {
// r := gin.New()
// r.Use(Logger())
//
// r.GET("/test", func(c *gin.Context) {
// example := c.MustGet("example").(string)
//
// // it would print: "12345"
// log.Println(example)
// })
//
// // Listen and serve on 0.0.0.0:8080
// r.Run(":8080")
//}
// 使用 BasicAuth() 中间件
// // simulate some private data
//var secrets = gin.H{
// "foo": gin.H{"email": "foo@bar.com", "phone": "123433"},
// "austin": gin.H{"email": "austin@example.com", "phone": "666"},
// "lena": gin.H{"email": "lena@guapa.com", "phone": "523443"},
//}
//
//func main() {
// r := gin.Default()
//
// // Group using gin.BasicAuth() middleware
// // gin.Accounts is a shortcut for map[string]string
// authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
// "foo": "bar",
// "austin": "1234",
// "lena": "hello2",
// "manu": "4321",
// }))
//
// // /admin/secrets endpoint
// // hit "localhost:8080/admin/secrets
// authorized.GET("/secrets", func(c *gin.Context) {
// // get user, it was set by the BasicAuth middleware
// user := c.MustGet(gin.AuthUserKey).(string)
// 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 :("})
// }
// })
//
// // Listen and serve on 0.0.0.0:8080
// r.Run(":8080")
//}
// 中间件中的 Goroutines
// 在中间件或处理程序中启动新的 Goroutine 时,不应使用其中的原始上下文,而必须使用只读副本。
//func main() {
// r := gin.Default()
//
// r.GET("/long_async", func(c *gin.Context) {
// // create copy to be used inside the goroutine
// cCp := c.Copy()
// go func() {
// // simulate a long task with time.Sleep(). 5 seconds
// time.Sleep(5 * time.Second)
//
// // note that you are using the copied context "cCp", IMPORTANT
// log.Println("Done! in path " + cCp.Request.URL.Path)
// }()
// })
//
// r.GET("/long_sync", func(c *gin.Context) {
// // simulate a long task with time.Sleep(). 5 seconds
// time.Sleep(5 * time.Second)
//
// // since we are NOT using a goroutine, we do not have to copy the context
// log.Println("Done! in path " + c.Request.URL.Path)
// })
//
// // Listen and serve on 0.0.0.0:8080
// r.Run(":8080")
//}
// 自定义 HTTP 配置
// http.ListenAndServe()直接使用
//func main() {
// router := gin.Default()
// http.ListenAndServe(":8080", router)
//}
// 或者
//func main() {
// router := gin.Default()
//
// s := &http.Server{
// Addr: ":8080",
// Handler: router,
// ReadTimeout: 10 * time.Second,
// WriteTimeout: 10 * time.Second,
// MaxHeaderBytes: 1 << 20,
// }
// s.ListenAndServe()
//}
// 加密
// LetsEncrypt HTTPS 服务器的示例
//package main
//
//import (
// "log"
//
//"github.com/gin-gonic/autotls"
//"github.com/gin-gonic/gin"
//)
//
//func main() {
// r := gin.Default()
//
// // Ping handler
// r.GET("/ping", func(c *gin.Context) {
// c.String(200, "pong")
// })
//
// log.Fatal(autotls.Run(r, "example1.com", "example2.com"))
//}
// 自定义自动证书管理器的示例
//package main
//
//import (
// "log"
//
//"github.com/gin-gonic/autotls"
//"github.com/gin-gonic/gin"
//"golang.org/x/crypto/acme/autocert"
//)
//
//func main() {
// r := gin.Default()
//
// // Ping handler
// r.GET("/ping", func(c *gin.Context) {
// c.String(200, "pong")
// })
//
// m := autocert.Manager{
// Prompt: autocert.AcceptTOS,
// HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"),
// Cache: autocert.DirCache("/var/www/.cache"),
// }
//
// log.Fatal(autotls.RunWithManager(r, &m))
//}
// 使用 Gin 运行多个服务
//package main
//
//import (
// "log"
//"net/http"
//"time"
//
//"github.com/gin-gonic/gin"
//"golang.org/x/sync/errgroup"
//)
//
//var (
// g errgroup.Group
//)
//
//func router01() http.Handler {
// e := gin.New()
// e.Use(gin.Recovery())
// e.GET("/", func(c *gin.Context) {
// c.JSON(
// http.StatusOK,
// gin.H{
// "code": http.StatusOK,
// "error": "Welcome server 01",
//},
//)
//})
//
// return e
//}
//
//func router02() http.Handler {
// e := gin.New()
// e.Use(gin.Recovery())
// e.GET("/", func(c *gin.Context) {
// c.JSON(
// http.StatusOK,
// gin.H{
// "code": http.StatusOK,
// "error": "Welcome server 02",
//},
//)
//})
//
// return e
//}
//
//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 {
// err := server01.ListenAndServe()
// if err != nil && err != http.ErrServerClosed {
// log.Fatal(err)
// }
// return err
// })
//
// g.Go(func() error {
// err := server02.ListenAndServe()
// if err != nil && err != http.ErrServerClosed {
// log.Fatal(err)
// }
// return err
// })
//
// if err := g.Wait(); err != nil {
// log.Fatal(err)
// }
//}
// 执行正常关闭或重新启动,使用第三方包
//router := gin.Default()
//router.GET("/", handler)
[...]
//endless.ListenAndServe(":4242", router)
//manners: 优雅地关闭.
//graceful: 正常关闭 http.Handler 服务器
//grace: 优雅重启和零停机部署
// 关闭,Shutdown() 方法
+build go1.8
//
//package main
//
//import (
// "context"
//"log"
//"net/http"
//"os"
//"os/signal"
//"syscall"
//"time"
//
//"github.com/gin-gonic/gin"
//)
//
//func main() {
// router := gin.Default()
// router.GET("/", func(c *gin.Context) {
// time.Sleep(5 * time.Second)
// c.String(http.StatusOK, "Welcome Gin Server")
// })
//
// srv := &http.Server{
// Addr: ":8080",
// Handler: router,
// }
//
// // Initializing the server in a goroutine so that
// // it won't block the graceful shutdown handling below
// go func() {
// if err := srv.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
// log.Printf("listen: %s\n", err)
// }
// }()
//
// // Wait for interrupt signal to gracefully shutdown the server with
// // a timeout of 5 seconds.
// quit := make(chan os.Signal)
// // kill (no param) default send syscall.SIGTERM
// // kill -2 is syscall.SIGINT
// // kill -9 is syscall.SIGKILL but can't be caught, so don't need to add it
// signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
// <-quit
// log.Println("Shutting down server...")
//
// // The context is used to inform the server it has 5 seconds to finish
// // the request it is currently handling
// ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
// defer cancel()
//
// if err := srv.Shutdown(ctx); err != nil {
// log.Fatal("Server forced to shutdown:", err)
// }
//
// log.Println("Server exiting")
//}
//
// 使用模板构建单个二进制文件
// 您可以使用go-assets将服务器构建为包含模板的单个二进制文件
//func main() {
// r := gin.New()
//
// t, err := loadTemplate()
// if err != nil {
// panic(err)
// }
// r.SetHTMLTemplate(t)
//
// r.GET("/", func(c *gin.Context) {
// c.HTML(http.StatusOK, "/html/index.tmpl",nil)
// })
// r.Run(":8080")
//}
//
loadTemplate loads templates embedded by go-assets-builder
//func loadTemplate() (*template.Template, error) {
// t := template.New("")
// for name, file := range Assets.Files {
// defer file.Close()
// if file.IsDir() || !strings.HasSuffix(name, ".tmpl") {
// continue
// }
// h, err := ioutil.ReadAll(file)
// if err != nil {
// return nil, err
// }
// t, err = t.New(name).Parse(string(h))
// if err != nil {
// return nil, err
// }
// }
// return t, nil
//}
// 使用自定义结构绑定表单数据请求
//type StructA struct {
// FieldA string `form:"field_a"`
//}
//
//type StructB struct {
// NestedStruct StructA
// FieldB string `form:"field_b"`
//}
//
//type StructC struct {
// NestedStructPointer *StructA
// FieldC string `form:"field_c"`
//}
//
//type StructD struct {
// NestedAnonyStruct struct {
// FieldX string `form:"field_x"`
// }
// FieldD string `form:"field_d"`
//}
//
//func GetDataB(c *gin.Context) {
// var b StructB
// c.Bind(&b)
// c.JSON(200, gin.H{
// "a": b.NestedStruct,
// "b": b.FieldB,
// })
//}
//
//func GetDataC(c *gin.Context) {
// var b StructC
// c.Bind(&b)
// c.JSON(200, gin.H{
// "a": b.NestedStructPointer,
// "c": b.FieldC,
// })
//}
//
//func GetDataD(c *gin.Context) {
// var b StructD
// c.Bind(&b)
// c.JSON(200, gin.H{
// "x": b.NestedAnonyStruct,
// "d": b.FieldD,
// })
//}
//
//func main() {
// r := gin.Default()
// r.GET("/getb", GetDataB)
// r.GET("/getc", GetDataC)
// r.GET("/getd", GetDataD)
//
// r.Run()
//}
// curl 访问示例
//$ curl "http://localhost:8080/getb?field_a=hello&field_b=world"
//{"a":{"FieldA":"hello"},"b":"world"}
//$ curl "http://localhost:8080/getc?field_a=hello&field_c=world"
//{"a":{"FieldA":"hello"},"c":"world"}
//$ curl "http://localhost:8080/getd?field_x=hello&field_d=world"
//{"d":"world","x":{"FieldX":"hello"}}
// 尝试将 body 绑定到不同的结构中
// 绑定请求体的普通方法会消耗c.Request.Body,不能多次调用。
//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{}
// // This c.ShouldBind consumes c.Request.Body and it cannot be reused.
// if errA := c.ShouldBind(&objA); errA == nil {
// c.String(http.StatusOK, `the body should be formA`)
// // Always an error is occurred by this because c.Request.Body is EOF now.
// } else if errB := c.ShouldBind(&objB); errB == nil {
// c.String(http.StatusOK, `the body should be formB`)
// } else {
// ...
// }
//}
// 使用 c.ShouldBindBodyWith
//func SomeHandler(c *gin.Context) {
// objA := formA{}
// objB := formB{}
// // This reads c.Request.Body and stores the result into the context.
// if errA := c.ShouldBindBodyWith(&objA, binding.Form); errA == nil {
// c.String(http.StatusOK, `the body should be formA`)
// // At this time, it reuses body stored in the context.
// } else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
// c.String(http.StatusOK, `the body should be formB JSON`)
// // And it can accepts other formats
// } else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {
// c.String(http.StatusOK, `the body should be formB XML`)
// } else {
// ...
// }
//}
//c.ShouldBindBodyWith在绑定之前将 body 存储到上下文中。这对性能有轻微影响,因此如果您足以立即调用绑定,则不应使用此方法。
//仅某些格式需要此功能 - JSON、XML、MsgPack、 ProtoBuf。对于其他格式,Query, Form, FormPost, FormMultipart, 可以被c.ShouldBind()多次调用而不会对性能造成任何损害(参见#1341)。
// 使用自定义结构和自定义标记绑定表单数据请求
//const (
// customerTag = "url"
// defaultMemory = 32 << 20
//)
//
//type customerBinding struct {}
//
//func (customerBinding) Name() string {
// return "form"
//}
//
//func (customerBinding) Bind(req *http.Request, obj interface{}) error {
//if err := req.ParseForm(); err != nil {
//return err
//}
//if err := req.ParseMultipartForm(defaultMemory); err != nil {
//if err != http.ErrNotMultipart {
//return err
//}
//}
//if err := binding.MapFormWithTag(obj, req.Form, customerTag); err != nil {
//return err
//}
//return validate(obj)
//}
//
//func validate(obj interface{}) error {
//if binding.Validator == nil {
//return nil
//}
//return binding.Validator.ValidateStruct(obj)
//}
//
Now we can do this!!!
FormA is a external type that we can't modify it's tag
//type FormA struct {
//FieldA string `url:"field_a"`
//}
//
//func ListHandler(s *Service) func(ctx *gin.Context) {
//return func(ctx *gin.Context) {
//var urlBinding = customerBinding{}
//var opt FormA
//err := ctx.MustBindWith(&opt, urlBinding)
//if err != nil {
//...
//}
//...
//}
//}
// http2 服务器推送
//package main
//
//import (
// "html/template"
//"log"
//
//"github.com/gin-gonic/gin"
//)
//
//var html = template.Must(template.New("https").Parse(`
// <html>
//<head>
// <title>Https Test</title>
// <script src="/assets/app.js"></script>
//</head>
//<body>
// <h1 style="color:red;">Welcome, Ginner!</h1>
//</body>
//</html>
//`))
//
//func main() {
// r := gin.Default()
// r.Static("/assets", "./assets")
// r.SetHTMLTemplate(html)
//
// r.GET("/", func(c *gin.Context) {
// if pusher := c.Writer.Pusher(); pusher != nil {
// // use pusher.Push() to do server push
// if err := pusher.Push("/assets/app.js", nil); err != nil {
// log.Printf("Failed to push: %v", err)
// }
// }
// c.HTML(200, "https", gin.H{
// "status": "success",
// })
// })
//
// // Listen and Server in https://127.0.0.1:8080
// r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key")
//}
// 使用标准日志包记录所有路由
//import (
// "log"
//"net/http"
//
//"github.com/gin-gonic/gin"
//)
//
//func main() {
// r := gin.Default()
// gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
// log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)
// }
//
// r.POST("/foo", func(c *gin.Context) {
// c.JSON(http.StatusOK, "foo")
// })
//
// r.GET("/bar", func(c *gin.Context) {
// c.JSON(http.StatusOK, "bar")
// })
//
// r.GET("/status", func(c *gin.Context) {
// c.JSON(http.StatusOK, "ok")
// })
//
// // Listen and Server in http://0.0.0.0:8080
// r.Run()
//}
// 设置并获取 cookie
//import (
// "fmt"
//"github.com/gin-gonic/gin"
//)
//func main() {
// router := gin.Default()
// router.GET("/cookie", func(c *gin.Context) {
// cookie, err := c.Cookie("gin_cookie")
// if err != nil {
// cookie = "NotSet"
// c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
// }
// fmt.Printf("Cookie value: %s \n", cookie)
// })
// router.Run()
//}
// 信任代理设置
//import (
// "fmt"
//
//"github.com/gin-gonic/gin"
//)
//
//func main() {
//
// router := gin.Default()
// router.SetTrustedProxies([]string{"192.168.1.2"})
//
// router.GET("/", func(c *gin.Context) {
// // If the client is 192.168.1.2, use the X-Forwarded-For
// // header to deduce the original client IP from the trust-
// // worthy parts of that header.
// // Otherwise, simply return the direct client IP
// fmt.Printf("ClientIP: %s\n", c.ClientIP())
// })
// router.Run()
//}
// 使用的是 CDN 服务,您可以设置Engine.TrustedPlatform 跳过 TrustedProxies 检查,它的优先级高于 TrustedProxies
//import (
// "fmt"
//
//"github.com/gin-gonic/gin"
//)
//
//func main() {
//
// router := gin.Default()
// // Use predefined header gin.PlatformXXX
// router.TrustedPlatform = gin.PlatformGoogleAppEngine
// // Or set your own trusted request header for another trusted proxy service
// // Don't set it to any suspect request header, it's unsafe
// router.TrustedPlatform = "X-CDN-IP"
//
// router.GET("/", func(c *gin.Context) {
// // If you set TrustedPlatform, ClientIP() will resolve the
// // corresponding header and return IP directly
// fmt.Printf("ClientIP: %s\n", c.ClientIP())
// })
// router.Run()
//}
// 测试
//该net/http/httptest包是 HTTP 测试的首选方式。
//package main
//
//func setupRouter() *gin.Engine {
// r := gin.Default()
// r.GET("/ping", func(c *gin.Context) {
// c.String(200, "pong")
//})
// return r
//}
//
//func main() {
// r := setupRouter()
// r.Run(":8080")
//}
// 测试上面的代码
//package main
//
//import (
// "net/http"
//"net/http/httptest"
//"testing"
//
//"github.com/stretchr/testify/assert"
//)
//
//func TestPingRoute(t *testing.T) {
// router := setupRouter()
//
// w := httptest.NewRecorder()
// req, _ := http.NewRequest("GET", "/ping", nil)
// router.ServeHTTP(w, req)
//
// assert.Equal(t, 200, w.Code)
// assert.Equal(t, "pong", w.Body.String())
//}
router.Run(":8080")
}
go第三库Gin使用示例
最新推荐文章于 2024-05-08 09:35:08 发布