gin icon indicating copy to clipboard operation
gin copied to clipboard

README.md error errA := c.ShouldBindBodyWith(&objA, binding.Form)

Open ItisDL opened this issue 1 year ago • 2 comments

  • With issues: type formA struct { Foo string json:"foo" xml:"foo" binding:"required" } objA := formA{} c.ShouldBindBodyWith(&objA, binding.Form) cant use binding.Form should use binding.JSON maybe

Description

How to reproduce

package main

import (
	"context"
	"errors"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
)

func Logger() gin.HandlerFunc {
	return func(c *gin.Context) {
		t := time.Now()

		c.Set("example", "12345")

		// before request
		c.Next()
		// after request
		latency := time.Since(t)
		log.Println(latency)
	}
}

func main() {
	r := gin.Default()

	// 中间件测试
	r.Use(Logger())

	r.StaticFS("/assets", http.Dir("assets"))
	r.GET("/local/file", func(c *gin.Context) {
		c.File("main.go")
	})
	r.POST("/test", func(c *gin.Context) {
		example := c.MustGet("example").(string)
		log.Println(example)

		type formA struct {
			Foo string `json:"foo" xml:"foo" binding:"required"`
		}

		type formB struct {
			Bar string `json:"bar" xml:"bar" binding:"required"`
		}
		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`)
		  }

		// c.Redirect(http.StatusFound, "/someDataFromReader")
	})
	r.GET("/long_async", func(c *gin.Context) {
		cCp := c.Copy()
		go func() {
			time.Sleep(5 * time.Second)
			log.Println("Done! in path " + cCp.Request.URL.Path)
		}()
	})
	r.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)
	})
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	// gin.H is a shortcut for map[string]interface{}
	r.GET("/someJSON", func(c *gin.Context) {
		names := []string{"lena", "austin", "foo"}

		// Will output  :   while(1);["lena","austin","foo"]
		c.SecureJSON(http.StatusOK, names)
	})

	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})
	})

	// 优雅启动或者关闭
	srv := &http.Server{
		Addr:           ":8080",
		Handler:        r,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20, // 2 的 20次方
	}

	// 协程启动服务
	go func() {
		if err := srv.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
			log.Printf("listen:%s\n", err)
		}
	}()

	quit := make(chan os.Signal)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	log.Println("Shutting down server...")

	// 延迟关闭
	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")
}

Expectations

cannot use binding.Form (variable of type binding.formBinding) as binding.BindingBody value in argument to c.ShouldBindBodyWith: binding.formBinding does not implement binding.BindingBody (missing method BindBody)

Actual result

```

Environment

  • go version:1.19
  • gin version (or commit ref):1.8.1
  • operating system:windows

ItisDL avatar Aug 25 '22 08:08 ItisDL

looks like it is a kind of typo in README. obviously binding.Form doesn't have BindBody method and it doesn't implement BindingBody interface.

c.ShouldBindBodyWith(&objA, binding.Form)

Gasoid avatar Sep 05 '22 09:09 Gasoid

My PR: https://github.com/gin-gonic/gin/pull/3312 however I think it is possible to update README and delete this feature for binding.Form

Gasoid avatar Sep 05 '22 13:09 Gasoid