cookie
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func CookieTool() gin.HandlerFunc {
return func(c *gin.Context) {
// Get cookie
if cookie, err := c.Cookie("label"); err == nil {
if cookie == "ok" {
c.Next()
return
}
}
// Cookie verification failed
c.JSON(http.StatusForbidden, gin.H{"error": "Forbidden with no cookie"})
c.Abort()
}
}
func main() {
route := gin.Default()
route.GET("/login", func(c *gin.Context) {
// Set cookie {"label": "ok" }, maxAge 30 seconds.
//func (c *Context) SetCookie(name string, value string, maxAge int, path string, domain string, secure bool, httpOnly bool
c.SetCookie("label", "ok", 30, "/", "localhost", false, true)
c.String(200, "Login success!")
})
route.GET("/home", CookieTool(), func(c *gin.Context) {
c.JSON(200, gin.H{"data": "Your home page"})
})
route.Run(":8080")
}
需要注意的是Cookie里的domain是严格匹配的:so 127.0.0.1
不同于 localhost
, 如果domain设置的是localhost
,那么你用127.0.0.1:8080/admin
访问仍是Forbidden with no cookie
custom-validation
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" json:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
CheckOut time.Time `form:"check_out" json:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
}
//define tag
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()
//register
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("bookabledate", bookableDate)
}
route.POST("/bookable", getBookable)
route.Run(":8080")
}
func getBookable(c *gin.Context) {
var b Booking
if err := c.ShouldBind(&b); err == nil {
c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}
这时你可能会一脸懵逼,time解析失败 -- 原因是我们需要传入UTC time
favicon
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
app := gin.Default()
// serve static favicon file from a location relative to main.go directory
//app.StaticFile("/favicon.ico", "./.assets/favicon.ico")
app.StaticFile("/favicon.ico", "./favicon.ico")
app.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "Hello favicon.")
})
app.Run(":8080")
}
file binding
package main
import (
"fmt"
"mime/multipart"
"net/http"
"path/filepath"
"github.com/gin-gonic/gin"
)
type BindFile struct {
Name string `form:"name" binding:"required"`
Email string `form:"email" binding:"required"`
File *multipart.FileHeader `form:"file" binding:"required"`
}
func main() {
router := gin.Default()
// Set a lower memory limit for multipart forms (default is 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.Static("/", "./public")
router.POST("/upload", func(c *gin.Context) {
var bindFile BindFile
// Bind file
if err := c.ShouldBind(&bindFile); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("err: %s", err.Error()))
return
}
// Save uploaded file
file := bindFile.File
fmt.Println("file name:", file.Filename)
fmt.Println("file size:", file.Size) //kb
dst := filepath.Base(file.Filename)
if err := c.SaveUploadedFile(file, dst); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
return
}
c.String(http.StatusOK, fmt.Sprintf("File %s uploaded successfully with fields name=%s and email=%s.", file.Filename, bindFile.Name, bindFile.Email))
})
router.Run(":8080")
}
将index.html放入public目录
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>File binding</title>
</head>
<body>
<h1>Bind file with fields</h1>
<form action="/upload" method="post" enctype="multipart/form-data">
Name: <input type="text" name="name"><br>
Email: <input type="email" name="email"><br>
File: <input type="file" name="file"><br><br>
<input type="submit" value="Submit">
</form>
</body>
然后浏览器访问 http://127.0.0.1:8080/
submit之后:
可以看到 pdf已成功上传
再来看看文件大小对不对?
OK
我们再用postman测试一下
都是没问题的
graceful shutdown
a, notify with context
// build go1.16
package main
import (
"context"
"log"
"net/http"
"os/signal"
"syscall"
"time"
"github.com/gin-gonic/gin"
)
func main() {
// Create context that listens for the interrupt signal from the OS.
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
router := gin.Default()
router.GET("/", func(c *gin.Context) {
time.Sleep(10 * 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 && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// Listen for the interrupt signal.
<-ctx.Done()
// Restore default behavior on the interrupt signal and notify user of shutdown.
stop()
log.Println("shutting down gracefully, press Ctrl+C again to force")
// 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")
}
b, notify without context
//go:build go1.8
// +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 && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// Wait for interrupt signal to gracefully shutdown the server with
// a timeout of 5 seconds.
quit := make(chan os.Signal, 1)
// kill (no param) default send syscall.SIGTERM
// kill -2 is syscall.SIGINT
// kill -9 is syscall.SIGKILL but can't be catch, so don't need 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")
}
group routes
package routes
import (
"github.com/gin-gonic/gin"
"net/http"
)
var router = gin.Default()
// Run will start the server
func Run() {
getRoutes()
router.Run(":5000")
}
// getRoutes will create our routes of our entire application
// this way every group of routes can be defined in their own file
// so this one won't be so messy
func getRoutes() {
v1 := router.Group("/v1")
addUserRoutes(v1)
addPingRoutes(v1)
v2 := router.Group("/v2")
addPingRoutes(v2)
}
func addUserRoutes(rg *gin.RouterGroup) {
users := rg.Group("/users")
users.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, "users")
})
users.GET("/comments", func(c *gin.Context) {
c.JSON(http.StatusOK, "users comments")
})
users.GET("/pictures", func(c *gin.Context) {
c.JSON(http.StatusOK, "users pictures")
})
}
func addPingRoutes(rg *gin.RouterGroup) {
ping := rg.Group("/ping")
ping.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, "pong")
})
}
mutiple service
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 {
return server01.ListenAndServe()
})
g.Go(func() error {
return server02.ListenAndServe()
})
if err := g.Wait(); err != nil {
log.Fatal(err)
}
}