旺仔小糖

gin http demo 2 years ago

go
gin
9796个字符
共有137人围观
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)
  }
}