Repository: gin-gonic/gin
Pull request #3987 (2024-06-08 08:07:15)

fix: prevent middleware re-entry
prevent middleware re-entry when handling context multiple times
Should fix https://github.com/gin-gonic/gin/pull/3641


Patch:
diff --git a/.github/workflows/gin.yml b/.github/workflows/gin.yml
index 947abf9..1062252 100644
--- a/.github/workflows/gin.yml
+++ b/.github/workflows/gin.yml
@@ -26,16 +26,22 @@ jobs:
       - name: Setup golangci-lint
         uses: golangci/golangci-lint-action@v6
         with:
-          version: v1.58.1
+          version: v1.61.0
           args: --verbose
   test:
     needs: lint
     strategy:
       matrix:
         os: [ubuntu-latest, macos-latest]
-        go: ["1.21", "1.22"]
+        go: ["1.23", "1.24"]
         test-tags:
-          ["", "-tags nomsgpack", '-tags "sonic avx"', "-tags go_json", "-race"]
+          [
+            "",
+            "-tags nomsgpack",
+            '--ldflags="-checklinkname=0" -tags "sonic avx"',
+            "-tags go_json",
+            "-race",
+          ]
         include:
           - os: ubuntu-latest
             go-build: ~/.cache/go-build
@@ -75,7 +81,3 @@ jobs:
         uses: codecov/codecov-action@v4
         with:
           flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }}
-
-      - name: Format
-        if: matrix.go-version == '1.22.x'
-        run: diff -u <(echo -n) <(gofmt -d .)
diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml
index 8ae1182..22edf45 100644
--- a/.github/workflows/goreleaser.yml
+++ b/.github/workflows/goreleaser.yml
@@ -21,7 +21,7 @@ jobs:
         with:
           go-version: "^1"
       - name: Run GoReleaser
-        uses: goreleaser/goreleaser-action@v5
+        uses: goreleaser/goreleaser-action@v6
         with:
           # either 'goreleaser' (default) or 'goreleaser-pro'
           distribution: goreleaser
diff --git a/.golangci.yml b/.golangci.yml
index 5a65972..925e130 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -7,7 +7,7 @@ linters:
     - durationcheck
     - errcheck
     - errorlint
-    - exportloopref
+    - copyloopvar
     - gci
     - gofmt
     - goimports
@@ -16,7 +16,10 @@ linters:
     - nakedret
     - nilerr
     - nolintlint
+    - perfsprint
     - revive
+    - testifylint
+    - usestdlibvars
     - wastedassign
 
 linters-settings:
@@ -33,6 +36,14 @@ linters-settings:
       - G112
       - G201
       - G203
+  perfsprint:
+    err-error: true
+    errorf: true
+    int-conversion: true
+    sprintf1: true
+    strconcat: true
+  testifylint:
+    enable-all: true
 
 issues:
   exclude-rules:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index de47c75..5648902 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -488,7 +488,7 @@
 - [FIX] Refactor render
 - [FIX] Reworked tests
 - [FIX] logger now supports cygwin
-- [FIX] Use X-Forwarded-For before X-Real-Ip
+- [FIX] Use X-Forwarded-For before X-Real-IP
 - [FIX] time.Time binding (#904)
 
 ## Gin 1.1.4
diff --git a/README.md b/README.md
index faeb495..fe5722b 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ If you need performance and good productivity, you will love Gin.
 
 ### Prerequisites
 
-Gin requires [Go](https://go.dev/) version [1.21](https://go.dev/doc/devel/release#go1.21.0) or above.
+Gin requires [Go](https://go.dev/) version [1.23](https://go.dev/doc/devel/release#go1.23.0) or above.
 
 ### Getting Gin
 
@@ -73,7 +73,7 @@ func main() {
 To run the code, use the `go run` command, like:
 
 ```sh
-$ go run example.go
+go run example.go
 ```
 
 Then visit [`0.0.0.0:8080/ping`](http://0.0.0.0:8080/ping) in your browser to see the response!
@@ -102,6 +102,7 @@ The documentation is also available on [gin-gonic.com](https://gin-gonic.com) in
 - [한국어](https://gin-gonic.com/ko-kr/docs/)
 - [Turkish](https://gin-gonic.com/tr/docs/)
 - [Persian](https://gin-gonic.com/fa/docs/)
+- [Português](https://gin-gonic.com/pt/docs/)
 
 ### Articles
 
@@ -112,7 +113,7 @@ The documentation is also available on [gin-gonic.com](https://gin-gonic.com) in
 Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter), [see all benchmarks](/BENCHMARKS.md).
 
 | Benchmark name                 |       (1) |             (2) |          (3) |             (4) |
-| ------------------------------ | ---------:| ---------------:| ------------:| ---------------:|
+| ------------------------------ | --------: | --------------: | -----------: | --------------: |
 | BenchmarkGin_GithubAll         | **43550** | **27364 ns/op** |   **0 B/op** | **0 allocs/op** |
 | BenchmarkAce_GithubAll         |     40543 |     29670 ns/op |       0 B/op |     0 allocs/op |
 | BenchmarkAero_GithubAll        |     57632 |     20648 ns/op |       0 B/op |     0 allocs/op |
diff --git a/binding/form_mapping.go b/binding/form_mapping.go
index 33389b2..235692d 100644
--- a/binding/form_mapping.go
+++ b/binding/form_mapping.go
@@ -159,6 +159,14 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter
 		if k, v := head(opt, "="); k == "default" {
 			setOpt.isDefaultExists = true
 			setOpt.defaultValue = v
+
+			// convert semicolon-separated default values to csv-separated values for processing in setByForm
+			if field.Type.Kind() == reflect.Slice || field.Type.Kind() == reflect.Array {
+				cfTag := field.Tag.Get("collection_format")
+				if cfTag == "" || cfTag == "multi" || cfTag == "csv" {
+					setOpt.defaultValue = strings.ReplaceAll(v, ";", ",")
+				}
+			}
 		}
 	}
 
@@ -182,6 +190,38 @@ func trySetCustom(val string, value reflect.Value) (isSet bool, err error) {
 	return false, nil
 }
 
+func trySplit(vs []string, field reflect.StructField) (newVs []string, err error) {
+	cfTag := field.Tag.Get("collection_format")
+	if cfTag == "" || cfTag == "multi" {
+		return vs, nil
+	}
+
+	var sep string
+	switch cfTag {
+	case "csv":
+		sep = ","
+	case "ssv":
+		sep = " "
+	case "tsv":
+		sep = "\t"
+	case "pipes":
+		sep = "|"
+	default:
+		return vs, fmt.Errorf("%s is not supported in the collection_format. (csv, ssv, pipes)", cfTag)
+	}
+
+	totalLength := 0
+	for _, v := range vs {
+		totalLength += strings.Count(v, sep) + 1
+	}
+	newVs = make([]string, 0, totalLength)
+	for _, v := range vs {
+		newVs = append(newVs, strings.Split(v, sep)...)
+	}
+
+	return newVs, nil
+}
+
 func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSet bool, err error) {
 	vs, ok := form[tagValue]
 	if !ok && !opt.isDefaultExists {
@@ -192,22 +232,42 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][
 	case reflect.Slice:
 		if !ok {
 			vs = []string{opt.defaultValue}
+
+			// pre-process the default value for multi if present
+			cfTag := field.Tag.Get("collection_format")
+			if cfTag == "" || cfTag == "multi" {
+				vs = strings.Split(opt.defaultValue, ",")
+			}
 		}
 
 		if ok, err = trySetCustom(vs[0], value); ok {
 			return ok, err
 		}
 
+		if vs, err = trySplit(vs, field); err != nil {
+			return false, err
+		}
+
 		return true, setSlice(vs, value, field)
 	case reflect.Array:
 		if !ok {
 			vs = []string{opt.defaultValue}
+
+			// pre-process the default value for multi if present
+			cfTag := field.Tag.Get("collection_format")
+			if cfTag == "" || cfTag == "multi" {
+				vs = strings.Split(opt.defaultValue, ",")
+			}
 		}
 
 		if ok, err = trySetCustom(vs[0], value); ok {
 			return ok, err
 		}
 
+		if vs, err = trySplit(vs, field); err != nil {
+			return false, err
+		}
+
 		if len(vs) != value.Len() {
 			return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
 		}
@@ -221,6 +281,9 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][
 
 		if len(vs) > 0 {
 			val = vs[0]
+			if val == "" {
+				val = opt.defaultValue
+			}
 		}
 		if ok, err := trySetCustom(val, value); ok {
 			return ok, err
@@ -335,18 +398,24 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val
 	}
 
 	switch tf := strings.ToLower(timeFormat); tf {
-	case "unix", "unixnano":
+	case "unix", "unixmilli", "unixmicro", "unixnano":
 		tv, err := strconv.ParseInt(val, 10, 64)
 		if err != nil {
 			return err
 		}
 
-		d := time.Duration(1)
-		if tf == "unixnano" {
-			d = time.Second
+		var t time.Time
+		switch tf {
+		case "unix":
+			t = time.Unix(tv, 0)
+		case "unixmilli":
+			t = time.UnixMilli(tv)
+		case "unixmicro":
+			t = time.UnixMicro(tv)
+		default:
+			t = time.Unix(0, tv)
 		}
 
-		t := time.Unix(tv/int64(d), tv%int64(d))
 		value.Set(reflect.ValueOf(t))
 		return nil
 	}
diff --git a/binding/toml.go b/binding/toml.go
index a66b93a..2681231 100644
--- a/binding/toml.go
+++ b/binding/toml.go
@@ -31,5 +31,5 @@ func decodeToml(r io.Reader, obj any) error {
 	if err := decoder.Decode(obj); err != nil {
 		return err
 	}
-	return decoder.Decode(obj)
+	return validate(obj)
 }
diff --git a/context.go b/context.go
index baa4b0f..1c76c0f 100644
--- a/context.go
+++ b/context.go
@@ -7,6 +7,7 @@ package gin
 import (
 	"errors"
 	"io"
+	"io/fs"
 	"log"
 	"math"
 	"mime/multipart"
@@ -186,10 +187,9 @@ func (c *Context) FullPath() string {
 func (c *Context) Next() {
 	c.index++
 	for c.index < int8(len(c.handlers)) {
-		if c.handlers[c.index] == nil {
-			continue
+		if c.handlers[c.index] != nil {
+			c.handlers[c.index](c)
 		}
-		c.handlers[c.index](c)
 		c.index++
 	}
 }
@@ -291,108 +291,171 @@ func (c *Context) MustGet(key string) any {
 	panic("Key \"" + key + "\" does not exist")
 }
 
-// GetString returns the value associated with the key as a string.
-func (c *Context) GetString(key string) (s string) {
+func getTyped[T any](c *Context, key string) (res T) {
 	if val, ok := c.Get(key); ok && val != nil {
-		s, _ = val.(string)
+		res, _ = val.(T)
 	}
 	return
 }
 
+// GetString returns the value associated with the key as a string.
+func (c *Context) GetString(key string) (s string) {
+	return getTyped[string](c, key)
+}
+
 // GetBool returns the value associated with the key as a boolean.
 func (c *Context) GetBool(key string) (b bool) {
-	if val, ok := c.Get(key); ok && val != nil {
-		b, _ = val.(bool)
-	}
-	return
+	return getTyped[bool](c, key)
 }
 
 // GetInt returns the value associated with the key as an integer.
 func (c *Context) GetInt(key string) (i int) {
-	if val, ok := c.Get(key); ok && val != nil {
-		i, _ = val.(int)
-	}
-	return
+	return getTyped[int](c, key)
+}
+
+// GetInt8 returns the value associated with the key as an integer 8.
+func (c *Context) GetInt8(key string) (i8 int8) {
+	return getTyped[int8](c, key)
+}
+
+// GetInt16 returns the value associated with the key as an integer 16.
+func (c *Context) GetInt16(key string) (i16 int16) {
+	return getTyped[int16](c, key)
 }
 
-// GetInt64 returns the value associated with the key as an integer.
+// GetInt32 returns the value associated with the key as an integer 32.
+func (c *Context) GetInt32(key string) (i32 int32) {
+	return getTyped[int32](c, key)
+}
+
+// GetInt64 returns the value associated with the key as an integer 64.
 func (c *Context) GetInt64(key string) (i64 int64) {
-	if val, ok := c.Get(key); ok && val != nil {
-		i64, _ = val.(int64)
-	}
-	return
+	return getTyped[int64](c, key)
 }
 
 // GetUint returns the value associated with the key as an unsigned integer.
 func (c *Context) GetUint(key string) (ui uint) {
-	if val, ok := c.Get(key); ok && val != nil {
-		ui, _ = val.(uint)
-	}
-	return
+	return getTyped[uint](c, key)
 }
 
-// GetUint64 returns the value associated with the key as an unsigned integer.
+// GetUint8 returns the value associated with the key as an unsigned integer 8.
+func (c *Context) GetUint8(key string) (ui8 uint8) {
+	return getTyped[uint8](c, key)
+}
+
+// GetUint16 returns the value associated with the key as an unsigned integer 16.
+func (c *Context) GetUint16(key string) (ui16 uint16) {
+	return getTyped[uint16](c, key)
+}
+
+// GetUint32 returns the value associated with the key as an unsigned integer 32.
+func (c *Context) GetUint32(key string) (ui32 uint32) {
+	return getTyped[uint32](c, key)
+}
+
+// GetUint64 returns the value associated with the key as an unsigned integer 64.
 func (c *Context) GetUint64(key string) (ui64 uint64) {
-	if val, ok := c.Get(key); ok && val != nil {
-		ui64, _ = val.(uint64)
-	}
-	return
+	return getTyped[uint64](c, key)
+}
+
+// GetFloat32 returns the value associated with the key as a float32.
+func (c *Context) GetFloat32(key string) (f32 float32) {
+	return getTyped[float32](c, key)
 }
 
 // GetFloat64 returns the value associated with the key as a float64.
 func (c *Context) GetFloat64(key string) (f64 float64) {
-	if val, ok := c.Get(key); ok && val != nil {
-		f64, _ = val.(float64)
-	}
-	return
+	return getTyped[float64](c, key)
 }
 
 // GetTime returns the value associated with the key as time.
 func (c *Context) GetTime(key string) (t time.Time) {
-	if val, ok := c.Get(key); ok && val != nil {
-		t, _ = val.(time.Time)
-	}
-	return
+	return getTyped[time.Time](c, key)
 }
 
 // GetDuration returns the value associated with the key as a duration.
 func (c *Context) GetDuration(key string) (d time.Duration) {
-	if val, ok := c.Get(key); ok && val != nil {
-		d, _ = val.(time.Duration)
-	}
-	return
+	return getTyped[time.Duration](c, key)
+}
+
+// GetIntSlice returns the value associated with the key as a slice of integers.
+func (c *Context) GetIntSlice(key string) (is []int) {
+	return getTyped[[]int](c, key)
+}
+
+// GetInt8Slice returns the value associated with the key as a slice of int8 integers.
+func (c *Context) GetInt8Slice(key string) (i8s []int8) {
+	return getTyped[[]int8](c, key)
+}
+
+// GetInt16Slice returns the value associated with the key as a slice of int16 integers.
+func (c *Context) GetInt16Slice(key string) (i16s []int16) {
+	return getTyped[[]int16](c, key)
+}
+
+// GetInt32Slice returns the value associated with the key as a slice of int32 integers.
+func (c *Context) GetInt32Slice(key string) (i32s []int32) {
+	return getTyped[[]int32](c, key)
+}
+
+// GetInt64Slice returns the value associated with the key as a slice of int64 integers.
+func (c *Context) GetInt64Slice(key string) (i64s []int64) {
+	return getTyped[[]int64](c, key)
+}
+
+// GetUintSlice returns the value associated with the key as a slice of unsigned integers.
+func (c *Context) GetUintSlice(key string) (uis []uint) {
+	return getTyped[[]uint](c, key)
+}
+
+// GetUint8Slice returns the value associated with the key as a slice of uint8 integers.
+func (c *Context) GetUint8Slice(key string) (ui8s []uint8) {
+	return getTyped[[]uint8](c, key)
+}
+
+// GetUint16Slice returns the value associated with the key as a slice of uint16 integers.
+func (c *Context) GetUint16Slice(key string) (ui16s []uint16) {
+	return getTyped[[]uint16](c, key)
+}
+
+// GetUint32Slice returns the value associated with the key as a slice of uint32 integers.
+func (c *Context) GetUint32Slice(key string) (ui32s []uint32) {
+	return getTyped[[]uint32](c, key)
+}
+
+// GetUint64Slice returns the value associated with the key as a slice of uint64 integers.
+func (c *Context) GetUint64Slice(key string) (ui64s []uint64) {
+	return getTyped[[]uint64](c, key)
+}
+
+// GetFloat32Slice returns the value associated with the key as a slice of float32 numbers.
+func (c *Context) GetFloat32Slice(key string) (f32s []float32) {
+	return getTyped[[]float32](c, key)
+}
+
+// GetFloat64Slice returns the value associated with the key as a slice of float64 numbers.
+func (c *Context) GetFloat64Slice(key string) (f64s []float64) {
+	return getTyped[[]float64](c, key)
 }
 
 // GetStringSlice returns the value associated with the key as a slice of strings.
 func (c *Context) GetStringSlice(key string) (ss []string) {
-	if val, ok := c.Get(key); ok && val != nil {
-		ss, _ = val.([]string)
-	}
-	return
+	return getTyped[[]string](c, key)
 }
 
 // GetStringMap returns the value associated with the key as a map of interfaces.
 func (c *Context) GetStringMap(key string) (sm map[string]any) {
-	if val, ok := c.Get(key); ok && val != nil {
-		sm, _ = val.(map[string]any)
-	}
-	return
+	return getTyped[map[string]any](c, key)
 }
 
 // GetStringMapString returns the value associated with the key as a map of strings.
 func (c *Context) GetStringMapString(key string) (sms map[string]string) {
-	if val, ok := c.Get(key); ok && val != nil {
-		sms, _ = val.(map[string]string)
-	}
-	return
+	return getTyped[map[string]string](c, key)
 }
 
 // GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
 func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) {
-	if val, ok := c.Get(key); ok && val != nil {
-		smss, _ = val.(map[string][]string)
-	}
-	return
+	return getTyped[map[string][]string](c, key)
 }
 
 /************************************/
@@ -614,14 +677,22 @@ func (c *Context) MultipartForm() (*multipart.Form, error) {
 }
 
 // SaveUploadedFile uploads the form file to specific dst.
-func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error {
+func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string, perm ...fs.FileMode) error {
 	src, err := file.Open()
 	if err != nil {
 		return err
 	}
 	defer src.Close()
 
-	if err = os.MkdirAll(filepath.Dir(dst), 0o750); err != nil {
+	var mode os.FileMode = 0o750
+	if len(perm) > 0 {
+		mode = perm[0]
+	}
+	dir := filepath.Dir(dst)
+	if err = os.MkdirAll(dir, mode); err != nil {
+		return err
+	}
+	if err = os.Chmod(dir, mode); err != nil {
 		return err
 	}
 
@@ -811,14 +882,14 @@ func (c *Context) ShouldBindBodyWithTOML(obj any) error {
 	return c.ShouldBindBodyWith(obj, binding.TOML)
 }
 
-// ShouldBindBodyWithJSON is a shortcut for c.ShouldBindBodyWith(obj, binding.JSON).
+// ShouldBindBodyWithPlain is a shortcut for c.ShouldBindBodyWith(obj, binding.Plain).
 func (c *Context) ShouldBindBodyWithPlain(obj any) error {
 	return c.ShouldBindBodyWith(obj, binding.Plain)
 }
 
 // ClientIP implements one best effort algorithm to return the real client IP.
 // It calls c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not.
-// If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]).
+// If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-IP]).
 // If the headers are not syntactically valid OR the remote IP does not correspond to a trusted proxy,
 // the remote IP (coming from Request.RemoteAddr) is returned.
 func (c *Context) ClientIP() string {
diff --git a/debug.go b/debug.go
index 62085c5..f201616 100644
--- a/debug.go
+++ b/debug.go
@@ -78,7 +78,7 @@ func getMinVer(v string) (uint64, error) {
 
 func debugPrintWARNINGDefault() {
 	if v, e := getMinVer(runtime.Version()); e == nil && v < ginSupportMinGoVer {
-		debugPrint(`[WARNING] Now Gin requires Go 1.21+.
+		debugPrint(`[WARNING] Now Gin requires Go 1.23+.
 
 `)
 	}
diff --git a/docs/doc.md b/docs/doc.md
index 177c447..bea417b 100644
--- a/docs/doc.md
+++ b/docs/doc.md
@@ -26,6 +26,8 @@
   - [Custom Validators](#custom-validators)
   - [Only Bind Query String](#only-bind-query-string)
   - [Bind Query String or Post Data](#bind-query-string-or-post-data)
+  - [Bind default value if none provided](#bind-default-value-if-none-provided)
+  - [Collection format for arrays](#collection-format-for-arrays)
   - [Bind Uri](#bind-uri)
   - [Bind custom unmarshaler](#bind-custom-unmarshaler)
   - [Bind Header](#bind-header)
@@ -170,7 +172,7 @@ func main() {
   router := gin.Default()
 
   // Query string parameters are parsed using the existing underlying request object.
-  // The request responds to an url matching:  /welcome?firstname=Jane&lastname=Doe
+  // The request responds to an 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")
@@ -338,16 +340,16 @@ func main() {
   router := gin.Default()
 
   // Simple group: v1
-  v1 := router.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 := router.Group("/v2")
     v2.POST("/login", loginEndpoint)
     v2.POST("/submit", submitEndpoint)
     v2.POST("/read", readEndpoint)
@@ -514,19 +516,19 @@ Sample Output
 ```go
 func main() {
   router := gin.New()
-  
+
   // skip logging for desired paths by setting SkipPaths in LoggerConfig
   loggerConfig := gin.LoggerConfig{SkipPaths: []string{"/metrics"}}
-  
+
   // skip logging based on your logic by setting Skip func in LoggerConfig
   loggerConfig.Skip = func(c *gin.Context) bool {
       // as an example skip non server side errors
       return c.Writer.Status() < http.StatusInternalServerError
   }
-  
-  engine.Use(gin.LoggerWithConfig(loggerConfig))
+
+  router.Use(gin.LoggerWithConfig(loggerConfig))
   router.Use(gin.Recovery())
-  
+
   // skipped
   router.GET("/metrics", func(c *gin.Context) {
       c.Status(http.StatusNotImplemented)
@@ -541,7 +543,7 @@ func main() {
   router.GET("/data", func(c *gin.Context) {
     c.Status(http.StatusNotImplemented)
   })
-  
+
   router.Run(":8080")
 }
 
@@ -613,7 +615,7 @@ You can also specify that specific fields are required. If a field is decorated
 ```go
 // Binding from JSON
 type Login struct {
-  User     string `form:"user" json:"user" xml:"user"  binding:"required"`
+  User     string `form:"user" json:"user" xml:"user" binding:"required"`
   Password string `form:"password" json:"password" xml:"password" binding:"required"`
 }
 
@@ -830,6 +832,8 @@ type Person struct {
   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"`
+  UnixMilliTime   time.Time `form:"unixMilliTime" time_format:"unixmilli"`
+  UnixMicroTime   time.Time `form:"unixMicroTime" time_format:"uNiXmIcRo"` // case does not matter for "unix*" time formats
 }
 
 func main() {
@@ -849,6 +853,8 @@ func startPage(c *gin.Context) {
     log.Println(person.Birthday)
     log.Println(person.CreateTime)
     log.Println(person.UnixTime)
+    log.Println(person.UnixMilliTime)
+    log.Println(person.UnixMicroTime)
   }
 
   c.String(http.StatusOK, "Success")
@@ -858,7 +864,107 @@ func startPage(c *gin.Context) {
 Test it with:
 
 ```sh
-curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033"
+curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033&unixMilliTime=1562400033001&unixMicroTime=1562400033000012"
+```
+
+
+### Bind default value if none provided
+
+If the server should bind a default value to a field when the client does not provide one, specify the default value using the `default` key within the `form` tag:
+
+```
+package main
+
+import (
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+)
+
+type Person struct {
+	Name      string    `form:"name,default=William"`
+	Age       int       `form:"age,default=10"`
+	Friends   []string  `form:"friends,default=Will;Bill"`
+	Addresses [2]string `form:"addresses,default=foo bar" collection_format:"ssv"`
+	LapTimes  []int     `form:"lap_times,default=1;2;3" collection_format:"csv"`
+}
+
+func main() {
+	g := gin.Default()
+	g.POST("/person", func(c *gin.Context) {
+		var req Person
+		if err := c.ShouldBindQuery(&req); err != nil {
+			c.JSON(http.StatusBadRequest, err)
+			return
+		}
+		c.JSON(http.StatusOK, req)
+	})
+	_ = g.Run("localhost:8080")
+}
+```
+
+```
+curl -X POST http://localhost:8080/person
+{"Name":"William","Age":10,"Friends":["Will","Bill"],"Colors":["red","blue"],"LapTimes":[1,2,3]}
+```
+
+NOTE: For default [collection values](#collection-format-for-arrays), the following rules apply:
+- Since commas are used to delimit tag options, they are not supported within a default value and will result in undefined behavior
+- For the collection formats "multi" and "csv", a semicolon should be used in place of a comma to delimited default values
+- Since semicolons are used to delimit default values for "multi" and "csv", they are not supported within a default value for "multi" and "csv"
+
+
+#### Collection format for arrays
+
+| Format          | Description                                               | Example                 |
+| --------------- | --------------------------------------------------------- | ----------------------- |
+| multi (default) | Multiple parameter instances rather than multiple values. | key=foo&key=bar&key=baz |
+| csv             | Comma-separated values.                                   | foo,bar,baz             |
+| ssv             | Space-separated values.                                   | foo bar baz             |
+| tsv             | Tab-separated values.                                     | "foo\tbar\tbaz"         |
+| pipes           | Pipe-separated values.                                    | foo\|bar\|baz           |
+
+```go
+package main
+
+import (
+	"log"
+	"time"
+	"github.com/gin-gonic/gin"
+)
+
+type Person struct {
+	Name       string    `form:"name"`
+	Addresses  []string  `form:"addresses" collection_format:"csv"`
+	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.Addresses)
+                log.Println(person.Birthday)
+                log.Println(person.CreateTime)
+                log.Println(person.UnixTime)
+        }
+	c.String(200, "Success")
+}
+```
+
+Test it with:
+```sh
+$ curl -X GET "localhost:8085/testing?name=appleboy&addresses=foo,bar&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033"
 ```
 
 ### Bind Uri
@@ -1150,7 +1256,7 @@ func main() {
 
 #### JSONP
 
-Using JSONP to request data from a server  in a different domain. Add callback to response body if the query parameter callback exists.
+Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists.
 
 ```go
 func main() {
@@ -1199,7 +1305,7 @@ func main() {
 
 #### PureJSON
 
-Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes  `\u003c`. If you want to encode such characters literally, you can use PureJSON instead.
+Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes `\u003c`. If you want to encode such characters literally, you can use PureJSON instead.
 This feature is unavailable in Go 1.6 and lower.
 
 ```go
@@ -1234,7 +1340,7 @@ func main() {
   router.StaticFS("/more_static", http.Dir("my_file_system"))
   router.StaticFile("/favicon.ico", "./resources/favicon.ico")
   router.StaticFileFS("/more_favicon.ico", "more_favicon.ico", http.Dir("my_file_system"))
-  
+
   // Listen and serve on 0.0.0.0:8080
   router.Run(":8080")
 }
@@ -2218,7 +2324,7 @@ or network CIDRs from where clients which their request headers related to clien
 IP can be trusted. They can be IPv4 addresses, IPv4 CIDRs, IPv6 addresses or
 IPv6 CIDRs.
 
-**Attention:** Gin trust all proxies by default if you don't specify a trusted 
+**Attention:** Gin trust all proxies by default if you don't specify a trusted
 proxy using the function above, **this is NOT safe**. At the same time, if you don't
 use any proxy, you can disable this feature by using `Engine.SetTrustedProxies(nil)`,
 then `Context.ClientIP()` will return the remote address directly to avoid some
@@ -2247,7 +2353,7 @@ func main() {
 ```
 
 **Notice:** If you are using a CDN service, you can set the `Engine.TrustedPlatform`
-to skip TrustedProxies check, it has a higher priority than TrustedProxies. 
+to skip TrustedProxies check, it has a higher priority than TrustedProxies.
 Look at the example below:
 
 ```go
diff --git a/gin.go b/gin.go
index 5ba1cf6..0761c14 100644
--- a/gin.go
+++ b/gin.go
@@ -598,7 +598,7 @@ func (engine *Engine) RunQUIC(addr, certFile, keyFile string) (err error) {
 
 	if engine.isUnsafeTrustedProxies() {
 		debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
-			"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
+			"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.")
 	}
 
 	err = http3.ListenAndServeQUIC(addr, certFile, keyFile, engine.Handler())
@@ -637,10 +637,12 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 // Disclaimer: You can loop yourself to deal with this, use wisely.
 func (engine *Engine) HandleContext(c *Context) {
 	oldIndexValue := c.index
+	oldHandlers := c.handlers
 	c.reset()
 	engine.handleHTTPRequest(c)
 
 	c.index = oldIndexValue
+	c.handlers = oldHandlers
 }
 
 func (engine *Engine) handleHTTPRequest(c *Context) {
@@ -687,7 +689,7 @@ func (engine *Engine) handleHTTPRequest(c *Context) {
 		break
 	}
 
-	if engine.HandleMethodNotAllowed {
+	if engine.HandleMethodNotAllowed && len(t) > 0 {
 		// According to RFC 7231 section 6.5.5, MUST generate an Allow header field in response
 		// containing a list of the target resource's currently supported methods.
 		allowed := make([]string, 0, len(t)-1)
diff --git a/go.mod b/go.mod
index 4937d2b..1223267 100644
--- a/go.mod
+++ b/go.mod
@@ -1,27 +1,26 @@
 module github.com/gin-gonic/gin
 
-go 1.21.0
+go 1.23.0
 
 require (
-	github.com/bytedance/sonic v1.11.6
+	github.com/bytedance/sonic v1.13.1
 	github.com/gin-contrib/sse v0.1.0
-	github.com/go-playground/validator/v10 v10.20.0
+	github.com/go-playground/validator/v10 v10.22.1
 	github.com/goccy/go-json v0.10.2
 	github.com/json-iterator/go v1.1.12
 	github.com/mattn/go-isatty v0.0.20
 	github.com/pelletier/go-toml/v2 v2.2.2
-	github.com/quic-go/quic-go v0.43.1
+	github.com/quic-go/quic-go v0.48.2
 	github.com/stretchr/testify v1.9.0
 	github.com/ugorji/go/codec v1.2.12
-	golang.org/x/net v0.25.0
+	golang.org/x/net v0.37.0
 	google.golang.org/protobuf v1.34.1
 	gopkg.in/yaml.v3 v3.0.1
 )
 
 require (
-	github.com/bytedance/sonic/loader v0.1.1 // indirect
-	github.com/cloudwego/base64x v0.1.4 // indirect
-	github.com/cloudwego/iasm v0.2.0 // indirect
+	github.com/bytedance/sonic/loader v0.2.4 // indirect
+	github.com/cloudwego/base64x v0.1.5 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/gabriel-vasile/mimetype v1.4.3 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
@@ -29,20 +28,19 @@ require (
 	github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
 	github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
 	github.com/klauspost/cpuid/v2 v2.0.9 // indirect
-	github.com/kr/pretty v0.3.1 // indirect
 	github.com/leodido/go-urn v1.4.0 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/onsi/ginkgo/v2 v2.9.5 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/quic-go/qpack v0.4.0 // indirect
+	github.com/quic-go/qpack v0.5.1 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	go.uber.org/mock v0.4.0 // indirect
 	golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
-	golang.org/x/crypto v0.23.0 // indirect
-	golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
-	golang.org/x/mod v0.11.0 // indirect
-	golang.org/x/sys v0.20.0 // indirect
-	golang.org/x/text v0.15.0 // indirect
-	golang.org/x/tools v0.9.1 // indirect
+	golang.org/x/crypto v0.36.0 // indirect
+	golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
+	golang.org/x/mod v0.17.0 // indirect
+	golang.org/x/sys v0.31.0 // indirect
+	golang.org/x/text v0.23.0 // indirect
+	golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
 )
diff --git a/go.sum b/go.sum
index 44af4cc..2e1c30a 100644
--- a/go.sum
+++ b/go.sum
@@ -1,15 +1,14 @@
-github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
-github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
-github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
+github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g=
+github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
 github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
+github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
-github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
-github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
+github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
+github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
 github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -25,16 +24,16 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
-github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
+github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
 github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
 github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
 github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
 github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
 github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
@@ -44,10 +43,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
 github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
-github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
-github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
 github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -62,15 +57,12 @@ github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
 github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
 github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
 github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
-github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
-github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
-github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ=
-github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
-github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
+github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
+github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
+github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -92,31 +84,31 @@ go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
 go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
-golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
-golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
-golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
-golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
-golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
-golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
+golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
+golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
+golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
+golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
-golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
-golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
 golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
 golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
-golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
-golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
 google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
 google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/tree.go b/tree.go
index b0a5f98..0d3e5a8 100644
--- a/tree.go
+++ b/tree.go
@@ -369,7 +369,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain)
 
 		// currently fixed width 1 for '/'
 		i--
-		if path[i] != '/' {
+		if i < 0 || path[i] != '/' {
 			panic("no / before catch-all in path '" + fullPath + "'")
 		}
 


Test patch:
diff --git a/auth_test.go b/auth_test.go
index f717592..9166e3b 100644
--- a/auth_test.go
+++ b/auth_test.go
@@ -90,7 +90,7 @@ func TestBasicAuthSucceed(t *testing.T) {
 	})
 
 	w := httptest.NewRecorder()
-	req, _ := http.NewRequest("GET", "/login", nil)
+	req, _ := http.NewRequest(http.MethodGet, "/login", nil)
 	req.Header.Set("Authorization", authorizationHeader("admin", "password"))
 	router.ServeHTTP(w, req)
 
@@ -109,7 +109,7 @@ func TestBasicAuth401(t *testing.T) {
 	})
 
 	w := httptest.NewRecorder()
-	req, _ := http.NewRequest("GET", "/login", nil)
+	req, _ := http.NewRequest(http.MethodGet, "/login", nil)
 	req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password")))
 	router.ServeHTTP(w, req)
 
@@ -129,7 +129,7 @@ func TestBasicAuth401WithCustomRealm(t *testing.T) {
 	})
 
 	w := httptest.NewRecorder()
-	req, _ := http.NewRequest("GET", "/login", nil)
+	req, _ := http.NewRequest(http.MethodGet, "/login", nil)
 	req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password")))
 	router.ServeHTTP(w, req)
 
@@ -147,7 +147,7 @@ func TestBasicAuthForProxySucceed(t *testing.T) {
 	})
 
 	w := httptest.NewRecorder()
-	req, _ := http.NewRequest("GET", "/test", nil)
+	req, _ := http.NewRequest(http.MethodGet, "/test", nil)
 	req.Header.Set("Proxy-Authorization", authorizationHeader("admin", "password"))
 	router.ServeHTTP(w, req)
 
@@ -166,7 +166,7 @@ func TestBasicAuthForProxy407(t *testing.T) {
 	})
 
 	w := httptest.NewRecorder()
-	req, _ := http.NewRequest("GET", "/test", nil)
+	req, _ := http.NewRequest(http.MethodGet, "/test", nil)
 	req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password")))
 	router.ServeHTTP(w, req)
 
diff --git a/benchmarks_test.go b/benchmarks_test.go
index 5b7929b..3a8d53f 100644
--- a/benchmarks_test.go
+++ b/benchmarks_test.go
@@ -14,21 +14,21 @@ import (
 func BenchmarkOneRoute(B *testing.B) {
 	router := New()
 	router.GET("/ping", func(c *Context) {})
-	runRequest(B, router, "GET", "/ping")
+	runRequest(B, router, http.MethodGet, "/ping")
 }
 
 func BenchmarkRecoveryMiddleware(B *testing.B) {
 	router := New()
 	router.Use(Recovery())
 	router.GET("/", func(c *Context) {})
-	runRequest(B, router, "GET", "/")
+	runRequest(B, router, http.MethodGet, "/")
 }
 
 func BenchmarkLoggerMiddleware(B *testing.B) {
 	router := New()
 	router.Use(LoggerWithWriter(newMockWriter()))
 	router.GET("/", func(c *Context) {})
-	runRequest(B, router, "GET", "/")
+	runRequest(B, router, http.MethodGet, "/")
 }
 
 func BenchmarkManyHandlers(B *testing.B) {
@@ -37,7 +37,7 @@ func BenchmarkManyHandlers(B *testing.B) {
 	router.Use(func(c *Context) {})
 	router.Use(func(c *Context) {})
 	router.GET("/ping", func(c *Context) {})
-	runRequest(B, router, "GET", "/ping")
+	runRequest(B, router, http.MethodGet, "/ping")
 }
 
 func Benchmark5Params(B *testing.B) {
@@ -45,7 +45,7 @@ func Benchmark5Params(B *testing.B) {
 	router := New()
 	router.Use(func(c *Context) {})
 	router.GET("/param/:param1/:params2/:param3/:param4/:param5", func(c *Context) {})
-	runRequest(B, router, "GET", "/param/path/to/parameter/john/12345")
+	runRequest(B, router, http.MethodGet, "/param/path/to/parameter/john/12345")
 }
 
 func BenchmarkOneRouteJSON(B *testing.B) {
@@ -56,7 +56,7 @@ func BenchmarkOneRouteJSON(B *testing.B) {
 	router.GET("/json", func(c *Context) {
 		c.JSON(http.StatusOK, data)
 	})
-	runRequest(B, router, "GET", "/json")
+	runRequest(B, router, http.MethodGet, "/json")
 }
 
 func BenchmarkOneRouteHTML(B *testing.B) {
@@ -68,7 +68,7 @@ func BenchmarkOneRouteHTML(B *testing.B) {
 	router.GET("/html", func(c *Context) {
 		c.HTML(http.StatusOK, "index", "hola")
 	})
-	runRequest(B, router, "GET", "/html")
+	runRequest(B, router, http.MethodGet, "/html")
 }
 
 func BenchmarkOneRouteSet(B *testing.B) {
@@ -76,7 +76,7 @@ func BenchmarkOneRouteSet(B *testing.B) {
 	router.GET("/ping", func(c *Context) {
 		c.Set("key", "value")
 	})
-	runRequest(B, router, "GET", "/ping")
+	runRequest(B, router, http.MethodGet, "/ping")
 }
 
 func BenchmarkOneRouteString(B *testing.B) {
@@ -84,13 +84,13 @@ func BenchmarkOneRouteString(B *testing.B) {
 	router.GET("/text", func(c *Context) {
 		c.String(http.StatusOK, "this is a plain text")
 	})
-	runRequest(B, router, "GET", "/text")
+	runRequest(B, router, http.MethodGet, "/text")
 }
 
 func BenchmarkManyRoutesFist(B *testing.B) {
 	router := New()
 	router.Any("/ping", func(c *Context) {})
-	runRequest(B, router, "GET", "/ping")
+	runRequest(B, router, http.MethodGet, "/ping")
 }
 
 func BenchmarkManyRoutesLast(B *testing.B) {
@@ -103,7 +103,7 @@ func Benchmark404(B *testing.B) {
 	router := New()
 	router.Any("/something", func(c *Context) {})
 	router.NoRoute(func(c *Context) {})
-	runRequest(B, router, "GET", "/ping")
+	runRequest(B, router, http.MethodGet, "/ping")
 }
 
 func Benchmark404Many(B *testing.B) {
@@ -118,7 +118,7 @@ func Benchmark404Many(B *testing.B) {
 	router.GET("/user/:id/:mode", func(c *Context) {})
 
 	router.NoRoute(func(c *Context) {})
-	runRequest(B, router, "GET", "/viewfake")
+	runRequest(B, router, http.MethodGet, "/viewfake")
 }
 
 type mockWriter struct {
diff --git a/binding/binding_msgpack_test.go b/binding/binding_msgpack_test.go
index a6cd6aa..7a5db34 100644
--- a/binding/binding_msgpack_test.go
+++ b/binding/binding_msgpack_test.go
@@ -8,9 +8,11 @@ package binding
 
 import (
 	"bytes"
+	"net/http"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"github.com/ugorji/go/codec"
 )
 
@@ -24,7 +26,7 @@ func TestBindingMsgPack(t *testing.T) {
 	buf := bytes.NewBuffer([]byte{})
 	assert.NotNil(t, buf)
 	err := codec.NewEncoder(buf, h).Encode(test)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	data := buf.Bytes()
 
@@ -38,20 +40,20 @@ func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body,
 	assert.Equal(t, name, b.Name())
 
 	obj := FooStruct{}
-	req := requestWithBody("POST", path, body)
+	req := requestWithBody(http.MethodPost, path, body)
 	req.Header.Add("Content-Type", MIMEMSGPACK)
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "bar", obj.Foo)
 
 	obj = FooStruct{}
-	req = requestWithBody("POST", badPath, badBody)
+	req = requestWithBody(http.MethodPost, badPath, badBody)
 	req.Header.Add("Content-Type", MIMEMSGPACK)
 	err = MsgPack.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestBindingDefaultMsgPack(t *testing.T) {
-	assert.Equal(t, MsgPack, Default("POST", MIMEMSGPACK))
-	assert.Equal(t, MsgPack, Default("PUT", MIMEMSGPACK2))
+	assert.Equal(t, MsgPack, Default(http.MethodPost, MIMEMSGPACK))
+	assert.Equal(t, MsgPack, Default(http.MethodPut, MIMEMSGPACK2))
 }
diff --git a/binding/binding_test.go b/binding/binding_test.go
index c59e5e9..bdab369 100644
--- a/binding/binding_test.go
+++ b/binding/binding_test.go
@@ -20,6 +20,7 @@ import (
 
 	"github.com/gin-gonic/gin/testdata/protoexample"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"google.golang.org/protobuf/proto"
 )
 
@@ -68,15 +69,19 @@ type FooStructDisallowUnknownFields struct {
 }
 
 type FooBarStructForTimeType struct {
-	TimeFoo    time.Time `form:"time_foo" time_format:"2006-01-02" time_utc:"1" time_location:"Asia/Chongqing"`
-	TimeBar    time.Time `form:"time_bar" time_format:"2006-01-02" time_utc:"1"`
-	CreateTime time.Time `form:"createTime" time_format:"unixNano"`
-	UnixTime   time.Time `form:"unixTime" time_format:"unix"`
+	TimeFoo       time.Time `form:"time_foo" time_format:"2006-01-02" time_utc:"1" time_location:"Asia/Chongqing"`
+	TimeBar       time.Time `form:"time_bar" time_format:"2006-01-02" time_utc:"1"`
+	CreateTime    time.Time `form:"createTime" time_format:"unixNano"`
+	UnixTime      time.Time `form:"unixTime" time_format:"unix"`
+	UnixMilliTime time.Time `form:"unixMilliTime" time_format:"unixmilli"`
+	UnixMicroTime time.Time `form:"unixMicroTime" time_format:"uNiXmiCrO"`
 }
 
 type FooStructForTimeTypeNotUnixFormat struct {
-	CreateTime time.Time `form:"createTime" time_format:"unixNano"`
-	UnixTime   time.Time `form:"unixTime" time_format:"unix"`
+	CreateTime    time.Time `form:"createTime" time_format:"unixNano"`
+	UnixTime      time.Time `form:"unixTime" time_format:"unix"`
+	UnixMilliTime time.Time `form:"unixMilliTime" time_format:"unixMilli"`
+	UnixMicroTime time.Time `form:"unixMicroTime" time_format:"unixMicro"`
 }
 
 type FooStructForTimeTypeNotFormat struct {
@@ -144,38 +149,38 @@ type FooStructForMapPtrType struct {
 }
 
 func TestBindingDefault(t *testing.T) {
-	assert.Equal(t, Form, Default("GET", ""))
-	assert.Equal(t, Form, Default("GET", MIMEJSON))
+	assert.Equal(t, Form, Default(http.MethodGet, ""))
+	assert.Equal(t, Form, Default(http.MethodGet, MIMEJSON))
 
-	assert.Equal(t, JSON, Default("POST", MIMEJSON))
-	assert.Equal(t, JSON, Default("PUT", MIMEJSON))
+	assert.Equal(t, JSON, Default(http.MethodPost, MIMEJSON))
+	assert.Equal(t, JSON, Default(http.MethodPut, MIMEJSON))
 
-	assert.Equal(t, XML, Default("POST", MIMEXML))
-	assert.Equal(t, XML, Default("PUT", MIMEXML2))
+	assert.Equal(t, XML, Default(http.MethodPost, MIMEXML))
+	assert.Equal(t, XML, Default(http.MethodPut, MIMEXML2))
 
-	assert.Equal(t, Form, Default("POST", MIMEPOSTForm))
-	assert.Equal(t, Form, Default("PUT", MIMEPOSTForm))
+	assert.Equal(t, Form, Default(http.MethodPost, MIMEPOSTForm))
+	assert.Equal(t, Form, Default(http.MethodPut, MIMEPOSTForm))
 
-	assert.Equal(t, FormMultipart, Default("POST", MIMEMultipartPOSTForm))
-	assert.Equal(t, FormMultipart, Default("PUT", MIMEMultipartPOSTForm))
+	assert.Equal(t, FormMultipart, Default(http.MethodPost, MIMEMultipartPOSTForm))
+	assert.Equal(t, FormMultipart, Default(http.MethodPut, MIMEMultipartPOSTForm))
 
-	assert.Equal(t, ProtoBuf, Default("POST", MIMEPROTOBUF))
-	assert.Equal(t, ProtoBuf, Default("PUT", MIMEPROTOBUF))
+	assert.Equal(t, ProtoBuf, Default(http.MethodPost, MIMEPROTOBUF))
+	assert.Equal(t, ProtoBuf, Default(http.MethodPut, MIMEPROTOBUF))
 
-	assert.Equal(t, YAML, Default("POST", MIMEYAML))
-	assert.Equal(t, YAML, Default("PUT", MIMEYAML))
-	assert.Equal(t, YAML, Default("POST", MIMEYAML2))
-	assert.Equal(t, YAML, Default("PUT", MIMEYAML2))
+	assert.Equal(t, YAML, Default(http.MethodPost, MIMEYAML))
+	assert.Equal(t, YAML, Default(http.MethodPut, MIMEYAML))
+	assert.Equal(t, YAML, Default(http.MethodPost, MIMEYAML2))
+	assert.Equal(t, YAML, Default(http.MethodPut, MIMEYAML2))
 
-	assert.Equal(t, TOML, Default("POST", MIMETOML))
-	assert.Equal(t, TOML, Default("PUT", MIMETOML))
+	assert.Equal(t, TOML, Default(http.MethodPost, MIMETOML))
+	assert.Equal(t, TOML, Default(http.MethodPut, MIMETOML))
 }
 
 func TestBindingJSONNilBody(t *testing.T) {
 	var obj FooStruct
 	req, _ := http.NewRequest(http.MethodPost, "/", nil)
 	err := JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestBindingJSON(t *testing.T) {
@@ -226,137 +231,137 @@ func TestBindingJSONStringMap(t *testing.T) {
 }
 
 func TestBindingForm(t *testing.T) {
-	testFormBinding(t, "POST",
+	testFormBinding(t, http.MethodPost,
 		"/", "/",
 		"foo=bar&bar=foo", "bar2=foo")
 }
 
 func TestBindingForm2(t *testing.T) {
-	testFormBinding(t, "GET",
+	testFormBinding(t, http.MethodGet,
 		"/?foo=bar&bar=foo", "/?bar2=foo",
 		"", "")
 }
 
 func TestBindingFormEmbeddedStruct(t *testing.T) {
-	testFormBindingEmbeddedStruct(t, "POST",
+	testFormBindingEmbeddedStruct(t, http.MethodPost,
 		"/", "/",
 		"page=1&size=2&appkey=test-appkey", "bar2=foo")
 }
 
 func TestBindingFormEmbeddedStruct2(t *testing.T) {
-	testFormBindingEmbeddedStruct(t, "GET",
+	testFormBindingEmbeddedStruct(t, http.MethodGet,
 		"/?page=1&size=2&appkey=test-appkey", "/?bar2=foo",
 		"", "")
 }
 
 func TestBindingFormDefaultValue(t *testing.T) {
-	testFormBindingDefaultValue(t, "POST",
+	testFormBindingDefaultValue(t, http.MethodPost,
 		"/", "/",
 		"foo=bar", "bar2=foo")
 }
 
 func TestBindingFormDefaultValue2(t *testing.T) {
-	testFormBindingDefaultValue(t, "GET",
+	testFormBindingDefaultValue(t, http.MethodGet,
 		"/?foo=bar", "/?bar2=foo",
 		"", "")
 }
 
 func TestBindingFormForTime(t *testing.T) {
-	testFormBindingForTime(t, "POST",
+	testFormBindingForTime(t, http.MethodPost,
 		"/", "/",
-		"time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033", "bar2=foo")
-	testFormBindingForTimeNotUnixFormat(t, "POST",
+		"time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033&unixMilliTime=1562400033001&unixMicroTime=1562400033000012", "bar2=foo")
+	testFormBindingForTimeNotUnixFormat(t, http.MethodPost,
 		"/", "/",
-		"time_foo=2017-11-15&createTime=bad&unixTime=bad", "bar2=foo")
-	testFormBindingForTimeNotFormat(t, "POST",
+		"time_foo=2017-11-15&createTime=bad&unixTime=bad&unixMilliTime=bad&unixMicroTime=bad", "bar2=foo")
+	testFormBindingForTimeNotFormat(t, http.MethodPost,
 		"/", "/",
 		"time_foo=2017-11-15", "bar2=foo")
-	testFormBindingForTimeFailFormat(t, "POST",
+	testFormBindingForTimeFailFormat(t, http.MethodPost,
 		"/", "/",
 		"time_foo=2017-11-15", "bar2=foo")
-	testFormBindingForTimeFailLocation(t, "POST",
+	testFormBindingForTimeFailLocation(t, http.MethodPost,
 		"/", "/",
 		"time_foo=2017-11-15", "bar2=foo")
 }
 
 func TestBindingFormForTime2(t *testing.T) {
-	testFormBindingForTime(t, "GET",
-		"/?time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033", "/?bar2=foo",
+	testFormBindingForTime(t, http.MethodGet,
+		"/?time_foo=2017-11-15&time_bar=&createTime=1562400033000000123&unixTime=1562400033&unixMilliTime=1562400033001&unixMicroTime=1562400033000012", "/?bar2=foo",
 		"", "")
-	testFormBindingForTimeNotUnixFormat(t, "POST",
+	testFormBindingForTimeNotUnixFormat(t, http.MethodPost,
 		"/", "/",
-		"time_foo=2017-11-15&createTime=bad&unixTime=bad", "bar2=foo")
-	testFormBindingForTimeNotFormat(t, "GET",
+		"time_foo=2017-11-15&createTime=bad&unixTime=bad&unixMilliTime=bad&unixMicroTime=bad", "bar2=foo")
+	testFormBindingForTimeNotFormat(t, http.MethodGet,
 		"/?time_foo=2017-11-15", "/?bar2=foo",
 		"", "")
-	testFormBindingForTimeFailFormat(t, "GET",
+	testFormBindingForTimeFailFormat(t, http.MethodGet,
 		"/?time_foo=2017-11-15", "/?bar2=foo",
 		"", "")
-	testFormBindingForTimeFailLocation(t, "GET",
+	testFormBindingForTimeFailLocation(t, http.MethodGet,
 		"/?time_foo=2017-11-15", "/?bar2=foo",
 		"", "")
 }
 
 func TestFormBindingIgnoreField(t *testing.T) {
-	testFormBindingIgnoreField(t, "POST",
+	testFormBindingIgnoreField(t, http.MethodPost,
 		"/", "/",
 		"-=bar", "")
 }
 
 func TestBindingFormInvalidName(t *testing.T) {
-	testFormBindingInvalidName(t, "POST",
+	testFormBindingInvalidName(t, http.MethodPost,
 		"/", "/",
 		"test_name=bar", "bar2=foo")
 }
 
 func TestBindingFormInvalidName2(t *testing.T) {
-	testFormBindingInvalidName2(t, "POST",
+	testFormBindingInvalidName2(t, http.MethodPost,
 		"/", "/",
 		"map_foo=bar", "bar2=foo")
 }
 
 func TestBindingFormForType(t *testing.T) {
-	testFormBindingForType(t, "POST",
+	testFormBindingForType(t, http.MethodPost,
 		"/", "/",
 		"map_foo={\"bar\":123}", "map_foo=1", "Map")
 
-	testFormBindingForType(t, "POST",
+	testFormBindingForType(t, http.MethodPost,
 		"/", "/",
 		"slice_foo=1&slice_foo=2", "bar2=1&bar2=2", "Slice")
 
-	testFormBindingForType(t, "GET",
+	testFormBindingForType(t, http.MethodGet,
 		"/?slice_foo=1&slice_foo=2", "/?bar2=1&bar2=2",
 		"", "", "Slice")
 
-	testFormBindingForType(t, "POST",
+	testFormBindingForType(t, http.MethodPost,
 		"/", "/",
 		"slice_map_foo=1&slice_map_foo=2", "bar2=1&bar2=2", "SliceMap")
 
-	testFormBindingForType(t, "GET",
+	testFormBindingForType(t, http.MethodGet,
 		"/?slice_map_foo=1&slice_map_foo=2", "/?bar2=1&bar2=2",
 		"", "", "SliceMap")
 
-	testFormBindingForType(t, "POST",
+	testFormBindingForType(t, http.MethodPost,
 		"/", "/",
 		"ptr_bar=test", "bar2=test", "Ptr")
 
-	testFormBindingForType(t, "GET",
+	testFormBindingForType(t, http.MethodGet,
 		"/?ptr_bar=test", "/?bar2=test",
 		"", "", "Ptr")
 
-	testFormBindingForType(t, "POST",
+	testFormBindingForType(t, http.MethodPost,
 		"/", "/",
 		"idx=123", "id1=1", "Struct")
 
-	testFormBindingForType(t, "GET",
+	testFormBindingForType(t, http.MethodGet,
 		"/?idx=123", "/?id1=1",
 		"", "", "Struct")
 
-	testFormBindingForType(t, "POST",
+	testFormBindingForType(t, http.MethodPost,
 		"/", "/",
 		"name=thinkerou", "name1=ou", "StructPointer")
 
-	testFormBindingForType(t, "GET",
+	testFormBindingForType(t, http.MethodGet,
 		"/?name=thinkerou", "/?name1=ou",
 		"", "", "StructPointer")
 }
@@ -373,10 +378,10 @@ func TestBindingFormStringMap(t *testing.T) {
 
 func TestBindingFormStringSliceMap(t *testing.T) {
 	obj := make(map[string][]string)
-	req := requestWithBody("POST", "/", "foo=something&foo=bar&hello=world")
+	req := requestWithBody(http.MethodPost, "/", "foo=something&foo=bar&hello=world")
 	req.Header.Add("Content-Type", MIMEPOSTForm)
 	err := Form.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.NotNil(t, obj)
 	assert.Len(t, obj, 2)
 	target := map[string][]string{
@@ -386,38 +391,38 @@ func TestBindingFormStringSliceMap(t *testing.T) {
 	assert.True(t, reflect.DeepEqual(obj, target))
 
 	objInvalid := make(map[string][]int)
-	req = requestWithBody("POST", "/", "foo=something&foo=bar&hello=world")
+	req = requestWithBody(http.MethodPost, "/", "foo=something&foo=bar&hello=world")
 	req.Header.Add("Content-Type", MIMEPOSTForm)
 	err = Form.Bind(req, &objInvalid)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestBindingQuery(t *testing.T) {
-	testQueryBinding(t, "POST",
+	testQueryBinding(t, http.MethodPost,
 		"/?foo=bar&bar=foo", "/",
 		"foo=unused", "bar2=foo")
 }
 
 func TestBindingQuery2(t *testing.T) {
-	testQueryBinding(t, "GET",
+	testQueryBinding(t, http.MethodGet,
 		"/?foo=bar&bar=foo", "/?bar2=foo",
 		"foo=unused", "")
 }
 
 func TestBindingQueryFail(t *testing.T) {
-	testQueryBindingFail(t, "POST",
+	testQueryBindingFail(t, http.MethodPost,
 		"/?map_foo=", "/",
 		"map_foo=unused", "bar2=foo")
 }
 
 func TestBindingQueryFail2(t *testing.T) {
-	testQueryBindingFail(t, "GET",
+	testQueryBindingFail(t, http.MethodGet,
 		"/?map_foo=", "/?bar2=foo",
 		"map_foo=unused", "")
 }
 
 func TestBindingQueryBoolFail(t *testing.T) {
-	testQueryBindingBoolFail(t, "GET",
+	testQueryBindingBoolFail(t, http.MethodGet,
 		"/?bool_foo=fasl", "/?bar2=foo",
 		"bool_foo=unused", "")
 }
@@ -426,18 +431,18 @@ func TestBindingQueryStringMap(t *testing.T) {
 	b := Query
 
 	obj := make(map[string]string)
-	req := requestWithBody("GET", "/?foo=bar&hello=world", "")
+	req := requestWithBody(http.MethodGet, "/?foo=bar&hello=world", "")
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.NotNil(t, obj)
 	assert.Len(t, obj, 2)
 	assert.Equal(t, "bar", obj["foo"])
 	assert.Equal(t, "world", obj["hello"])
 
 	obj = make(map[string]string)
-	req = requestWithBody("GET", "/?foo=bar&foo=2&hello=world", "") // should pick last
+	req = requestWithBody(http.MethodGet, "/?foo=bar&foo=2&hello=world", "") // should pick last
 	err = b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.NotNil(t, obj)
 	assert.Len(t, obj, 2)
 	assert.Equal(t, "2", obj["foo"])
@@ -494,29 +499,29 @@ func TestBindingYAMLFail(t *testing.T) {
 }
 
 func createFormPostRequest(t *testing.T) *http.Request {
-	req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
-	assert.NoError(t, err)
+	req, err := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar&bar=foo"))
+	require.NoError(t, err)
 	req.Header.Set("Content-Type", MIMEPOSTForm)
 	return req
 }
 
 func createDefaultFormPostRequest(t *testing.T) *http.Request {
-	req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar"))
-	assert.NoError(t, err)
+	req, err := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", bytes.NewBufferString("foo=bar"))
+	require.NoError(t, err)
 	req.Header.Set("Content-Type", MIMEPOSTForm)
 	return req
 }
 
 func createFormPostRequestForMap(t *testing.T) *http.Request {
-	req, err := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo={\"bar\":123}"))
-	assert.NoError(t, err)
+	req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", bytes.NewBufferString("map_foo={\"bar\":123}"))
+	require.NoError(t, err)
 	req.Header.Set("Content-Type", MIMEPOSTForm)
 	return req
 }
 
 func createFormPostRequestForMapFail(t *testing.T) *http.Request {
-	req, err := http.NewRequest("POST", "/?map_foo=getfoo", bytes.NewBufferString("map_foo=hello"))
-	assert.NoError(t, err)
+	req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", bytes.NewBufferString("map_foo=hello"))
+	require.NoError(t, err)
 	req.Header.Set("Content-Type", MIMEPOSTForm)
 	return req
 }
@@ -527,20 +532,20 @@ func createFormFilesMultipartRequest(t *testing.T) *http.Request {
 	mw := multipart.NewWriter(body)
 	defer mw.Close()
 
-	assert.NoError(t, mw.SetBoundary(boundary))
-	assert.NoError(t, mw.WriteField("foo", "bar"))
-	assert.NoError(t, mw.WriteField("bar", "foo"))
+	require.NoError(t, mw.SetBoundary(boundary))
+	require.NoError(t, mw.WriteField("foo", "bar"))
+	require.NoError(t, mw.WriteField("bar", "foo"))
 
 	f, err := os.Open("form.go")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	defer f.Close()
 	fw, err1 := mw.CreateFormFile("file", "form.go")
-	assert.NoError(t, err1)
+	require.NoError(t, err1)
 	_, err = io.Copy(fw, f)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
-	req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
-	assert.NoError(t, err2)
+	req, err2 := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", body)
+	require.NoError(t, err2)
 	req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
 
 	return req
@@ -552,20 +557,20 @@ func createFormFilesMultipartRequestFail(t *testing.T) *http.Request {
 	mw := multipart.NewWriter(body)
 	defer mw.Close()
 
-	assert.NoError(t, mw.SetBoundary(boundary))
-	assert.NoError(t, mw.WriteField("foo", "bar"))
-	assert.NoError(t, mw.WriteField("bar", "foo"))
+	require.NoError(t, mw.SetBoundary(boundary))
+	require.NoError(t, mw.WriteField("foo", "bar"))
+	require.NoError(t, mw.WriteField("bar", "foo"))
 
 	f, err := os.Open("form.go")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	defer f.Close()
 	fw, err1 := mw.CreateFormFile("file_foo", "form_foo.go")
-	assert.NoError(t, err1)
+	require.NoError(t, err1)
 	_, err = io.Copy(fw, f)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
-	req, err2 := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
-	assert.NoError(t, err2)
+	req, err2 := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", body)
+	require.NoError(t, err2)
 	req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
 
 	return req
@@ -577,11 +582,11 @@ func createFormMultipartRequest(t *testing.T) *http.Request {
 	mw := multipart.NewWriter(body)
 	defer mw.Close()
 
-	assert.NoError(t, mw.SetBoundary(boundary))
-	assert.NoError(t, mw.WriteField("foo", "bar"))
-	assert.NoError(t, mw.WriteField("bar", "foo"))
-	req, err := http.NewRequest("POST", "/?foo=getfoo&bar=getbar", body)
-	assert.NoError(t, err)
+	require.NoError(t, mw.SetBoundary(boundary))
+	require.NoError(t, mw.WriteField("foo", "bar"))
+	require.NoError(t, mw.WriteField("bar", "foo"))
+	req, err := http.NewRequest(http.MethodPost, "/?foo=getfoo&bar=getbar", body)
+	require.NoError(t, err)
 	req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
 	return req
 }
@@ -592,10 +597,10 @@ func createFormMultipartRequestForMap(t *testing.T) *http.Request {
 	mw := multipart.NewWriter(body)
 	defer mw.Close()
 
-	assert.NoError(t, mw.SetBoundary(boundary))
-	assert.NoError(t, mw.WriteField("map_foo", "{\"bar\":123, \"name\":\"thinkerou\", \"pai\": 3.14}"))
-	req, err := http.NewRequest("POST", "/?map_foo=getfoo", body)
-	assert.NoError(t, err)
+	require.NoError(t, mw.SetBoundary(boundary))
+	require.NoError(t, mw.WriteField("map_foo", "{\"bar\":123, \"name\":\"thinkerou\", \"pai\": 3.14}"))
+	req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", body)
+	require.NoError(t, err)
 	req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
 	return req
 }
@@ -606,10 +611,10 @@ func createFormMultipartRequestForMapFail(t *testing.T) *http.Request {
 	mw := multipart.NewWriter(body)
 	defer mw.Close()
 
-	assert.NoError(t, mw.SetBoundary(boundary))
-	assert.NoError(t, mw.WriteField("map_foo", "3.14"))
-	req, err := http.NewRequest("POST", "/?map_foo=getfoo", body)
-	assert.NoError(t, err)
+	require.NoError(t, mw.SetBoundary(boundary))
+	require.NoError(t, mw.WriteField("map_foo", "3.14"))
+	req, err := http.NewRequest(http.MethodPost, "/?map_foo=getfoo", body)
+	require.NoError(t, err)
 	req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
 	return req
 }
@@ -617,7 +622,7 @@ func createFormMultipartRequestForMapFail(t *testing.T) *http.Request {
 func TestBindingFormPost(t *testing.T) {
 	req := createFormPostRequest(t)
 	var obj FooBarStruct
-	assert.NoError(t, FormPost.Bind(req, &obj))
+	require.NoError(t, FormPost.Bind(req, &obj))
 
 	assert.Equal(t, "form-urlencoded", FormPost.Name())
 	assert.Equal(t, "bar", obj.Foo)
@@ -627,7 +632,7 @@ func TestBindingFormPost(t *testing.T) {
 func TestBindingDefaultValueFormPost(t *testing.T) {
 	req := createDefaultFormPostRequest(t)
 	var obj FooDefaultBarStruct
-	assert.NoError(t, FormPost.Bind(req, &obj))
+	require.NoError(t, FormPost.Bind(req, &obj))
 
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, "hello", obj.Bar)
@@ -637,22 +642,22 @@ func TestBindingFormPostForMap(t *testing.T) {
 	req := createFormPostRequestForMap(t)
 	var obj FooStructForMapType
 	err := FormPost.Bind(req, &obj)
-	assert.NoError(t, err)
-	assert.Equal(t, float64(123), obj.MapFoo["bar"].(float64))
+	require.NoError(t, err)
+	assert.InDelta(t, float64(123), obj.MapFoo["bar"].(float64), 0.01)
 }
 
 func TestBindingFormPostForMapFail(t *testing.T) {
 	req := createFormPostRequestForMapFail(t)
 	var obj FooStructForMapType
 	err := FormPost.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestBindingFormFilesMultipart(t *testing.T) {
 	req := createFormFilesMultipartRequest(t)
 	var obj FooBarFileStruct
 	err := FormMultipart.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	// file from os
 	f, _ := os.Open("form.go")
@@ -664,9 +669,9 @@ func TestBindingFormFilesMultipart(t *testing.T) {
 	defer mf.Close()
 	fileExpect, _ := io.ReadAll(mf)
 
-	assert.Equal(t, FormMultipart.Name(), "multipart/form-data")
-	assert.Equal(t, obj.Foo, "bar")
-	assert.Equal(t, obj.Bar, "foo")
+	assert.Equal(t, "multipart/form-data", FormMultipart.Name())
+	assert.Equal(t, "bar", obj.Foo)
+	assert.Equal(t, "foo", obj.Bar)
 	assert.Equal(t, fileExpect, fileActual)
 }
 
@@ -674,13 +679,13 @@ func TestBindingFormFilesMultipartFail(t *testing.T) {
 	req := createFormFilesMultipartRequestFail(t)
 	var obj FooBarFileFailStruct
 	err := FormMultipart.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestBindingFormMultipart(t *testing.T) {
 	req := createFormMultipartRequest(t)
 	var obj FooBarStruct
-	assert.NoError(t, FormMultipart.Bind(req, &obj))
+	require.NoError(t, FormMultipart.Bind(req, &obj))
 
 	assert.Equal(t, "multipart/form-data", FormMultipart.Name())
 	assert.Equal(t, "bar", obj.Foo)
@@ -691,17 +696,17 @@ func TestBindingFormMultipartForMap(t *testing.T) {
 	req := createFormMultipartRequestForMap(t)
 	var obj FooStructForMapType
 	err := FormMultipart.Bind(req, &obj)
-	assert.NoError(t, err)
-	assert.Equal(t, float64(123), obj.MapFoo["bar"].(float64))
+	require.NoError(t, err)
+	assert.InDelta(t, float64(123), obj.MapFoo["bar"].(float64), 0.01)
 	assert.Equal(t, "thinkerou", obj.MapFoo["name"].(string))
-	assert.Equal(t, float64(3.14), obj.MapFoo["pai"].(float64))
+	assert.InDelta(t, float64(3.14), obj.MapFoo["pai"].(float64), 0.01)
 }
 
 func TestBindingFormMultipartForMapFail(t *testing.T) {
 	req := createFormMultipartRequestForMapFail(t)
 	var obj FooStructForMapType
 	err := FormMultipart.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestBindingProtoBuf(t *testing.T) {
@@ -730,9 +735,9 @@ func TestBindingProtoBufFail(t *testing.T) {
 
 func TestValidationFails(t *testing.T) {
 	var obj FooStruct
-	req := requestWithBody("POST", "/", `{"bar": "foo"}`)
+	req := requestWithBody(http.MethodPost, "/", `{"bar": "foo"}`)
 	err := JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestValidationDisabled(t *testing.T) {
@@ -741,9 +746,9 @@ func TestValidationDisabled(t *testing.T) {
 	defer func() { Validator = backup }()
 
 	var obj FooStruct
-	req := requestWithBody("POST", "/", `{"bar": "foo"}`)
+	req := requestWithBody(http.MethodPost, "/", `{"bar": "foo"}`)
 	err := JSON.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 }
 
 func TestRequiredSucceeds(t *testing.T) {
@@ -752,9 +757,9 @@ func TestRequiredSucceeds(t *testing.T) {
 	}
 
 	var obj HogeStruct
-	req := requestWithBody("POST", "/", `{"hoge": 0}`)
+	req := requestWithBody(http.MethodPost, "/", `{"hoge": 0}`)
 	err := JSON.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 }
 
 func TestRequiredFails(t *testing.T) {
@@ -763,9 +768,9 @@ func TestRequiredFails(t *testing.T) {
 	}
 
 	var obj HogeStruct
-	req := requestWithBody("POST", "/", `{"boen": 0}`)
+	req := requestWithBody(http.MethodPost, "/", `{"boen": 0}`)
 	err := JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestHeaderBinding(t *testing.T) {
@@ -777,12 +782,12 @@ func TestHeaderBinding(t *testing.T) {
 	}
 
 	var theader tHeader
-	req := requestWithBody("GET", "/", "")
+	req := requestWithBody(http.MethodGet, "/", "")
 	req.Header.Add("limit", "1000")
-	assert.NoError(t, h.Bind(req, &theader))
+	require.NoError(t, h.Bind(req, &theader))
 	assert.Equal(t, 1000, theader.Limit)
 
-	req = requestWithBody("GET", "/", "")
+	req = requestWithBody(http.MethodGet, "/", "")
 	req.Header.Add("fail", `{fail:fail}`)
 
 	type failStruct struct {
@@ -790,7 +795,7 @@ func TestHeaderBinding(t *testing.T) {
 	}
 
 	err := h.Bind(req, &failStruct{})
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestUriBinding(t *testing.T) {
@@ -803,14 +808,14 @@ func TestUriBinding(t *testing.T) {
 	var tag Tag
 	m := make(map[string][]string)
 	m["name"] = []string{"thinkerou"}
-	assert.NoError(t, b.BindUri(m, &tag))
+	require.NoError(t, b.BindUri(m, &tag))
 	assert.Equal(t, "thinkerou", tag.Name)
 
 	type NotSupportStruct struct {
 		Name map[string]any `uri:"name"`
 	}
 	var not NotSupportStruct
-	assert.Error(t, b.BindUri(m, &not))
+	require.Error(t, b.BindUri(m, &not))
 	assert.Equal(t, map[string]any(nil), not.Name)
 }
 
@@ -831,9 +836,9 @@ func TestUriInnerBinding(t *testing.T) {
 	}
 
 	var tag Tag
-	assert.NoError(t, Uri.BindUri(m, &tag))
-	assert.Equal(t, tag.Name, expectedName)
-	assert.Equal(t, tag.S.Age, expectedAge)
+	require.NoError(t, Uri.BindUri(m, &tag))
+	assert.Equal(t, expectedName, tag.Name)
+	assert.Equal(t, expectedAge, tag.S.Age)
 }
 
 func testFormBindingEmbeddedStruct(t *testing.T, method, path, badPath, body, badBody string) {
@@ -842,11 +847,11 @@ func testFormBindingEmbeddedStruct(t *testing.T, method, path, badPath, body, ba
 
 	obj := QueryTest{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, 1, obj.Page)
 	assert.Equal(t, 2, obj.Size)
 	assert.Equal(t, "test-appkey", obj.Appkey)
@@ -858,18 +863,18 @@ func testFormBinding(t *testing.T, method, path, badPath, body, badBody string)
 
 	obj := FooBarStruct{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, "foo", obj.Bar)
 
 	obj = FooBarStruct{}
 	req = requestWithBody(method, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testFormBindingDefaultValue(t *testing.T, method, path, badPath, body, badBody string) {
@@ -878,18 +883,18 @@ func testFormBindingDefaultValue(t *testing.T, method, path, badPath, body, badB
 
 	obj := FooDefaultBarStruct{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, "hello", obj.Bar)
 
 	obj = FooDefaultBarStruct{}
 	req = requestWithBody(method, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestFormBindingFail(t *testing.T) {
@@ -897,20 +902,20 @@ func TestFormBindingFail(t *testing.T) {
 	assert.Equal(t, "form", b.Name())
 
 	obj := FooBarStruct{}
-	req, _ := http.NewRequest("POST", "/", nil)
+	req, _ := http.NewRequest(http.MethodPost, "/", nil)
 	err := b.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestFormBindingMultipartFail(t *testing.T) {
 	obj := FooBarStruct{}
-	req, err := http.NewRequest("POST", "/", strings.NewReader("foo=bar"))
-	assert.NoError(t, err)
+	req, err := http.NewRequest(http.MethodPost, "/", strings.NewReader("foo=bar"))
+	require.NoError(t, err)
 	req.Header.Set("Content-Type", MIMEMultipartPOSTForm+";boundary=testboundary")
 	_, err = req.MultipartReader()
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	err = Form.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestFormPostBindingFail(t *testing.T) {
@@ -918,9 +923,9 @@ func TestFormPostBindingFail(t *testing.T) {
 	assert.Equal(t, "form-urlencoded", b.Name())
 
 	obj := FooBarStruct{}
-	req, _ := http.NewRequest("POST", "/", nil)
+	req, _ := http.NewRequest(http.MethodPost, "/", nil)
 	err := b.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestFormMultipartBindingFail(t *testing.T) {
@@ -928,9 +933,9 @@ func TestFormMultipartBindingFail(t *testing.T) {
 	assert.Equal(t, "multipart/form-data", b.Name())
 
 	obj := FooBarStruct{}
-	req, _ := http.NewRequest("POST", "/", nil)
+	req, _ := http.NewRequest(http.MethodPost, "/", nil)
 	err := b.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody string) {
@@ -939,23 +944,25 @@ func testFormBindingForTime(t *testing.T, method, path, badPath, body, badBody s
 
 	obj := FooBarStructForTimeType{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, int64(1510675200), obj.TimeFoo.Unix())
 	assert.Equal(t, "Asia/Chongqing", obj.TimeFoo.Location().String())
 	assert.Equal(t, int64(-62135596800), obj.TimeBar.Unix())
 	assert.Equal(t, "UTC", obj.TimeBar.Location().String())
 	assert.Equal(t, int64(1562400033000000123), obj.CreateTime.UnixNano())
 	assert.Equal(t, int64(1562400033), obj.UnixTime.Unix())
+	assert.Equal(t, int64(1562400033001), obj.UnixMilliTime.UnixMilli())
+	assert.Equal(t, int64(1562400033000012), obj.UnixMicroTime.UnixMicro())
 
 	obj = FooBarStructForTimeType{}
 	req = requestWithBody(method, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testFormBindingForTimeNotUnixFormat(t *testing.T, method, path, badPath, body, badBody string) {
@@ -964,16 +971,16 @@ func testFormBindingForTimeNotUnixFormat(t *testing.T, method, path, badPath, bo
 
 	obj := FooStructForTimeTypeNotUnixFormat{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 
 	obj = FooStructForTimeTypeNotUnixFormat{}
 	req = requestWithBody(method, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testFormBindingForTimeNotFormat(t *testing.T, method, path, badPath, body, badBody string) {
@@ -982,16 +989,16 @@ func testFormBindingForTimeNotFormat(t *testing.T, method, path, badPath, body,
 
 	obj := FooStructForTimeTypeNotFormat{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 
 	obj = FooStructForTimeTypeNotFormat{}
 	req = requestWithBody(method, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testFormBindingForTimeFailFormat(t *testing.T, method, path, badPath, body, badBody string) {
@@ -1000,16 +1007,16 @@ func testFormBindingForTimeFailFormat(t *testing.T, method, path, badPath, body,
 
 	obj := FooStructForTimeTypeFailFormat{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 
 	obj = FooStructForTimeTypeFailFormat{}
 	req = requestWithBody(method, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, body, badBody string) {
@@ -1018,16 +1025,16 @@ func testFormBindingForTimeFailLocation(t *testing.T, method, path, badPath, bod
 
 	obj := FooStructForTimeTypeFailLocation{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 
 	obj = FooStructForTimeTypeFailLocation{}
 	req = requestWithBody(method, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testFormBindingIgnoreField(t *testing.T, method, path, badPath, body, badBody string) {
@@ -1036,11 +1043,11 @@ func testFormBindingIgnoreField(t *testing.T, method, path, badPath, body, badBo
 
 	obj := FooStructForIgnoreFormTag{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.Nil(t, obj.Foo)
 }
@@ -1051,17 +1058,17 @@ func testFormBindingInvalidName(t *testing.T, method, path, badPath, body, badBo
 
 	obj := InvalidNameType{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "", obj.TestName)
 
 	obj = InvalidNameType{}
 	req = requestWithBody(method, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testFormBindingInvalidName2(t *testing.T, method, path, badPath, body, badBody string) {
@@ -1070,16 +1077,16 @@ func testFormBindingInvalidName2(t *testing.T, method, path, badPath, body, badB
 
 	obj := InvalidNameMapType{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 
 	obj = InvalidNameMapType{}
 	req = requestWithBody(method, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody string, typ string) {
@@ -1087,24 +1094,24 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
 	assert.Equal(t, "form", b.Name())
 
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	switch typ {
 	case "Slice":
 		obj := FooStructForSliceType{}
 		err := b.Bind(req, &obj)
-		assert.NoError(t, err)
+		require.NoError(t, err)
 		assert.Equal(t, []int{1, 2}, obj.SliceFoo)
 
 		obj = FooStructForSliceType{}
 		req = requestWithBody(method, badPath, badBody)
 		err = JSON.Bind(req, &obj)
-		assert.Error(t, err)
+		require.Error(t, err)
 	case "Struct":
 		obj := FooStructForStructType{}
 		err := b.Bind(req, &obj)
-		assert.NoError(t, err)
+		require.NoError(t, err)
 		assert.Equal(t,
 			struct {
 				Idx int "form:\"idx\""
@@ -1113,7 +1120,7 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
 	case "StructPointer":
 		obj := FooStructForStructPointerType{}
 		err := b.Bind(req, &obj)
-		assert.NoError(t, err)
+		require.NoError(t, err)
 		assert.Equal(t,
 			struct {
 				Name string "form:\"name\""
@@ -1122,33 +1129,33 @@ func testFormBindingForType(t *testing.T, method, path, badPath, body, badBody s
 	case "Map":
 		obj := FooStructForMapType{}
 		err := b.Bind(req, &obj)
-		assert.NoError(t, err)
-		assert.Equal(t, float64(123), obj.MapFoo["bar"].(float64))
+		require.NoError(t, err)
+		assert.InDelta(t, float64(123), obj.MapFoo["bar"].(float64), 0.01)
 	case "SliceMap":
 		obj := FooStructForSliceMapType{}
 		err := b.Bind(req, &obj)
-		assert.Error(t, err)
+		require.Error(t, err)
 	case "Ptr":
 		obj := FooStructForStringPtrType{}
 		err := b.Bind(req, &obj)
-		assert.NoError(t, err)
+		require.NoError(t, err)
 		assert.Nil(t, obj.PtrFoo)
 		assert.Equal(t, "test", *obj.PtrBar)
 
 		obj = FooStructForStringPtrType{}
 		obj.PtrBar = new(string)
 		err = b.Bind(req, &obj)
-		assert.NoError(t, err)
+		require.NoError(t, err)
 		assert.Equal(t, "test", *obj.PtrBar)
 
 		objErr := FooStructForMapPtrType{}
 		err = b.Bind(req, &objErr)
-		assert.Error(t, err)
+		require.Error(t, err)
 
 		obj = FooStructForStringPtrType{}
 		req = requestWithBody(method, badPath, badBody)
 		err = b.Bind(req, &obj)
-		assert.Error(t, err)
+		require.Error(t, err)
 	}
 }
 
@@ -1158,11 +1165,11 @@ func testQueryBinding(t *testing.T, method, path, badPath, body, badBody string)
 
 	obj := FooBarStruct{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, "foo", obj.Bar)
 }
@@ -1173,11 +1180,11 @@ func testQueryBindingFail(t *testing.T, method, path, badPath, body, badBody str
 
 	obj := FooStructForMapType{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testQueryBindingBoolFail(t *testing.T, method, path, badPath, body, badBody string) {
@@ -1186,50 +1193,50 @@ func testQueryBindingBoolFail(t *testing.T, method, path, badPath, body, badBody
 
 	obj := FooStructForBoolType{}
 	req := requestWithBody(method, path, body)
-	if method == "POST" {
+	if method == http.MethodPost {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
 	assert.Equal(t, name, b.Name())
 
 	obj := FooStruct{}
-	req := requestWithBody("POST", path, body)
+	req := requestWithBody(http.MethodPost, path, body)
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "bar", obj.Foo)
 
 	obj = FooStruct{}
-	req = requestWithBody("POST", badPath, badBody)
+	req = requestWithBody(http.MethodPost, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testBodyBindingSlice(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
 	assert.Equal(t, name, b.Name())
 
 	var obj1 []FooStruct
-	req := requestWithBody("POST", path, body)
+	req := requestWithBody(http.MethodPost, path, body)
 	err := b.Bind(req, &obj1)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	var obj2 []FooStruct
-	req = requestWithBody("POST", badPath, badBody)
+	req = requestWithBody(http.MethodPost, badPath, badBody)
 	err = JSON.Bind(req, &obj2)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badBody string) {
 	obj := make(map[string]string)
-	req := requestWithBody("POST", path, body)
+	req := requestWithBody(http.MethodPost, path, body)
 	if b.Name() == "form" {
 		req.Header.Add("Content-Type", MIMEPOSTForm)
 	}
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.NotNil(t, obj)
 	assert.Len(t, obj, 2)
 	assert.Equal(t, "bar", obj["foo"])
@@ -1237,52 +1244,52 @@ func testBodyBindingStringMap(t *testing.T, b Binding, path, badPath, body, badB
 
 	if badPath != "" && badBody != "" {
 		obj = make(map[string]string)
-		req = requestWithBody("POST", badPath, badBody)
+		req = requestWithBody(http.MethodPost, badPath, badBody)
 		err = b.Bind(req, &obj)
-		assert.Error(t, err)
+		require.Error(t, err)
 	}
 
 	objInt := make(map[string]int)
-	req = requestWithBody("POST", path, body)
+	req = requestWithBody(http.MethodPost, path, body)
 	err = b.Bind(req, &objInt)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testBodyBindingUseNumber(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
 	assert.Equal(t, name, b.Name())
 
 	obj := FooStructUseNumber{}
-	req := requestWithBody("POST", path, body)
+	req := requestWithBody(http.MethodPost, path, body)
 	EnableDecoderUseNumber = true
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	// we hope it is int64(123)
 	v, e := obj.Foo.(json.Number).Int64()
-	assert.NoError(t, e)
+	require.NoError(t, e)
 	assert.Equal(t, int64(123), v)
 
 	obj = FooStructUseNumber{}
-	req = requestWithBody("POST", badPath, badBody)
+	req = requestWithBody(http.MethodPost, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testBodyBindingUseNumber2(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
 	assert.Equal(t, name, b.Name())
 
 	obj := FooStructUseNumber{}
-	req := requestWithBody("POST", path, body)
+	req := requestWithBody(http.MethodPost, path, body)
 	EnableDecoderUseNumber = false
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	// it will return float64(123) if not use EnableDecoderUseNumber
 	// maybe it is not hoped
-	assert.Equal(t, float64(123), obj.Foo)
+	assert.InDelta(t, float64(123), obj.Foo, 0.01)
 
 	obj = FooStructUseNumber{}
-	req = requestWithBody("POST", badPath, badBody)
+	req = requestWithBody(http.MethodPost, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testBodyBindingDisallowUnknownFields(t *testing.T, b Binding, path, badPath, body, badBody string) {
@@ -1292,15 +1299,15 @@ func testBodyBindingDisallowUnknownFields(t *testing.T, b Binding, path, badPath
 	}()
 
 	obj := FooStructDisallowUnknownFields{}
-	req := requestWithBody("POST", path, body)
+	req := requestWithBody(http.MethodPost, path, body)
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "bar", obj.Foo)
 
 	obj = FooStructDisallowUnknownFields{}
-	req = requestWithBody("POST", badPath, badBody)
+	req = requestWithBody(http.MethodPost, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 	assert.Contains(t, err.Error(), "what")
 }
 
@@ -1308,32 +1315,32 @@ func testBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, bad
 	assert.Equal(t, name, b.Name())
 
 	obj := FooStruct{}
-	req := requestWithBody("POST", path, body)
+	req := requestWithBody(http.MethodPost, path, body)
 	err := b.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 	assert.Equal(t, "", obj.Foo)
 
 	obj = FooStruct{}
-	req = requestWithBody("POST", badPath, badBody)
+	req = requestWithBody(http.MethodPost, badPath, badBody)
 	err = JSON.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func testProtoBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
 	assert.Equal(t, name, b.Name())
 
 	obj := protoexample.Test{}
-	req := requestWithBody("POST", path, body)
+	req := requestWithBody(http.MethodPost, path, body)
 	req.Header.Add("Content-Type", MIMEPROTOBUF)
 	err := b.Bind(req, &obj)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "yes", *obj.Label)
 
 	obj = protoexample.Test{}
-	req = requestWithBody("POST", badPath, badBody)
+	req = requestWithBody(http.MethodPost, badPath, badBody)
 	req.Header.Add("Content-Type", MIMEPROTOBUF)
 	err = ProtoBuf.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 type hook struct{}
@@ -1357,54 +1364,54 @@ func TestPlainBinding(t *testing.T) {
 	assert.Equal(t, "plain", p.Name())
 
 	var s string
-	req := requestWithBody("POST", "/", "test string")
-	assert.NoError(t, p.Bind(req, &s))
-	assert.Equal(t, s, "test string")
+	req := requestWithBody(http.MethodPost, "/", "test string")
+	require.NoError(t, p.Bind(req, &s))
+	assert.Equal(t, "test string", s)
 
 	var bs []byte
-	req = requestWithBody("POST", "/", "test []byte")
-	assert.NoError(t, p.Bind(req, &bs))
+	req = requestWithBody(http.MethodPost, "/", "test []byte")
+	require.NoError(t, p.Bind(req, &bs))
 	assert.Equal(t, bs, []byte("test []byte"))
 
 	var i int
-	req = requestWithBody("POST", "/", "test fail")
-	assert.Error(t, p.Bind(req, &i))
+	req = requestWithBody(http.MethodPost, "/", "test fail")
+	require.Error(t, p.Bind(req, &i))
 
-	req = requestWithBody("POST", "/", "")
+	req = requestWithBody(http.MethodPost, "/", "")
 	req.Body = &failRead{}
-	assert.Error(t, p.Bind(req, &s))
+	require.Error(t, p.Bind(req, &s))
 
-	req = requestWithBody("POST", "/", "")
-	assert.Nil(t, p.Bind(req, nil))
+	req = requestWithBody(http.MethodPost, "/", "")
+	require.NoError(t, p.Bind(req, nil))
 
 	var ptr *string
-	req = requestWithBody("POST", "/", "")
-	assert.Nil(t, p.Bind(req, ptr))
+	req = requestWithBody(http.MethodPost, "/", "")
+	require.NoError(t, p.Bind(req, ptr))
 }
 
 func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
 	assert.Equal(t, name, b.Name())
 
 	obj := protoexample.Test{}
-	req := requestWithBody("POST", path, body)
+	req := requestWithBody(http.MethodPost, path, body)
 
 	req.Body = io.NopCloser(&hook{})
 	req.Header.Add("Content-Type", MIMEPROTOBUF)
 	err := b.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 
 	invalidobj := FooStruct{}
 	req.Body = io.NopCloser(strings.NewReader(`{"msg":"hello"}`))
 	req.Header.Add("Content-Type", MIMEPROTOBUF)
 	err = b.Bind(req, &invalidobj)
-	assert.Error(t, err)
-	assert.Equal(t, err.Error(), "obj is not ProtoMessage")
+	require.Error(t, err)
+	assert.Equal(t, "obj is not ProtoMessage", err.Error())
 
 	obj = protoexample.Test{}
-	req = requestWithBody("POST", badPath, badBody)
+	req = requestWithBody(http.MethodPost, badPath, badBody)
 	req.Header.Add("Content-Type", MIMEPROTOBUF)
 	err = ProtoBuf.Bind(req, &obj)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func requestWithBody(method, path, body string) (req *http.Request) {
diff --git a/binding/form_mapping_test.go b/binding/form_mapping_test.go
index afd51f9..1277fd5 100644
--- a/binding/form_mapping_test.go
+++ b/binding/form_mapping_test.go
@@ -6,7 +6,7 @@ package binding
 
 import (
 	"encoding/hex"
-	"fmt"
+	"errors"
 	"mime/multipart"
 	"reflect"
 	"strconv"
@@ -15,6 +15,7 @@ import (
 	"time"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestMappingBaseTypes(t *testing.T) {
@@ -59,7 +60,7 @@ func TestMappingBaseTypes(t *testing.T) {
 		field := val.Elem().Type().Field(0)
 
 		_, err := mapping(val, emptyField, formSource{field.Name: {tt.form}}, "form")
-		assert.NoError(t, err, testName)
+		require.NoError(t, err, testName)
 
 		actual := val.Elem().Field(0).Interface()
 		assert.Equal(t, tt.expect, actual, testName)
@@ -68,13 +69,15 @@ func TestMappingBaseTypes(t *testing.T) {
 
 func TestMappingDefault(t *testing.T) {
 	var s struct {
+		Str   string `form:",default=defaultVal"`
 		Int   int    `form:",default=9"`
 		Slice []int  `form:",default=9"`
 		Array [1]int `form:",default=9"`
 	}
 	err := mappingByPtr(&s, formSource{}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
+	assert.Equal(t, "defaultVal", s.Str)
 	assert.Equal(t, 9, s.Int)
 	assert.Equal(t, []int{9}, s.Slice)
 	assert.Equal(t, [1]int{9}, s.Array)
@@ -85,7 +88,7 @@ func TestMappingSkipField(t *testing.T) {
 		A int
 	}
 	err := mappingByPtr(&s, formSource{}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.Equal(t, 0, s.A)
 }
@@ -96,7 +99,7 @@ func TestMappingIgnoreField(t *testing.T) {
 		B int `form:"-"`
 	}
 	err := mappingByPtr(&s, formSource{"A": {"9"}, "B": {"9"}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.Equal(t, 9, s.A)
 	assert.Equal(t, 0, s.B)
@@ -108,7 +111,7 @@ func TestMappingUnexportedField(t *testing.T) {
 		b int `form:"b"`
 	}
 	err := mappingByPtr(&s, formSource{"a": {"9"}, "b": {"9"}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.Equal(t, 9, s.A)
 	assert.Equal(t, 0, s.b)
@@ -119,7 +122,7 @@ func TestMappingPrivateField(t *testing.T) {
 		f int `form:"field"`
 	}
 	err := mappingByPtr(&s, formSource{"field": {"6"}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, 0, s.f)
 }
 
@@ -129,7 +132,7 @@ func TestMappingUnknownFieldType(t *testing.T) {
 	}
 
 	err := mappingByPtr(&s, formSource{"U": {"unknown"}}, "form")
-	assert.Error(t, err)
+	require.Error(t, err)
 	assert.Equal(t, errUnknownType, err)
 }
 
@@ -138,7 +141,7 @@ func TestMappingURI(t *testing.T) {
 		F int `uri:"field"`
 	}
 	err := mapURI(&s, map[string][]string{"field": {"6"}})
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, 6, s.F)
 }
 
@@ -147,16 +150,34 @@ func TestMappingForm(t *testing.T) {
 		F int `form:"field"`
 	}
 	err := mapForm(&s, map[string][]string{"field": {"6"}})
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, 6, s.F)
 }
 
+func TestMappingFormFieldNotSent(t *testing.T) {
+	var s struct {
+		F string `form:"field,default=defVal"`
+	}
+	err := mapForm(&s, map[string][]string{})
+	require.NoError(t, err)
+	assert.Equal(t, "defVal", s.F)
+}
+
+func TestMappingFormWithEmptyToDefault(t *testing.T) {
+	var s struct {
+		F string `form:"field,default=DefVal"`
+	}
+	err := mapForm(&s, map[string][]string{"field": {""}})
+	require.NoError(t, err)
+	assert.Equal(t, "DefVal", s.F)
+}
+
 func TestMapFormWithTag(t *testing.T) {
 	var s struct {
 		F int `externalTag:"field"`
 	}
 	err := MapFormWithTag(&s, map[string][]string{"field": {"6"}}, "externalTag")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, 6, s.F)
 }
 
@@ -171,7 +192,7 @@ func TestMappingTime(t *testing.T) {
 
 	var err error
 	time.Local, err = time.LoadLocation("Europe/Berlin")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	err = mapForm(&s, map[string][]string{
 		"Time":      {"2019-01-20T16:02:58Z"},
@@ -180,7 +201,7 @@ func TestMappingTime(t *testing.T) {
 		"CSTTime":   {"2019-01-20"},
 		"UTCTime":   {"2019-01-20"},
 	})
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.Equal(t, "2019-01-20 16:02:58 +0000 UTC", s.Time.String())
 	assert.Equal(t, "2019-01-20 00:00:00 +0100 CET", s.LocalTime.String())
@@ -195,14 +216,14 @@ func TestMappingTime(t *testing.T) {
 		Time time.Time `time_location:"wrong"`
 	}
 	err = mapForm(&wrongLoc, map[string][]string{"Time": {"2019-01-20T16:02:58Z"}})
-	assert.Error(t, err)
+	require.Error(t, err)
 
 	// wrong time value
 	var wrongTime struct {
 		Time time.Time
 	}
 	err = mapForm(&wrongTime, map[string][]string{"Time": {"wrong"}})
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestMappingTimeDuration(t *testing.T) {
@@ -212,12 +233,12 @@ func TestMappingTimeDuration(t *testing.T) {
 
 	// ok
 	err := mappingByPtr(&s, formSource{"D": {"5s"}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, 5*time.Second, s.D)
 
 	// error
 	err = mappingByPtr(&s, formSource{"D": {"wrong"}}, "form")
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestMappingSlice(t *testing.T) {
@@ -227,17 +248,17 @@ func TestMappingSlice(t *testing.T) {
 
 	// default value
 	err := mappingByPtr(&s, formSource{}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, []int{9}, s.Slice)
 
 	// ok
 	err = mappingByPtr(&s, formSource{"slice": {"3", "4"}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, []int{3, 4}, s.Slice)
 
 	// error
 	err = mappingByPtr(&s, formSource{"slice": {"wrong"}}, "form")
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestMappingArray(t *testing.T) {
@@ -247,20 +268,125 @@ func TestMappingArray(t *testing.T) {
 
 	// wrong default
 	err := mappingByPtr(&s, formSource{}, "form")
-	assert.Error(t, err)
+	require.Error(t, err)
 
 	// ok
 	err = mappingByPtr(&s, formSource{"array": {"3", "4"}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, [2]int{3, 4}, s.Array)
 
 	// error - not enough vals
 	err = mappingByPtr(&s, formSource{"array": {"3"}}, "form")
-	assert.Error(t, err)
+	require.Error(t, err)
 
 	// error - wrong value
 	err = mappingByPtr(&s, formSource{"array": {"wrong"}}, "form")
-	assert.Error(t, err)
+	require.Error(t, err)
+}
+
+func TestMappingCollectionFormat(t *testing.T) {
+	var s struct {
+		SliceMulti []int  `form:"slice_multi" collection_format:"multi"`
+		SliceCsv   []int  `form:"slice_csv" collection_format:"csv"`
+		SliceSsv   []int  `form:"slice_ssv" collection_format:"ssv"`
+		SliceTsv   []int  `form:"slice_tsv" collection_format:"tsv"`
+		SlicePipes []int  `form:"slice_pipes" collection_format:"pipes"`
+		ArrayMulti [2]int `form:"array_multi" collection_format:"multi"`
+		ArrayCsv   [2]int `form:"array_csv" collection_format:"csv"`
+		ArraySsv   [2]int `form:"array_ssv" collection_format:"ssv"`
+		ArrayTsv   [2]int `form:"array_tsv" collection_format:"tsv"`
+		ArrayPipes [2]int `form:"array_pipes" collection_format:"pipes"`
+	}
+	err := mappingByPtr(&s, formSource{
+		"slice_multi": {"1", "2"},
+		"slice_csv":   {"1,2"},
+		"slice_ssv":   {"1 2"},
+		"slice_tsv":   {"1	2"},
+		"slice_pipes": {"1|2"},
+		"array_multi": {"1", "2"},
+		"array_csv":   {"1,2"},
+		"array_ssv":   {"1 2"},
+		"array_tsv":   {"1	2"},
+		"array_pipes": {"1|2"},
+	}, "form")
+	require.NoError(t, err)
+
+	assert.Equal(t, []int{1, 2}, s.SliceMulti)
+	assert.Equal(t, []int{1, 2}, s.SliceCsv)
+	assert.Equal(t, []int{1, 2}, s.SliceSsv)
+	assert.Equal(t, []int{1, 2}, s.SliceTsv)
+	assert.Equal(t, []int{1, 2}, s.SlicePipes)
+	assert.Equal(t, [2]int{1, 2}, s.ArrayMulti)
+	assert.Equal(t, [2]int{1, 2}, s.ArrayCsv)
+	assert.Equal(t, [2]int{1, 2}, s.ArraySsv)
+	assert.Equal(t, [2]int{1, 2}, s.ArrayTsv)
+	assert.Equal(t, [2]int{1, 2}, s.ArrayPipes)
+}
+
+func TestMappingCollectionFormatInvalid(t *testing.T) {
+	var s struct {
+		SliceCsv []int `form:"slice_csv" collection_format:"xxx"`
+	}
+	err := mappingByPtr(&s, formSource{
+		"slice_csv": {"1,2"},
+	}, "form")
+	require.Error(t, err)
+
+	var s2 struct {
+		ArrayCsv [2]int `form:"array_csv" collection_format:"xxx"`
+	}
+	err = mappingByPtr(&s2, formSource{
+		"array_csv": {"1,2"},
+	}, "form")
+	require.Error(t, err)
+}
+
+func TestMappingMultipleDefaultWithCollectionFormat(t *testing.T) {
+	var s struct {
+		SliceMulti       []int     `form:",default=1;2;3" collection_format:"multi"`
+		SliceCsv         []int     `form:",default=1;2;3" collection_format:"csv"`
+		SliceSsv         []int     `form:",default=1 2 3" collection_format:"ssv"`
+		SliceTsv         []int     `form:",default=1\t2\t3" collection_format:"tsv"`
+		SlicePipes       []int     `form:",default=1|2|3" collection_format:"pipes"`
+		ArrayMulti       [2]int    `form:",default=1;2" collection_format:"multi"`
+		ArrayCsv         [2]int    `form:",default=1;2" collection_format:"csv"`
+		ArraySsv         [2]int    `form:",default=1 2" collection_format:"ssv"`
+		ArrayTsv         [2]int    `form:",default=1\t2" collection_format:"tsv"`
+		ArrayPipes       [2]int    `form:",default=1|2" collection_format:"pipes"`
+		SliceStringMulti []string  `form:",default=1;2;3" collection_format:"multi"`
+		SliceStringCsv   []string  `form:",default=1;2;3" collection_format:"csv"`
+		SliceStringSsv   []string  `form:",default=1 2 3" collection_format:"ssv"`
+		SliceStringTsv   []string  `form:",default=1\t2\t3" collection_format:"tsv"`
+		SliceStringPipes []string  `form:",default=1|2|3" collection_format:"pipes"`
+		ArrayStringMulti [2]string `form:",default=1;2" collection_format:"multi"`
+		ArrayStringCsv   [2]string `form:",default=1;2" collection_format:"csv"`
+		ArrayStringSsv   [2]string `form:",default=1 2" collection_format:"ssv"`
+		ArrayStringTsv   [2]string `form:",default=1\t2" collection_format:"tsv"`
+		ArrayStringPipes [2]string `form:",default=1|2" collection_format:"pipes"`
+	}
+	err := mappingByPtr(&s, formSource{}, "form")
+	require.NoError(t, err)
+
+	assert.Equal(t, []int{1, 2, 3}, s.SliceMulti)
+	assert.Equal(t, []int{1, 2, 3}, s.SliceCsv)
+	assert.Equal(t, []int{1, 2, 3}, s.SliceSsv)
+	assert.Equal(t, []int{1, 2, 3}, s.SliceTsv)
+	assert.Equal(t, []int{1, 2, 3}, s.SlicePipes)
+	assert.Equal(t, [2]int{1, 2}, s.ArrayMulti)
+	assert.Equal(t, [2]int{1, 2}, s.ArrayCsv)
+	assert.Equal(t, [2]int{1, 2}, s.ArraySsv)
+	assert.Equal(t, [2]int{1, 2}, s.ArrayTsv)
+	assert.Equal(t, [2]int{1, 2}, s.ArrayPipes)
+	assert.Equal(t, []string{"1", "2", "3"}, s.SliceStringMulti)
+	assert.Equal(t, []string{"1", "2", "3"}, s.SliceStringCsv)
+	assert.Equal(t, []string{"1", "2", "3"}, s.SliceStringSsv)
+	assert.Equal(t, []string{"1", "2", "3"}, s.SliceStringTsv)
+	assert.Equal(t, []string{"1", "2", "3"}, s.SliceStringPipes)
+	assert.Equal(t, [2]string{"1", "2"}, s.ArrayStringMulti)
+	assert.Equal(t, [2]string{"1", "2"}, s.ArrayStringCsv)
+	assert.Equal(t, [2]string{"1", "2"}, s.ArrayStringSsv)
+	assert.Equal(t, [2]string{"1", "2"}, s.ArrayStringTsv)
+	assert.Equal(t, [2]string{"1", "2"}, s.ArrayStringPipes)
 }
 
 func TestMappingStructField(t *testing.T) {
@@ -271,7 +397,7 @@ func TestMappingStructField(t *testing.T) {
 	}
 
 	err := mappingByPtr(&s, formSource{"J": {`{"I": 9}`}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, 9, s.J.I)
 }
 
@@ -289,20 +415,20 @@ func TestMappingPtrField(t *testing.T) {
 	// With 0 items.
 	var req0 ptrRequest
 	err = mappingByPtr(&req0, formSource{}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Empty(t, req0.Items)
 
 	// With 1 item.
 	var req1 ptrRequest
 	err = mappingByPtr(&req1, formSource{"items": {`{"key": 1}`}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Len(t, req1.Items, 1)
 	assert.EqualValues(t, 1, req1.Items[0].Key)
 
 	// With 2 items.
 	var req2 ptrRequest
 	err = mappingByPtr(&req2, formSource{"items": {`{"key": 1}`, `{"key": 2}`}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Len(t, req2.Items, 2)
 	assert.EqualValues(t, 1, req2.Items[0].Key)
 	assert.EqualValues(t, 2, req2.Items[1].Key)
@@ -314,7 +440,7 @@ func TestMappingMapField(t *testing.T) {
 	}
 
 	err := mappingByPtr(&s, formSource{"M": {`{"one": 1}`}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, map[string]int{"one": 1}, s.M)
 }
 
@@ -325,7 +451,7 @@ func TestMappingIgnoredCircularRef(t *testing.T) {
 	var s S
 
 	err := mappingByPtr(&s, formSource{}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 }
 
 type customUnmarshalParamHex int
@@ -344,7 +470,7 @@ func TestMappingCustomUnmarshalParamHexWithFormTag(t *testing.T) {
 		Foo customUnmarshalParamHex `form:"foo"`
 	}
 	err := mappingByPtr(&s, formSource{"foo": {`f5`}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.EqualValues(t, 245, s.Foo)
 }
@@ -354,7 +480,7 @@ func TestMappingCustomUnmarshalParamHexWithURITag(t *testing.T) {
 		Foo customUnmarshalParamHex `uri:"foo"`
 	}
 	err := mappingByPtr(&s, formSource{"foo": {`f5`}}, "uri")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.EqualValues(t, 245, s.Foo)
 }
@@ -368,7 +494,7 @@ type customUnmarshalParamType struct {
 func (f *customUnmarshalParamType) UnmarshalParam(param string) error {
 	parts := strings.Split(param, ":")
 	if len(parts) != 3 {
-		return fmt.Errorf("invalid format")
+		return errors.New("invalid format")
 	}
 	f.Protocol = parts[0]
 	f.Path = parts[1]
@@ -381,7 +507,7 @@ func TestMappingCustomStructTypeWithFormTag(t *testing.T) {
 		FileData customUnmarshalParamType `form:"data"`
 	}
 	err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.EqualValues(t, "file", s.FileData.Protocol)
 	assert.EqualValues(t, "/foo", s.FileData.Path)
@@ -393,7 +519,7 @@ func TestMappingCustomStructTypeWithURITag(t *testing.T) {
 		FileData customUnmarshalParamType `uri:"data"`
 	}
 	err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "uri")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.EqualValues(t, "file", s.FileData.Protocol)
 	assert.EqualValues(t, "/foo", s.FileData.Path)
@@ -405,7 +531,7 @@ func TestMappingCustomPointerStructTypeWithFormTag(t *testing.T) {
 		FileData *customUnmarshalParamType `form:"data"`
 	}
 	err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.EqualValues(t, "file", s.FileData.Protocol)
 	assert.EqualValues(t, "/foo", s.FileData.Path)
@@ -417,7 +543,7 @@ func TestMappingCustomPointerStructTypeWithURITag(t *testing.T) {
 		FileData *customUnmarshalParamType `uri:"data"`
 	}
 	err := mappingByPtr(&s, formSource{"data": {`file:/foo:happiness`}}, "uri")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.EqualValues(t, "file", s.FileData.Protocol)
 	assert.EqualValues(t, "/foo", s.FileData.Path)
@@ -430,7 +556,7 @@ func (p *customPath) UnmarshalParam(param string) error {
 	elems := strings.Split(param, "/")
 	n := len(elems)
 	if n < 2 {
-		return fmt.Errorf("invalid format")
+		return errors.New("invalid format")
 	}
 
 	*p = elems
@@ -442,7 +568,7 @@ func TestMappingCustomSliceUri(t *testing.T) {
 		FileData customPath `uri:"path"`
 	}
 	err := mappingByPtr(&s, formSource{"path": {`bar/foo`}}, "uri")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.EqualValues(t, "bar", s.FileData[0])
 	assert.EqualValues(t, "foo", s.FileData[1])
@@ -453,7 +579,7 @@ func TestMappingCustomSliceForm(t *testing.T) {
 		FileData customPath `form:"path"`
 	}
 	err := mappingByPtr(&s, formSource{"path": {`bar/foo`}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.EqualValues(t, "bar", s.FileData[0])
 	assert.EqualValues(t, "foo", s.FileData[1])
@@ -474,7 +600,7 @@ func (o *objectID) UnmarshalParam(param string) error {
 func convertTo(s string) (objectID, error) {
 	var nilObjectID objectID
 	if len(s) != 24 {
-		return nilObjectID, fmt.Errorf("invalid format")
+		return nilObjectID, errors.New("invalid format")
 	}
 
 	var oid [12]byte
@@ -492,7 +618,7 @@ func TestMappingCustomArrayUri(t *testing.T) {
 	}
 	val := `664a062ac74a8ad104e0e80f`
 	err := mappingByPtr(&s, formSource{"id": {val}}, "uri")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	expected, _ := convertTo(val)
 	assert.EqualValues(t, expected, s.FileData)
@@ -504,7 +630,7 @@ func TestMappingCustomArrayForm(t *testing.T) {
 	}
 	val := `664a062ac74a8ad104e0e80f`
 	err := mappingByPtr(&s, formSource{"id": {val}}, "form")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	expected, _ := convertTo(val)
 	assert.EqualValues(t, expected, s.FileData)
diff --git a/binding/multipart_form_mapping_test.go b/binding/multipart_form_mapping_test.go
index 4e97c0f..c93f214 100644
--- a/binding/multipart_form_mapping_test.go
+++ b/binding/multipart_form_mapping_test.go
@@ -12,6 +12,7 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestFormMultipartBindingBindOneFile(t *testing.T) {
@@ -27,7 +28,7 @@ func TestFormMultipartBindingBindOneFile(t *testing.T) {
 
 	req := createRequestMultipartFiles(t, file)
 	err := FormMultipart.Bind(req, &s)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assertMultipartFileHeader(t, &s.FileValue, file)
 	assertMultipartFileHeader(t, s.FilePtr, file)
@@ -53,7 +54,7 @@ func TestFormMultipartBindingBindTwoFiles(t *testing.T) {
 
 	req := createRequestMultipartFiles(t, files...)
 	err := FormMultipart.Bind(req, &s)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.Len(t, s.SliceValues, len(files))
 	assert.Len(t, s.SlicePtrs, len(files))
@@ -90,7 +91,7 @@ func TestFormMultipartBindingBindError(t *testing.T) {
 	} {
 		req := createRequestMultipartFiles(t, files...)
 		err := FormMultipart.Bind(req, tt.s)
-		assert.Error(t, err)
+		require.Error(t, err)
 	}
 }
 
@@ -106,17 +107,17 @@ func createRequestMultipartFiles(t *testing.T, files ...testFile) *http.Request
 	mw := multipart.NewWriter(&body)
 	for _, file := range files {
 		fw, err := mw.CreateFormFile(file.Fieldname, file.Filename)
-		assert.NoError(t, err)
+		require.NoError(t, err)
 
 		n, err := fw.Write(file.Content)
-		assert.NoError(t, err)
+		require.NoError(t, err)
 		assert.Equal(t, len(file.Content), n)
 	}
 	err := mw.Close()
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
-	req, err := http.NewRequest("POST", "/", &body)
-	assert.NoError(t, err)
+	req, err := http.NewRequest(http.MethodPost, "/", &body)
+	require.NoError(t, err)
 
 	req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+mw.Boundary())
 	return req
@@ -127,12 +128,12 @@ func assertMultipartFileHeader(t *testing.T, fh *multipart.FileHeader, file test
 	assert.Equal(t, int64(len(file.Content)), fh.Size)
 
 	fl, err := fh.Open()
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	body, err := io.ReadAll(fl)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, string(file.Content), string(body))
 
 	err = fl.Close()
-	assert.NoError(t, err)
+	require.NoError(t, err)
 }
diff --git a/binding/validate_test.go b/binding/validate_test.go
index 1fc15ff..c9bbe60 100644
--- a/binding/validate_test.go
+++ b/binding/validate_test.go
@@ -11,6 +11,7 @@ import (
 
 	"github.com/go-playground/validator/v10"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 type testInterface interface {
@@ -113,10 +114,10 @@ func TestValidateNoValidationValues(t *testing.T) {
 	test := createNoValidationValues()
 	empty := structNoValidationValues{}
 
-	assert.Nil(t, validate(test))
-	assert.Nil(t, validate(&test))
-	assert.Nil(t, validate(empty))
-	assert.Nil(t, validate(&empty))
+	require.NoError(t, validate(test))
+	require.NoError(t, validate(&test))
+	require.NoError(t, validate(empty))
+	require.NoError(t, validate(&empty))
 
 	assert.Equal(t, origin, test)
 }
@@ -163,8 +164,8 @@ func TestValidateNoValidationPointers(t *testing.T) {
 
 	//assert.Nil(t, validate(test))
 	//assert.Nil(t, validate(&test))
-	assert.Nil(t, validate(empty))
-	assert.Nil(t, validate(&empty))
+	require.NoError(t, validate(empty))
+	require.NoError(t, validate(&empty))
 
 	//assert.Equal(t, origin, test)
 }
@@ -173,22 +174,22 @@ type Object map[string]any
 
 func TestValidatePrimitives(t *testing.T) {
 	obj := Object{"foo": "bar", "bar": 1}
-	assert.NoError(t, validate(obj))
-	assert.NoError(t, validate(&obj))
+	require.NoError(t, validate(obj))
+	require.NoError(t, validate(&obj))
 	assert.Equal(t, Object{"foo": "bar", "bar": 1}, obj)
 
 	obj2 := []Object{{"foo": "bar", "bar": 1}, {"foo": "bar", "bar": 1}}
-	assert.NoError(t, validate(obj2))
-	assert.NoError(t, validate(&obj2))
+	require.NoError(t, validate(obj2))
+	require.NoError(t, validate(&obj2))
 
 	nu := 10
-	assert.NoError(t, validate(nu))
-	assert.NoError(t, validate(&nu))
+	require.NoError(t, validate(nu))
+	require.NoError(t, validate(&nu))
 	assert.Equal(t, 10, nu)
 
 	str := "value"
-	assert.NoError(t, validate(str))
-	assert.NoError(t, validate(&str))
+	require.NoError(t, validate(str))
+	require.NoError(t, validate(&str))
 	assert.Equal(t, "value", str)
 }
 
@@ -212,8 +213,8 @@ func TestValidateAndModifyStruct(t *testing.T) {
 	s := structModifyValidation{Integer: 1}
 	errs := validate(&s)
 
-	assert.Nil(t, errs)
-	assert.Equal(t, s, structModifyValidation{Integer: 0})
+	require.NoError(t, errs)
+	assert.Equal(t, structModifyValidation{Integer: 0}, s)
 }
 
 // structCustomValidation is a helper struct we use to check that
@@ -239,14 +240,14 @@ func TestValidatorEngine(t *testing.T) {
 
 	err := engine.RegisterValidation("notone", notOne)
 	// Check that we can register custom validation without error
-	assert.Nil(t, err)
+	require.NoError(t, err)
 
 	// Create an instance which will fail validation
 	withOne := structCustomValidation{Integer: 1}
 	errs := validate(withOne)
 
 	// Check that we got back non-nil errs
-	assert.NotNil(t, errs)
+	require.Error(t, errs)
 	// Check that the error matches expectation
-	assert.Error(t, errs, "", "", "notone")
+	require.Error(t, errs, "", "", "notone")
 }
diff --git a/context_test.go b/context_test.go
index 8bbf270..ef0cfcc 100644
--- a/context_test.go
+++ b/context_test.go
@@ -11,13 +11,16 @@ import (
 	"fmt"
 	"html/template"
 	"io"
+	"io/fs"
 	"mime/multipart"
 	"net"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
 	"os"
+	"path/filepath"
 	"reflect"
+	"strconv"
 	"strings"
 	"sync"
 	"testing"
@@ -27,6 +30,7 @@ import (
 	"github.com/gin-gonic/gin/binding"
 	testdata "github.com/gin-gonic/gin/testdata/protoexample"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"google.golang.org/protobuf/proto"
 )
 
@@ -58,7 +62,7 @@ func createMultipartRequest() *http.Request {
 	must(mw.WriteField("time_location", "31/12/2016 14:55"))
 	must(mw.WriteField("names[a]", "thinkerou"))
 	must(mw.WriteField("names[b]", "tianou"))
-	req, err := http.NewRequest("POST", "/", body)
+	req, err := http.NewRequest(http.MethodPost, "/", body)
 	must(err)
 	req.Header.Set("Content-Type", MIMEMultipartPOSTForm+"; boundary="+boundary)
 	return req
@@ -74,20 +78,18 @@ func TestContextFormFile(t *testing.T) {
 	buf := new(bytes.Buffer)
 	mw := multipart.NewWriter(buf)
 	w, err := mw.CreateFormFile("file", "test")
-	if assert.NoError(t, err) {
-		_, err = w.Write([]byte("test"))
-		assert.NoError(t, err)
-	}
+	require.NoError(t, err)
+	_, err = w.Write([]byte("test"))
+	require.NoError(t, err)
 	mw.Close()
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", buf)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", buf)
 	c.Request.Header.Set("Content-Type", mw.FormDataContentType())
 	f, err := c.FormFile("file")
-	if assert.NoError(t, err) {
-		assert.Equal(t, "test", f.Filename)
-	}
+	require.NoError(t, err)
+	assert.Equal(t, "test", f.Filename)
 
-	assert.NoError(t, c.SaveUploadedFile(f, "test"))
+	require.NoError(t, c.SaveUploadedFile(f, "test"))
 }
 
 func TestContextFormFileFailed(t *testing.T) {
@@ -95,33 +97,31 @@ func TestContextFormFileFailed(t *testing.T) {
 	mw := multipart.NewWriter(buf)
 	mw.Close()
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 	c.Request.Header.Set("Content-Type", mw.FormDataContentType())
 	c.engine.MaxMultipartMemory = 8 << 20
 	f, err := c.FormFile("file")
-	assert.Error(t, err)
+	require.Error(t, err)
 	assert.Nil(t, f)
 }
 
 func TestContextMultipartForm(t *testing.T) {
 	buf := new(bytes.Buffer)
 	mw := multipart.NewWriter(buf)
-	assert.NoError(t, mw.WriteField("foo", "bar"))
+	require.NoError(t, mw.WriteField("foo", "bar"))
 	w, err := mw.CreateFormFile("file", "test")
-	if assert.NoError(t, err) {
-		_, err = w.Write([]byte("test"))
-		assert.NoError(t, err)
-	}
+	require.NoError(t, err)
+	_, err = w.Write([]byte("test"))
+	require.NoError(t, err)
 	mw.Close()
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", buf)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", buf)
 	c.Request.Header.Set("Content-Type", mw.FormDataContentType())
 	f, err := c.MultipartForm()
-	if assert.NoError(t, err) {
-		assert.NotNil(t, f)
-	}
+	require.NoError(t, err)
+	assert.NotNil(t, f)
 
-	assert.NoError(t, c.SaveUploadedFile(f.File["file"][0], "test"))
+	require.NoError(t, c.SaveUploadedFile(f.File["file"][0], "test"))
 }
 
 func TestSaveUploadedOpenFailed(t *testing.T) {
@@ -130,33 +130,70 @@ func TestSaveUploadedOpenFailed(t *testing.T) {
 	mw.Close()
 
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", buf)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", buf)
 	c.Request.Header.Set("Content-Type", mw.FormDataContentType())
 
 	f := &multipart.FileHeader{
 		Filename: "file",
 	}
-	assert.Error(t, c.SaveUploadedFile(f, "test"))
+	require.Error(t, c.SaveUploadedFile(f, "test"))
 }
 
 func TestSaveUploadedCreateFailed(t *testing.T) {
 	buf := new(bytes.Buffer)
 	mw := multipart.NewWriter(buf)
 	w, err := mw.CreateFormFile("file", "test")
-	if assert.NoError(t, err) {
-		_, err = w.Write([]byte("test"))
-		assert.NoError(t, err)
-	}
+	require.NoError(t, err)
+	_, err = w.Write([]byte("test"))
+	require.NoError(t, err)
 	mw.Close()
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", buf)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", buf)
 	c.Request.Header.Set("Content-Type", mw.FormDataContentType())
 	f, err := c.FormFile("file")
-	if assert.NoError(t, err) {
-		assert.Equal(t, "test", f.Filename)
-	}
+	require.NoError(t, err)
+	assert.Equal(t, "test", f.Filename)
 
-	assert.Error(t, c.SaveUploadedFile(f, "/"))
+	require.Error(t, c.SaveUploadedFile(f, "/"))
+}
+
+func TestSaveUploadedFileWithPermission(t *testing.T) {
+	buf := new(bytes.Buffer)
+	mw := multipart.NewWriter(buf)
+	w, err := mw.CreateFormFile("file", "permission_test")
+	require.NoError(t, err)
+	_, err = w.Write([]byte("permission_test"))
+	require.NoError(t, err)
+	mw.Close()
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", buf)
+	c.Request.Header.Set("Content-Type", mw.FormDataContentType())
+	f, err := c.FormFile("file")
+	require.NoError(t, err)
+	assert.Equal(t, "permission_test", f.Filename)
+	var mode fs.FileMode = 0o755
+	require.NoError(t, c.SaveUploadedFile(f, "permission_test", mode))
+	info, err := os.Stat(filepath.Dir("permission_test"))
+	require.NoError(t, err)
+	assert.Equal(t, info.Mode().Perm(), mode)
+}
+
+func TestSaveUploadedFileWithPermissionFailed(t *testing.T) {
+	buf := new(bytes.Buffer)
+	mw := multipart.NewWriter(buf)
+	w, err := mw.CreateFormFile("file", "permission_test")
+	require.NoError(t, err)
+	_, err = w.Write([]byte("permission_test"))
+	require.NoError(t, err)
+	mw.Close()
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", buf)
+	c.Request.Header.Set("Content-Type", mw.FormDataContentType())
+	f, err := c.FormFile("file")
+	require.NoError(t, err)
+	assert.Equal(t, "permission_test", f.Filename)
+	var mode fs.FileMode = 0o644
+	require.Error(t, c.SaveUploadedFile(f, "test/permission_test", mode))
 }
 
 func TestContextReset(t *testing.T) {
@@ -174,10 +211,10 @@ func TestContextReset(t *testing.T) {
 	assert.False(t, c.IsAborted())
 	assert.Nil(t, c.Keys)
 	assert.Nil(t, c.Accepted)
-	assert.Len(t, c.Errors, 0)
+	assert.Empty(t, c.Errors)
 	assert.Empty(t, c.Errors.Errors())
 	assert.Empty(t, c.Errors.ByType(ErrorTypeAny))
-	assert.Len(t, c.Params, 0)
+	assert.Empty(t, c.Params)
 	assert.EqualValues(t, c.index, -1)
 	assert.Equal(t, c.Writer.(*responseWriter), &c.writermem)
 }
@@ -230,13 +267,13 @@ func TestContextSetGetValues(t *testing.T) {
 	var a any = 1
 	c.Set("intInterface", a)
 
-	assert.Exactly(t, c.MustGet("string").(string), "this is a string")
+	assert.Exactly(t, "this is a string", c.MustGet("string").(string))
 	assert.Exactly(t, c.MustGet("int32").(int32), int32(-42))
-	assert.Exactly(t, c.MustGet("int64").(int64), int64(42424242424242))
-	assert.Exactly(t, c.MustGet("uint64").(uint64), uint64(42))
-	assert.Exactly(t, c.MustGet("float32").(float32), float32(4.2))
-	assert.Exactly(t, c.MustGet("float64").(float64), 4.2)
-	assert.Exactly(t, c.MustGet("intInterface").(int), 1)
+	assert.Exactly(t, int64(42424242424242), c.MustGet("int64").(int64))
+	assert.Exactly(t, uint64(42), c.MustGet("uint64").(uint64))
+	assert.InDelta(t, float32(4.2), c.MustGet("float32").(float32), 0.01)
+	assert.InDelta(t, 4.2, c.MustGet("float64").(float64), 0.01)
+	assert.Exactly(t, 1, c.MustGet("intInterface").(int))
 }
 
 func TestContextGetString(t *testing.T) {
@@ -257,6 +294,30 @@ func TestContextGetInt(t *testing.T) {
 	assert.Equal(t, 1, c.GetInt("int"))
 }
 
+func TestContextGetInt8(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "int8"
+	value := int8(0x7F)
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetInt8(key))
+}
+
+func TestContextGetInt16(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "int16"
+	value := int16(0x7FFF)
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetInt16(key))
+}
+
+func TestContextGetInt32(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "int32"
+	value := int32(0x7FFFFFFF)
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetInt32(key))
+}
+
 func TestContextGetInt64(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
 	c.Set("int64", int64(42424242424242))
@@ -269,16 +330,48 @@ func TestContextGetUint(t *testing.T) {
 	assert.Equal(t, uint(1), c.GetUint("uint"))
 }
 
+func TestContextGetUint8(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "uint8"
+	value := uint8(0xFF)
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetUint8(key))
+}
+
+func TestContextGetUint16(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "uint16"
+	value := uint16(0xFFFF)
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetUint16(key))
+}
+
+func TestContextGetUint32(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "uint32"
+	value := uint32(0xFFFFFFFF)
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetUint32(key))
+}
+
 func TestContextGetUint64(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
 	c.Set("uint64", uint64(18446744073709551615))
 	assert.Equal(t, uint64(18446744073709551615), c.GetUint64("uint64"))
 }
 
+func TestContextGetFloat32(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "float32"
+	value := float32(3.14)
+	c.Set(key, value)
+	assert.InDelta(t, value, c.GetFloat32(key), 0.01)
+}
+
 func TestContextGetFloat64(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
 	c.Set("float64", 4.2)
-	assert.Equal(t, 4.2, c.GetFloat64("float64"))
+	assert.InDelta(t, 4.2, c.GetFloat64("float64"), 0.01)
 }
 
 func TestContextGetTime(t *testing.T) {
@@ -294,6 +387,102 @@ func TestContextGetDuration(t *testing.T) {
 	assert.Equal(t, time.Second, c.GetDuration("duration"))
 }
 
+func TestContextGetIntSlice(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "int-slice"
+	value := []int{1, 2}
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetIntSlice(key))
+}
+
+func TestContextGetInt8Slice(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "int8-slice"
+	value := []int8{1, 2}
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetInt8Slice(key))
+}
+
+func TestContextGetInt16Slice(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "int16-slice"
+	value := []int16{1, 2}
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetInt16Slice(key))
+}
+
+func TestContextGetInt32Slice(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "int32-slice"
+	value := []int32{1, 2}
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetInt32Slice(key))
+}
+
+func TestContextGetInt64Slice(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "int64-slice"
+	value := []int64{1, 2}
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetInt64Slice(key))
+}
+
+func TestContextGetUintSlice(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "uint-slice"
+	value := []uint{1, 2}
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetUintSlice(key))
+}
+
+func TestContextGetUint8Slice(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "uint8-slice"
+	value := []uint8{1, 2}
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetUint8Slice(key))
+}
+
+func TestContextGetUint16Slice(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "uint16-slice"
+	value := []uint16{1, 2}
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetUint16Slice(key))
+}
+
+func TestContextGetUint32Slice(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "uint32-slice"
+	value := []uint32{1, 2}
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetUint32Slice(key))
+}
+
+func TestContextGetUint64Slice(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "uint64-slice"
+	value := []uint64{1, 2}
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetUint64Slice(key))
+}
+
+func TestContextGetFloat32Slice(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "float32-slice"
+	value := []float32{1, 2}
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetFloat32Slice(key))
+}
+
+func TestContextGetFloat64Slice(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+	key := "float64-slice"
+	value := []float64{1, 2}
+	c.Set(key, value)
+	assert.Equal(t, value, c.GetFloat64Slice(key))
+}
+
 func TestContextGetStringSlice(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
 	c.Set("slice", []string{"foo"})
@@ -333,7 +522,7 @@ func TestContextGetStringMapStringSlice(t *testing.T) {
 func TestContextCopy(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
 	c.index = 2
-	c.Request, _ = http.NewRequest("POST", "/hola", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/hola", nil)
 	c.handlers = HandlersChain{func(c *Context) {}}
 	c.Params = Params{Param{Key: "foo", Value: "bar"}}
 	c.Set("foo", "bar")
@@ -344,12 +533,12 @@ func TestContextCopy(t *testing.T) {
 	assert.Nil(t, cp.writermem.ResponseWriter)
 	assert.Equal(t, &cp.writermem, cp.Writer.(*responseWriter))
 	assert.Equal(t, cp.Request, c.Request)
-	assert.Equal(t, cp.index, abortIndex)
+	assert.Equal(t, abortIndex, cp.index)
 	assert.Equal(t, cp.Keys, c.Keys)
 	assert.Equal(t, cp.engine, c.engine)
 	assert.Equal(t, cp.Params, c.Params)
 	cp.Set("foo", "notBar")
-	assert.False(t, cp.Keys["foo"] == c.Keys["foo"])
+	assert.NotEqual(t, cp.Keys["foo"], c.Keys["foo"])
 	assert.Equal(t, cp.fullPath, c.fullPath)
 }
 
@@ -366,7 +555,7 @@ func TestContextHandlerNames(t *testing.T) {
 
 	names := c.HandlerNames()
 
-	assert.True(t, len(names) == 4)
+	assert.Len(t, names, 4)
 	for _, name := range names {
 		assert.Regexp(t, `^(.*/vendor/)?(github\.com/gin-gonic/gin\.){1}(TestContextHandlerNames\.func.*){0,1}(handlerNameTest.*){0,1}`, name)
 	}
@@ -390,7 +579,7 @@ func TestContextHandler(t *testing.T) {
 
 func TestContextQuery(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10&id=", nil)
+	c.Request, _ = http.NewRequest(http.MethodGet, "http://example.com/?foo=bar&page=10&id=", nil)
 
 	value, ok := c.GetQuery("foo")
 	assert.True(t, ok)
@@ -425,7 +614,7 @@ func TestContextQuery(t *testing.T) {
 
 func TestContextInitQueryCache(t *testing.T) {
 	validURL, err := url.Parse("https://github.com/gin-gonic/gin/pull/3969?key=value&otherkey=othervalue")
-	assert.Nil(t, err)
+	require.NoError(t, err)
 
 	tests := []struct {
 		testName           string
@@ -463,7 +652,6 @@ func TestContextInitQueryCache(t *testing.T) {
 			assert.Equal(t, test.expectedQueryCache, test.testContext.queryCache)
 		})
 	}
-
 }
 
 func TestContextDefaultQueryOnEmptyRequest(t *testing.T) {
@@ -484,7 +672,7 @@ func TestContextDefaultQueryOnEmptyRequest(t *testing.T) {
 func TestContextQueryAndPostForm(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
 	body := bytes.NewBufferString("foo=bar&page=11&both=&foo=second")
-	c.Request, _ = http.NewRequest("POST",
+	c.Request, _ = http.NewRequest(http.MethodPost,
 		"/?both=GET&id=main&id=omit&array[]=first&array[]=second&ids[a]=hi&ids[b]=3.14", body)
 	c.Request.Header.Add("Content-Type", MIMEPOSTForm)
 
@@ -504,7 +692,7 @@ func TestContextQueryAndPostForm(t *testing.T) {
 	assert.Empty(t, value)
 	assert.Empty(t, c.PostForm("both"))
 	assert.Empty(t, c.DefaultPostForm("both", "nothing"))
-	assert.Equal(t, "GET", c.Query("both"), "GET")
+	assert.Equal(t, http.MethodGet, c.Query("both"), http.MethodGet)
 
 	value, ok = c.GetQuery("id")
 	assert.True(t, ok)
@@ -531,7 +719,7 @@ func TestContextQueryAndPostForm(t *testing.T) {
 		Both  string   `form:"both"`
 		Array []string `form:"array[]"`
 	}
-	assert.NoError(t, c.Bind(&obj))
+	require.NoError(t, c.Bind(&obj))
 	assert.Equal(t, "bar", obj.Foo, "bar")
 	assert.Equal(t, "main", obj.ID, "main")
 	assert.Equal(t, 11, obj.Page, 11)
@@ -548,11 +736,11 @@ func TestContextQueryAndPostForm(t *testing.T) {
 	assert.Equal(t, "second", values[1])
 
 	values = c.QueryArray("nokey")
-	assert.Equal(t, 0, len(values))
+	assert.Empty(t, values)
 
 	values = c.QueryArray("both")
-	assert.Equal(t, 1, len(values))
-	assert.Equal(t, "GET", values[0])
+	assert.Len(t, values, 1)
+	assert.Equal(t, http.MethodGet, values[0])
 
 	dicts, ok := c.GetQueryMap("ids")
 	assert.True(t, ok)
@@ -561,22 +749,22 @@ func TestContextQueryAndPostForm(t *testing.T) {
 
 	dicts, ok = c.GetQueryMap("nokey")
 	assert.False(t, ok)
-	assert.Equal(t, 0, len(dicts))
+	assert.Empty(t, dicts)
 
 	dicts, ok = c.GetQueryMap("both")
 	assert.False(t, ok)
-	assert.Equal(t, 0, len(dicts))
+	assert.Empty(t, dicts)
 
 	dicts, ok = c.GetQueryMap("array")
 	assert.False(t, ok)
-	assert.Equal(t, 0, len(dicts))
+	assert.Empty(t, dicts)
 
 	dicts = c.QueryMap("ids")
 	assert.Equal(t, "hi", dicts["a"])
 	assert.Equal(t, "3.14", dicts["b"])
 
 	dicts = c.QueryMap("nokey")
-	assert.Equal(t, 0, len(dicts))
+	assert.Empty(t, dicts)
 }
 
 func TestContextPostFormMultipart(t *testing.T) {
@@ -594,7 +782,7 @@ func TestContextPostFormMultipart(t *testing.T) {
 		TimeLocation time.Time `form:"time_location" time_format:"02/01/2006 15:04" time_location:"Asia/Tokyo"`
 		BlankTime    time.Time `form:"blank_time" time_format:"02/01/2006 15:04"`
 	}
-	assert.NoError(t, c.Bind(&obj))
+	require.NoError(t, c.Bind(&obj))
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, "10", obj.Bar)
 	assert.Equal(t, 10, obj.BarAsInt)
@@ -648,10 +836,10 @@ func TestContextPostFormMultipart(t *testing.T) {
 	assert.Equal(t, "second", values[1])
 
 	values = c.PostFormArray("nokey")
-	assert.Equal(t, 0, len(values))
+	assert.Empty(t, values)
 
 	values = c.PostFormArray("foo")
-	assert.Equal(t, 1, len(values))
+	assert.Len(t, values, 1)
 	assert.Equal(t, "bar", values[0])
 
 	dicts, ok := c.GetPostFormMap("names")
@@ -661,14 +849,14 @@ func TestContextPostFormMultipart(t *testing.T) {
 
 	dicts, ok = c.GetPostFormMap("nokey")
 	assert.False(t, ok)
-	assert.Equal(t, 0, len(dicts))
+	assert.Empty(t, dicts)
 
 	dicts = c.PostFormMap("names")
 	assert.Equal(t, "thinkerou", dicts["a"])
 	assert.Equal(t, "tianou", dicts["b"])
 
 	dicts = c.PostFormMap("nokey")
-	assert.Equal(t, 0, len(dicts))
+	assert.Empty(t, dicts)
 }
 
 func TestContextSetCookie(t *testing.T) {
@@ -687,13 +875,13 @@ func TestContextSetCookiePathEmpty(t *testing.T) {
 
 func TestContextGetCookie(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("GET", "/get", nil)
+	c.Request, _ = http.NewRequest(http.MethodGet, "/get", nil)
 	c.Request.Header.Set("Cookie", "user=gin")
 	cookie, _ := c.Cookie("user")
 	assert.Equal(t, "gin", cookie)
 
 	_, err := c.Cookie("nokey")
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestContextBodyAllowedForStatus(t *testing.T) {
@@ -739,7 +927,7 @@ func TestContextRenderJSON(t *testing.T) {
 func TestContextRenderJSONP(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
-	c.Request, _ = http.NewRequest("GET", "http://example.com/?callback=x", nil)
+	c.Request, _ = http.NewRequest(http.MethodGet, "http://example.com/?callback=x", nil)
 
 	c.JSONP(http.StatusCreated, H{"foo": "bar"})
 
@@ -753,7 +941,7 @@ func TestContextRenderJSONP(t *testing.T) {
 func TestContextRenderJSONPWithoutCallback(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
-	c.Request, _ = http.NewRequest("GET", "http://example.com", nil)
+	c.Request, _ = http.NewRequest(http.MethodGet, "http://example.com", nil)
 
 	c.JSONP(http.StatusCreated, H{"foo": "bar"})
 
@@ -798,7 +986,7 @@ func TestContextRenderNoContentAPIJSON(t *testing.T) {
 
 	assert.Equal(t, http.StatusNoContent, w.Code)
 	assert.Empty(t, w.Body.String())
-	assert.Equal(t, w.Header().Get("Content-Type"), "application/vnd.api+json")
+	assert.Equal(t, "application/vnd.api+json", w.Header().Get("Content-Type"))
 }
 
 // Tests that the response is serialized as JSON
@@ -896,7 +1084,7 @@ func TestContextRenderHTML2(t *testing.T) {
 	c, router := CreateTestContext(w)
 
 	// print debug warning log when Engine.trees > 0
-	router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})
+	router.addRoute(http.MethodGet, "/", HandlersChain{func(_ *Context) {}})
 	assert.Len(t, router.trees, 1)
 
 	templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
@@ -1006,7 +1194,7 @@ func TestContextRenderNoContentHTMLString(t *testing.T) {
 	assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
 }
 
-// TestContextData tests that the response can be written from `bytestring`
+// TestContextRenderData tests that the response can be written from `bytestring`
 // with specified MIME type
 func TestContextRenderData(t *testing.T) {
 	w := httptest.NewRecorder()
@@ -1052,7 +1240,7 @@ func TestContextRenderFile(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("GET", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
 	c.File("./gin.go")
 
 	assert.Equal(t, http.StatusOK, w.Code)
@@ -1066,7 +1254,7 @@ func TestContextRenderFileFromFS(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("GET", "/some/path", nil)
+	c.Request, _ = http.NewRequest(http.MethodGet, "/some/path", nil)
 	c.FileFromFS("./gin.go", Dir(".", false))
 
 	assert.Equal(t, http.StatusOK, w.Code)
@@ -1082,7 +1270,7 @@ func TestContextRenderAttachment(t *testing.T) {
 	c, _ := CreateTestContext(w)
 	newFilename := "new_filename.go"
 
-	c.Request, _ = http.NewRequest("GET", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
 	c.FileAttachment("./gin.go", newFilename)
 
 	assert.Equal(t, 200, w.Code)
@@ -1096,7 +1284,7 @@ func TestContextRenderAndEscapeAttachment(t *testing.T) {
 	maliciousFilename := "tampering_field.sh\"; \\\"; dummy=.go"
 	actualEscapedResponseFilename := "tampering_field.sh\\\"; \\\\\\\"; dummy=.go"
 
-	c.Request, _ = http.NewRequest("GET", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
 	c.FileAttachment("./gin.go", maliciousFilename)
 
 	assert.Equal(t, 200, w.Code)
@@ -1109,7 +1297,7 @@ func TestContextRenderUTF8Attachment(t *testing.T) {
 	c, _ := CreateTestContext(w)
 	newFilename := "new🧡_filename.go"
 
-	c.Request, _ = http.NewRequest("GET", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodGet, "/", nil)
 	c.FileAttachment("./gin.go", newFilename)
 
 	assert.Equal(t, 200, w.Code)
@@ -1160,7 +1348,7 @@ func TestContextRenderProtoBuf(t *testing.T) {
 	c.ProtoBuf(http.StatusCreated, data)
 
 	protoData, err := proto.Marshal(data)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	assert.Equal(t, http.StatusCreated, w.Code)
 	assert.Equal(t, string(protoData), w.Body.String())
@@ -1188,7 +1376,7 @@ func TestContextRenderRedirectWithRelativePath(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", nil)
 	assert.Panics(t, func() { c.Redirect(299, "/new_path") })
 	assert.Panics(t, func() { c.Redirect(309, "/new_path") })
 
@@ -1202,7 +1390,7 @@ func TestContextRenderRedirectWithAbsolutePath(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", nil)
 	c.Redirect(http.StatusFound, "http://google.com")
 	c.Writer.WriteHeaderNow()
 
@@ -1214,7 +1402,7 @@ func TestContextRenderRedirectWith201(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", nil)
 	c.Redirect(http.StatusCreated, "/resource")
 	c.Writer.WriteHeaderNow()
 
@@ -1224,7 +1412,7 @@ func TestContextRenderRedirectWith201(t *testing.T) {
 
 func TestContextRenderRedirectAll(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", nil)
 	assert.Panics(t, func() { c.Redirect(http.StatusOK, "/resource") })
 	assert.Panics(t, func() { c.Redirect(http.StatusAccepted, "/resource") })
 	assert.Panics(t, func() { c.Redirect(299, "/resource") })
@@ -1236,7 +1424,7 @@ func TestContextRenderRedirectAll(t *testing.T) {
 func TestContextNegotiationWithJSON(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
-	c.Request, _ = http.NewRequest("POST", "", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
 
 	c.Negotiate(http.StatusOK, Negotiate{
 		Offered: []string{MIMEJSON, MIMEXML, MIMEYAML, MIMEYAML2},
@@ -1251,7 +1439,7 @@ func TestContextNegotiationWithJSON(t *testing.T) {
 func TestContextNegotiationWithXML(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
-	c.Request, _ = http.NewRequest("POST", "", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
 
 	c.Negotiate(http.StatusOK, Negotiate{
 		Offered: []string{MIMEXML, MIMEJSON, MIMEYAML, MIMEYAML2},
@@ -1266,7 +1454,7 @@ func TestContextNegotiationWithXML(t *testing.T) {
 func TestContextNegotiationWithYAML(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
-	c.Request, _ = http.NewRequest("POST", "", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
 
 	c.Negotiate(http.StatusOK, Negotiate{
 		Offered: []string{MIMEYAML, MIMEXML, MIMEJSON, MIMETOML, MIMEYAML2},
@@ -1281,7 +1469,7 @@ func TestContextNegotiationWithYAML(t *testing.T) {
 func TestContextNegotiationWithTOML(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
-	c.Request, _ = http.NewRequest("POST", "", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
 
 	c.Negotiate(http.StatusOK, Negotiate{
 		Offered: []string{MIMETOML, MIMEXML, MIMEJSON, MIMEYAML, MIMEYAML2},
@@ -1296,7 +1484,7 @@ func TestContextNegotiationWithTOML(t *testing.T) {
 func TestContextNegotiationWithHTML(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, router := CreateTestContext(w)
-	c.Request, _ = http.NewRequest("POST", "", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
 	templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
 	router.SetHTMLTemplate(templ)
 
@@ -1314,20 +1502,20 @@ func TestContextNegotiationWithHTML(t *testing.T) {
 func TestContextNegotiationNotSupport(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
-	c.Request, _ = http.NewRequest("POST", "", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
 
 	c.Negotiate(http.StatusOK, Negotiate{
 		Offered: []string{MIMEPOSTForm},
 	})
 
 	assert.Equal(t, http.StatusNotAcceptable, w.Code)
-	assert.Equal(t, c.index, abortIndex)
+	assert.Equal(t, abortIndex, c.index)
 	assert.True(t, c.IsAborted())
 }
 
 func TestContextNegotiationFormat(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "", nil)
 
 	assert.Panics(t, func() { c.NegotiateFormat() })
 	assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
@@ -1336,7 +1524,7 @@ func TestContextNegotiationFormat(t *testing.T) {
 
 func TestContextNegotiationFormatWithAccept(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 	c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8")
 
 	assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEJSON, MIMEXML))
@@ -1346,31 +1534,31 @@ func TestContextNegotiationFormatWithAccept(t *testing.T) {
 
 func TestContextNegotiationFormatWithWildcardAccept(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 	c.Request.Header.Add("Accept", "*/*")
 
-	assert.Equal(t, c.NegotiateFormat("*/*"), "*/*")
-	assert.Equal(t, c.NegotiateFormat("text/*"), "text/*")
-	assert.Equal(t, c.NegotiateFormat("application/*"), "application/*")
-	assert.Equal(t, c.NegotiateFormat(MIMEJSON), MIMEJSON)
-	assert.Equal(t, c.NegotiateFormat(MIMEXML), MIMEXML)
-	assert.Equal(t, c.NegotiateFormat(MIMEHTML), MIMEHTML)
+	assert.Equal(t, "*/*", c.NegotiateFormat("*/*"))
+	assert.Equal(t, "text/*", c.NegotiateFormat("text/*"))
+	assert.Equal(t, "application/*", c.NegotiateFormat("application/*"))
+	assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON))
+	assert.Equal(t, MIMEXML, c.NegotiateFormat(MIMEXML))
+	assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML))
 
 	c, _ = CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 	c.Request.Header.Add("Accept", "text/*")
 
-	assert.Equal(t, c.NegotiateFormat("*/*"), "*/*")
-	assert.Equal(t, c.NegotiateFormat("text/*"), "text/*")
-	assert.Equal(t, c.NegotiateFormat("application/*"), "")
-	assert.Equal(t, c.NegotiateFormat(MIMEJSON), "")
-	assert.Equal(t, c.NegotiateFormat(MIMEXML), "")
-	assert.Equal(t, c.NegotiateFormat(MIMEHTML), MIMEHTML)
+	assert.Equal(t, "*/*", c.NegotiateFormat("*/*"))
+	assert.Equal(t, "text/*", c.NegotiateFormat("text/*"))
+	assert.Equal(t, "", c.NegotiateFormat("application/*"))
+	assert.Equal(t, "", c.NegotiateFormat(MIMEJSON))
+	assert.Equal(t, "", c.NegotiateFormat(MIMEXML))
+	assert.Equal(t, MIMEHTML, c.NegotiateFormat(MIMEHTML))
 }
 
 func TestContextNegotiationFormatCustom(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 	c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8")
 
 	c.Accepted = nil
@@ -1383,7 +1571,7 @@ func TestContextNegotiationFormatCustom(t *testing.T) {
 
 func TestContextNegotiationFormat2(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 	c.Request.Header.Add("Accept", "image/tiff-fx")
 
 	assert.Equal(t, "", c.NegotiateFormat("image/tiff"))
@@ -1403,7 +1591,7 @@ func TestContextIsAborted(t *testing.T) {
 	assert.True(t, c.IsAborted())
 }
 
-// TestContextData tests that the response can be written from `bytestring`
+// TestContextAbortWithStatus tests that the response can be written from `bytestring`
 // with specified MIME type
 func TestContextAbortWithStatus(t *testing.T) {
 	w := httptest.NewRecorder()
@@ -1444,7 +1632,7 @@ func TestContextAbortWithStatusJSON(t *testing.T) {
 
 	buf := new(bytes.Buffer)
 	_, err := buf.ReadFrom(w.Body)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	jsonStringBody := buf.String()
 	assert.Equal(t, "{\"foo\":\"fooValue\",\"bar\":\"barValue\"}", jsonStringBody)
 }
@@ -1511,7 +1699,7 @@ func TestContextAbortWithError(t *testing.T) {
 
 func TestContextClientIP(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 	c.engine.trustedCIDRs, _ = c.engine.prepareTrustedCIDRs()
 	resetContextForClientIPTests(c)
 
@@ -1654,7 +1842,7 @@ func resetContextForClientIPTests(c *Context) {
 
 func TestContextContentType(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 	c.Request.Header.Set("Content-Type", "application/json; charset=utf-8")
 
 	assert.Equal(t, "application/json", c.ContentType())
@@ -1662,14 +1850,14 @@ func TestContextContentType(t *testing.T) {
 
 func TestContextAutoBindJSON(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
 	c.Request.Header.Add("Content-Type", MIMEJSON)
 
 	var obj struct {
 		Foo string `json:"foo"`
 		Bar string `json:"bar"`
 	}
-	assert.NoError(t, c.Bind(&obj))
+	require.NoError(t, c.Bind(&obj))
 	assert.Equal(t, "foo", obj.Bar)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Empty(t, c.Errors)
@@ -1679,14 +1867,14 @@ func TestContextBindWithJSON(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
 	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
 
 	var obj struct {
 		Foo string `json:"foo"`
 		Bar string `json:"bar"`
 	}
-	assert.NoError(t, c.BindJSON(&obj))
+	require.NoError(t, c.BindJSON(&obj))
 	assert.Equal(t, "foo", obj.Bar)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, 0, w.Body.Len())
@@ -1696,7 +1884,7 @@ func TestContextBindWithXML(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?>
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?>
 		<root>
 			<foo>FOO</foo>
 		   	<bar>BAR</bar>
@@ -1707,7 +1895,7 @@ func TestContextBindWithXML(t *testing.T) {
 		Foo string `xml:"foo"`
 		Bar string `xml:"bar"`
 	}
-	assert.NoError(t, c.BindXML(&obj))
+	require.NoError(t, c.BindXML(&obj))
 	assert.Equal(t, "FOO", obj.Foo)
 	assert.Equal(t, "BAR", obj.Bar)
 	assert.Equal(t, 0, w.Body.Len())
@@ -1717,22 +1905,22 @@ func TestContextBindPlain(t *testing.T) {
 	// string
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test string`))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`test string`))
 	c.Request.Header.Add("Content-Type", MIMEPlain)
 
 	var s string
 
-	assert.NoError(t, c.BindPlain(&s))
+	require.NoError(t, c.BindPlain(&s))
 	assert.Equal(t, "test string", s)
 	assert.Equal(t, 0, w.Body.Len())
 
 	// []byte
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test []byte`))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`test []byte`))
 	c.Request.Header.Add("Content-Type", MIMEPlain)
 
 	var bs []byte
 
-	assert.NoError(t, c.BindPlain(&bs))
+	require.NoError(t, c.BindPlain(&bs))
 	assert.Equal(t, []byte("test []byte"), bs)
 	assert.Equal(t, 0, w.Body.Len())
 }
@@ -1741,7 +1929,7 @@ func TestContextBindHeader(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 	c.Request.Header.Add("rate", "8000")
 	c.Request.Header.Add("domain", "music")
 	c.Request.Header.Add("limit", "1000")
@@ -1752,7 +1940,7 @@ func TestContextBindHeader(t *testing.T) {
 		Limit  int    `header:"limit"`
 	}
 
-	assert.NoError(t, c.BindHeader(&testHeader))
+	require.NoError(t, c.BindHeader(&testHeader))
 	assert.Equal(t, 8000, testHeader.Rate)
 	assert.Equal(t, "music", testHeader.Domain)
 	assert.Equal(t, 1000, testHeader.Limit)
@@ -1763,13 +1951,13 @@ func TestContextBindWithQuery(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused"))
 
 	var obj struct {
 		Foo string `form:"foo"`
 		Bar string `form:"bar"`
 	}
-	assert.NoError(t, c.BindQuery(&obj))
+	require.NoError(t, c.BindQuery(&obj))
 	assert.Equal(t, "foo", obj.Bar)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, 0, w.Body.Len())
@@ -1779,14 +1967,14 @@ func TestContextBindWithYAML(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("foo: bar\nbar: foo"))
 	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
 
 	var obj struct {
 		Foo string `yaml:"foo"`
 		Bar string `yaml:"bar"`
 	}
-	assert.NoError(t, c.BindYAML(&obj))
+	require.NoError(t, c.BindYAML(&obj))
 	assert.Equal(t, "foo", obj.Bar)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, 0, w.Body.Len())
@@ -1796,14 +1984,14 @@ func TestContextBindWithTOML(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo = 'bar'\nbar = 'foo'"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("foo = 'bar'\nbar = 'foo'"))
 	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
 
 	var obj struct {
 		Foo string `toml:"foo"`
 		Bar string `toml:"bar"`
 	}
-	assert.NoError(t, c.BindTOML(&obj))
+	require.NoError(t, c.BindTOML(&obj))
 	assert.Equal(t, "foo", obj.Bar)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, 0, w.Body.Len())
@@ -1813,7 +2001,7 @@ func TestContextBadAutoBind(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
 	c.Request.Header.Add("Content-Type", MIMEJSON)
 	var obj struct {
 		Foo string `json:"foo"`
@@ -1821,7 +2009,7 @@ func TestContextBadAutoBind(t *testing.T) {
 	}
 
 	assert.False(t, c.IsAborted())
-	assert.Error(t, c.Bind(&obj))
+	require.Error(t, c.Bind(&obj))
 	c.Writer.WriteHeaderNow()
 
 	assert.Empty(t, obj.Bar)
@@ -1832,14 +2020,14 @@ func TestContextBadAutoBind(t *testing.T) {
 
 func TestContextAutoShouldBindJSON(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
 	c.Request.Header.Add("Content-Type", MIMEJSON)
 
 	var obj struct {
 		Foo string `json:"foo"`
 		Bar string `json:"bar"`
 	}
-	assert.NoError(t, c.ShouldBind(&obj))
+	require.NoError(t, c.ShouldBind(&obj))
 	assert.Equal(t, "foo", obj.Bar)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Empty(t, c.Errors)
@@ -1849,14 +2037,14 @@ func TestContextShouldBindWithJSON(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
 	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
 
 	var obj struct {
 		Foo string `json:"foo"`
 		Bar string `json:"bar"`
 	}
-	assert.NoError(t, c.ShouldBindJSON(&obj))
+	require.NoError(t, c.ShouldBindJSON(&obj))
 	assert.Equal(t, "foo", obj.Bar)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, 0, w.Body.Len())
@@ -1866,7 +2054,7 @@ func TestContextShouldBindWithXML(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?>
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`<?xml version="1.0" encoding="UTF-8"?>
 		<root>
 			<foo>FOO</foo>
 			<bar>BAR</bar>
@@ -1877,7 +2065,7 @@ func TestContextShouldBindWithXML(t *testing.T) {
 		Foo string `xml:"foo"`
 		Bar string `xml:"bar"`
 	}
-	assert.NoError(t, c.ShouldBindXML(&obj))
+	require.NoError(t, c.ShouldBindXML(&obj))
 	assert.Equal(t, "FOO", obj.Foo)
 	assert.Equal(t, "BAR", obj.Bar)
 	assert.Equal(t, 0, w.Body.Len())
@@ -1887,22 +2075,22 @@ func TestContextShouldBindPlain(t *testing.T) {
 	// string
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test string`))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`test string`))
 	c.Request.Header.Add("Content-Type", MIMEPlain)
 
 	var s string
 
-	assert.NoError(t, c.ShouldBindPlain(&s))
+	require.NoError(t, c.ShouldBindPlain(&s))
 	assert.Equal(t, "test string", s)
 	assert.Equal(t, 0, w.Body.Len())
 	// []byte
 
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test []byte`))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(`test []byte`))
 	c.Request.Header.Add("Content-Type", MIMEPlain)
 
 	var bs []byte
 
-	assert.NoError(t, c.ShouldBindPlain(&bs))
+	require.NoError(t, c.ShouldBindPlain(&bs))
 	assert.Equal(t, []byte("test []byte"), bs)
 	assert.Equal(t, 0, w.Body.Len())
 }
@@ -1911,7 +2099,7 @@ func TestContextShouldBindHeader(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 	c.Request.Header.Add("rate", "8000")
 	c.Request.Header.Add("domain", "music")
 	c.Request.Header.Add("limit", "1000")
@@ -1922,7 +2110,7 @@ func TestContextShouldBindHeader(t *testing.T) {
 		Limit  int    `header:"limit"`
 	}
 
-	assert.NoError(t, c.ShouldBindHeader(&testHeader))
+	require.NoError(t, c.ShouldBindHeader(&testHeader))
 	assert.Equal(t, 8000, testHeader.Rate)
 	assert.Equal(t, "music", testHeader.Domain)
 	assert.Equal(t, 1000, testHeader.Limit)
@@ -1933,7 +2121,7 @@ func TestContextShouldBindWithQuery(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo&Foo=bar1&Bar=foo1", bytes.NewBufferString("foo=unused"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/?foo=bar&bar=foo&Foo=bar1&Bar=foo1", bytes.NewBufferString("foo=unused"))
 
 	var obj struct {
 		Foo  string `form:"foo"`
@@ -1941,7 +2129,7 @@ func TestContextShouldBindWithQuery(t *testing.T) {
 		Foo1 string `form:"Foo"`
 		Bar1 string `form:"Bar"`
 	}
-	assert.NoError(t, c.ShouldBindQuery(&obj))
+	require.NoError(t, c.ShouldBindQuery(&obj))
 	assert.Equal(t, "foo", obj.Bar)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, "foo1", obj.Bar1)
@@ -1953,14 +2141,14 @@ func TestContextShouldBindWithYAML(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo: bar\nbar: foo"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("foo: bar\nbar: foo"))
 	c.Request.Header.Add("Content-Type", MIMEXML) // set fake content-type
 
 	var obj struct {
 		Foo string `yaml:"foo"`
 		Bar string `yaml:"bar"`
 	}
-	assert.NoError(t, c.ShouldBindYAML(&obj))
+	require.NoError(t, c.ShouldBindYAML(&obj))
 	assert.Equal(t, "foo", obj.Bar)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, 0, w.Body.Len())
@@ -1970,14 +2158,14 @@ func TestContextShouldBindWithTOML(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("foo='bar'\nbar= 'foo'"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("foo='bar'\nbar= 'foo'"))
 	c.Request.Header.Add("Content-Type", MIMETOML) // set fake content-type
 
 	var obj struct {
 		Foo string `toml:"foo"`
 		Bar string `toml:"bar"`
 	}
-	assert.NoError(t, c.ShouldBindTOML(&obj))
+	require.NoError(t, c.ShouldBindTOML(&obj))
 	assert.Equal(t, "foo", obj.Bar)
 	assert.Equal(t, "bar", obj.Foo)
 	assert.Equal(t, 0, w.Body.Len())
@@ -1987,7 +2175,7 @@ func TestContextBadAutoShouldBind(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
 	c.Request.Header.Add("Content-Type", MIMEJSON)
 	var obj struct {
 		Foo string `json:"foo"`
@@ -1995,7 +2183,7 @@ func TestContextBadAutoShouldBind(t *testing.T) {
 	}
 
 	assert.False(t, c.IsAborted())
-	assert.Error(t, c.ShouldBind(&obj))
+	require.Error(t, c.ShouldBind(&obj))
 
 	assert.Empty(t, obj.Bar)
 	assert.Empty(t, obj.Foo)
@@ -2051,15 +2239,15 @@ func TestContextShouldBindBodyWith(t *testing.T) {
 			w := httptest.NewRecorder()
 			c, _ := CreateTestContext(w)
 			c.Request, _ = http.NewRequest(
-				"POST", "http://example.com", bytes.NewBufferString(tt.bodyA),
+				http.MethodPost, "http://example.com", bytes.NewBufferString(tt.bodyA),
 			)
 			// When it binds to typeA and typeB, it finds the body is
 			// not typeB but typeA.
 			objA := typeA{}
-			assert.NoError(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
+			require.NoError(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
 			assert.Equal(t, typeA{"FOO"}, objA)
 			objB := typeB{}
-			assert.Error(t, c.ShouldBindBodyWith(&objB, tt.bindingB))
+			require.Error(t, c.ShouldBindBodyWith(&objB, tt.bindingB))
 			assert.NotEqual(t, typeB{"BAR"}, objB)
 		}
 		// bodyB to typeA and typeB
@@ -2069,13 +2257,13 @@ func TestContextShouldBindBodyWith(t *testing.T) {
 			w := httptest.NewRecorder()
 			c, _ := CreateTestContext(w)
 			c.Request, _ = http.NewRequest(
-				"POST", "http://example.com", bytes.NewBufferString(tt.bodyB),
+				http.MethodPost, "http://example.com", bytes.NewBufferString(tt.bodyB),
 			)
 			objA := typeA{}
-			assert.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
+			require.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
 			assert.NotEqual(t, typeA{"FOO"}, objA)
 			objB := typeB{}
-			assert.NoError(t, c.ShouldBindBodyWith(&objB, tt.bindingB))
+			require.NoError(t, c.ShouldBindBodyWith(&objB, tt.bindingB))
 			assert.Equal(t, typeB{"BAR"}, objB)
 		}
 	}
@@ -2116,7 +2304,7 @@ func TestContextShouldBindBodyWithJSON(t *testing.T) {
 		w := httptest.NewRecorder()
 		c, _ := CreateTestContext(w)
 
-		c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body))
+		c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body))
 
 		type typeJSON struct {
 			Foo string `json:"foo" binding:"required"`
@@ -2124,22 +2312,22 @@ func TestContextShouldBindBodyWithJSON(t *testing.T) {
 		objJSON := typeJSON{}
 
 		if tt.bindingBody == binding.JSON {
-			assert.NoError(t, c.ShouldBindBodyWithJSON(&objJSON))
+			require.NoError(t, c.ShouldBindBodyWithJSON(&objJSON))
 			assert.Equal(t, typeJSON{"FOO"}, objJSON)
 		}
 
 		if tt.bindingBody == binding.XML {
-			assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
+			require.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
 			assert.Equal(t, typeJSON{}, objJSON)
 		}
 
 		if tt.bindingBody == binding.YAML {
-			assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
+			require.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
 			assert.Equal(t, typeJSON{}, objJSON)
 		}
 
 		if tt.bindingBody == binding.TOML {
-			assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
+			require.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
 			assert.Equal(t, typeJSON{}, objJSON)
 		}
 	}
@@ -2180,7 +2368,7 @@ func TestContextShouldBindBodyWithXML(t *testing.T) {
 		w := httptest.NewRecorder()
 		c, _ := CreateTestContext(w)
 
-		c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body))
+		c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body))
 
 		type typeXML struct {
 			Foo string `xml:"foo" binding:"required"`
@@ -2188,22 +2376,22 @@ func TestContextShouldBindBodyWithXML(t *testing.T) {
 		objXML := typeXML{}
 
 		if tt.bindingBody == binding.JSON {
-			assert.Error(t, c.ShouldBindBodyWithXML(&objXML))
+			require.Error(t, c.ShouldBindBodyWithXML(&objXML))
 			assert.Equal(t, typeXML{}, objXML)
 		}
 
 		if tt.bindingBody == binding.XML {
-			assert.NoError(t, c.ShouldBindBodyWithXML(&objXML))
+			require.NoError(t, c.ShouldBindBodyWithXML(&objXML))
 			assert.Equal(t, typeXML{"FOO"}, objXML)
 		}
 
 		if tt.bindingBody == binding.YAML {
-			assert.Error(t, c.ShouldBindBodyWithXML(&objXML))
+			require.Error(t, c.ShouldBindBodyWithXML(&objXML))
 			assert.Equal(t, typeXML{}, objXML)
 		}
 
 		if tt.bindingBody == binding.TOML {
-			assert.Error(t, c.ShouldBindBodyWithXML(&objXML))
+			require.Error(t, c.ShouldBindBodyWithXML(&objXML))
 			assert.Equal(t, typeXML{}, objXML)
 		}
 	}
@@ -2244,7 +2432,7 @@ func TestContextShouldBindBodyWithYAML(t *testing.T) {
 		w := httptest.NewRecorder()
 		c, _ := CreateTestContext(w)
 
-		c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body))
+		c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body))
 
 		type typeYAML struct {
 			Foo string `yaml:"foo" binding:"required"`
@@ -2253,22 +2441,22 @@ func TestContextShouldBindBodyWithYAML(t *testing.T) {
 
 		// YAML belongs to a super collection of JSON, so JSON can be parsed by YAML
 		if tt.bindingBody == binding.JSON {
-			assert.NoError(t, c.ShouldBindBodyWithYAML(&objYAML))
+			require.NoError(t, c.ShouldBindBodyWithYAML(&objYAML))
 			assert.Equal(t, typeYAML{"FOO"}, objYAML)
 		}
 
 		if tt.bindingBody == binding.XML {
-			assert.Error(t, c.ShouldBindBodyWithYAML(&objYAML))
+			require.Error(t, c.ShouldBindBodyWithYAML(&objYAML))
 			assert.Equal(t, typeYAML{}, objYAML)
 		}
 
 		if tt.bindingBody == binding.YAML {
-			assert.NoError(t, c.ShouldBindBodyWithYAML(&objYAML))
+			require.NoError(t, c.ShouldBindBodyWithYAML(&objYAML))
 			assert.Equal(t, typeYAML{"FOO"}, objYAML)
 		}
 
 		if tt.bindingBody == binding.TOML {
-			assert.Error(t, c.ShouldBindBodyWithYAML(&objYAML))
+			require.Error(t, c.ShouldBindBodyWithYAML(&objYAML))
 			assert.Equal(t, typeYAML{}, objYAML)
 		}
 	}
@@ -2309,7 +2497,7 @@ func TestContextShouldBindBodyWithTOML(t *testing.T) {
 		w := httptest.NewRecorder()
 		c, _ := CreateTestContext(w)
 
-		c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body))
+		c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body))
 
 		type typeTOML struct {
 			Foo string `toml:"foo" binding:"required"`
@@ -2317,22 +2505,22 @@ func TestContextShouldBindBodyWithTOML(t *testing.T) {
 		objTOML := typeTOML{}
 
 		if tt.bindingBody == binding.JSON {
-			assert.Error(t, c.ShouldBindBodyWithTOML(&objTOML))
+			require.Error(t, c.ShouldBindBodyWithTOML(&objTOML))
 			assert.Equal(t, typeTOML{}, objTOML)
 		}
 
 		if tt.bindingBody == binding.XML {
-			assert.Error(t, c.ShouldBindBodyWithTOML(&objTOML))
+			require.Error(t, c.ShouldBindBodyWithTOML(&objTOML))
 			assert.Equal(t, typeTOML{}, objTOML)
 		}
 
 		if tt.bindingBody == binding.YAML {
-			assert.Error(t, c.ShouldBindBodyWithTOML(&objTOML))
+			require.Error(t, c.ShouldBindBodyWithTOML(&objTOML))
 			assert.Equal(t, typeTOML{}, objTOML)
 		}
 
 		if tt.bindingBody == binding.TOML {
-			assert.NoError(t, c.ShouldBindBodyWithTOML(&objTOML))
+			require.NoError(t, c.ShouldBindBodyWithTOML(&objTOML))
 			assert.Equal(t, typeTOML{"FOO"}, objTOML)
 		}
 	}
@@ -2378,7 +2566,7 @@ func TestContextShouldBindBodyWithPlain(t *testing.T) {
 		w := httptest.NewRecorder()
 		c, _ := CreateTestContext(w)
 
-		c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body))
+		c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString(tt.body))
 
 		type typeJSON struct {
 			Foo string `json:"foo" binding:"required"`
@@ -2387,27 +2575,27 @@ func TestContextShouldBindBodyWithPlain(t *testing.T) {
 
 		if tt.bindingBody == binding.Plain {
 			body := ""
-			assert.NoError(t, c.ShouldBindBodyWithPlain(&body))
-			assert.Equal(t, body, "foo=FOO")
+			require.NoError(t, c.ShouldBindBodyWithPlain(&body))
+			assert.Equal(t, "foo=FOO", body)
 		}
 
 		if tt.bindingBody == binding.JSON {
-			assert.NoError(t, c.ShouldBindBodyWithJSON(&objJSON))
+			require.NoError(t, c.ShouldBindBodyWithJSON(&objJSON))
 			assert.Equal(t, typeJSON{"FOO"}, objJSON)
 		}
 
 		if tt.bindingBody == binding.XML {
-			assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
+			require.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
 			assert.Equal(t, typeJSON{}, objJSON)
 		}
 
 		if tt.bindingBody == binding.YAML {
-			assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
+			require.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
 			assert.Equal(t, typeJSON{}, objJSON)
 		}
 
 		if tt.bindingBody == binding.TOML {
-			assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
+			require.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
 			assert.Equal(t, typeJSON{}, objJSON)
 		}
 	}
@@ -2415,11 +2603,11 @@ func TestContextShouldBindBodyWithPlain(t *testing.T) {
 
 func TestContextGolangContext(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
-	assert.NoError(t, c.Err())
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
+	require.NoError(t, c.Err())
 	assert.Nil(t, c.Done())
 	ti, ok := c.Deadline()
-	assert.Equal(t, ti, time.Time{})
+	assert.Equal(t, time.Time{}, ti)
 	assert.False(t, ok)
 	assert.Equal(t, c.Value(ContextRequestKey), c.Request)
 	assert.Equal(t, c.Value(ContextKey), c)
@@ -2433,7 +2621,7 @@ func TestContextGolangContext(t *testing.T) {
 func TestWebsocketsRequired(t *testing.T) {
 	// Example request from spec: https://tools.ietf.org/html/rfc6455#section-1.2
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("GET", "/chat", nil)
+	c.Request, _ = http.NewRequest(http.MethodGet, "/chat", nil)
 	c.Request.Header.Set("Host", "server.example.com")
 	c.Request.Header.Set("Upgrade", "websocket")
 	c.Request.Header.Set("Connection", "Upgrade")
@@ -2446,7 +2634,7 @@ func TestWebsocketsRequired(t *testing.T) {
 
 	// Normal request, no websocket required.
 	c, _ = CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("GET", "/chat", nil)
+	c.Request, _ = http.NewRequest(http.MethodGet, "/chat", nil)
 	c.Request.Header.Set("Host", "server.example.com")
 
 	assert.False(t, c.IsWebsocket())
@@ -2454,7 +2642,7 @@ func TestWebsocketsRequired(t *testing.T) {
 
 func TestGetRequestHeaderValue(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("GET", "/chat", nil)
+	c.Request, _ = http.NewRequest(http.MethodGet, "/chat", nil)
 	c.Request.Header.Set("Gin-Version", "1.0.0")
 
 	assert.Equal(t, "1.0.0", c.GetHeader("Gin-Version"))
@@ -2464,11 +2652,11 @@ func TestGetRequestHeaderValue(t *testing.T) {
 func TestContextGetRawData(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
 	body := bytes.NewBufferString("Fetch binary post data")
-	c.Request, _ = http.NewRequest("POST", "/", body)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", body)
 	c.Request.Header.Add("Content-Type", MIMEPOSTForm)
 
 	data, err := c.GetRawData()
-	assert.Nil(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "Fetch binary post data", string(data))
 }
 
@@ -2487,7 +2675,7 @@ func TestContextRenderDataFromReader(t *testing.T) {
 	assert.Equal(t, http.StatusOK, w.Code)
 	assert.Equal(t, body, w.Body.String())
 	assert.Equal(t, contentType, w.Header().Get("Content-Type"))
-	assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length"))
+	assert.Equal(t, strconv.FormatInt(contentLength, 10), w.Header().Get("Content-Length"))
 	assert.Equal(t, extraHeaders["Content-Disposition"], w.Header().Get("Content-Disposition"))
 }
 
@@ -2505,7 +2693,7 @@ func TestContextRenderDataFromReaderNoHeaders(t *testing.T) {
 	assert.Equal(t, http.StatusOK, w.Code)
 	assert.Equal(t, body, w.Body.String())
 	assert.Equal(t, contentType, w.Header().Get("Content-Type"))
-	assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length"))
+	assert.Equal(t, strconv.FormatInt(contentLength, 10), w.Header().Get("Content-Length"))
 }
 
 type TestResponseRecorder struct {
@@ -2539,7 +2727,7 @@ func TestContextStream(t *testing.T) {
 		}()
 
 		_, err := w.Write([]byte("test"))
-		assert.NoError(t, err)
+		require.NoError(t, err)
 
 		return stopStream
 	})
@@ -2557,7 +2745,7 @@ func TestContextStreamWithClientGone(t *testing.T) {
 		}()
 
 		_, err := writer.Write([]byte("test"))
-		assert.NoError(t, err)
+		require.NoError(t, err)
 
 		return true
 	})
@@ -2593,8 +2781,8 @@ func TestRaceParamsContextCopy(t *testing.T) {
 			}(c.Copy(), c.Param("name"))
 		})
 	}
-	PerformRequest(router, "GET", "/name1/api")
-	PerformRequest(router, "GET", "/name2/api")
+	PerformRequest(router, http.MethodGet, "/name1/api")
+	PerformRequest(router, http.MethodGet, "/name2/api")
 	wg.Wait()
 }
 
@@ -2613,7 +2801,7 @@ func TestContextWithKeysMutex(t *testing.T) {
 
 func TestRemoteIPFail(t *testing.T) {
 	c, _ := CreateTestContext(httptest.NewRecorder())
-	c.Request, _ = http.NewRequest("POST", "/", nil)
+	c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 	c.Request.RemoteAddr = "[:::]:80"
 	ip := net.ParseIP(c.RemoteIP())
 	trust := c.engine.isTrustedProxy(ip)
@@ -2685,7 +2873,7 @@ func TestContextWithFallbackErrFromRequestContext(t *testing.T) {
 	// enable ContextWithFallback feature flag
 	c.engine.ContextWithFallback = true
 
-	assert.Nil(t, c.Err())
+	require.NoError(t, c.Err())
 
 	c2, _ := CreateTestContext(httptest.NewRecorder())
 	// enable ContextWithFallback feature flag
@@ -2710,11 +2898,12 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
 		{
 			name: "c with struct context key",
 			getContextAndKey: func() (*Context, any) {
-				var key struct{}
+				type KeyStruct struct{} // https://staticcheck.dev/docs/checks/#SA1029
+				var key KeyStruct
 				c, _ := CreateTestContext(httptest.NewRecorder())
 				// enable ContextWithFallback feature flag
 				c.engine.ContextWithFallback = true
-				c.Request, _ = http.NewRequest("POST", "/", nil)
+				c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 				c.Request = c.Request.WithContext(context.WithValue(context.TODO(), key, "value"))
 				return c, key
 			},
@@ -2726,7 +2915,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
 				c, _ := CreateTestContext(httptest.NewRecorder())
 				// enable ContextWithFallback feature flag
 				c.engine.ContextWithFallback = true
-				c.Request, _ = http.NewRequest("POST", "/", nil)
+				c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 				c.Request = c.Request.WithContext(context.WithValue(context.TODO(), contextKey("key"), "value"))
 				return c, contextKey("key")
 			},
@@ -2749,7 +2938,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
 				c, _ := CreateTestContext(httptest.NewRecorder())
 				// enable ContextWithFallback feature flag
 				c.engine.ContextWithFallback = true
-				c.Request, _ = http.NewRequest("POST", "/", nil)
+				c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
 				return c, "key"
 			},
 			value: nil,
@@ -2834,7 +3023,7 @@ func TestContextAddParam(t *testing.T) {
 	c.AddParam(id, value)
 
 	v, ok := c.Params.Get(id)
-	assert.Equal(t, ok, true)
+	assert.True(t, ok)
 	assert.Equal(t, value, v)
 }
 
@@ -2881,7 +3070,7 @@ func TestInterceptedHeader(t *testing.T) {
 		c.Header("X-Test-2", "present")
 		c.String(http.StatusOK, "hello world")
 	})
-	c.Request = httptest.NewRequest("GET", "/", nil)
+	c.Request = httptest.NewRequest(http.MethodGet, "/", nil)
 	r.HandleContext(c)
 	// Result() has headers frozen when WriteHeaderNow() has been called
 	// Compared to this time, this is when the response headers will be flushed
@@ -2890,3 +3079,47 @@ func TestInterceptedHeader(t *testing.T) {
 	assert.Equal(t, "", w.Result().Header.Get("X-Test"))
 	assert.Equal(t, "present", w.Result().Header.Get("X-Test-2"))
 }
+
+func TestContextNext(t *testing.T) {
+	c, _ := CreateTestContext(httptest.NewRecorder())
+
+	// Test with no handlers
+	c.Next()
+	assert.Equal(t, int8(0), c.index)
+
+	// Test with one handler
+	c.index = -1
+	c.handlers = HandlersChain{func(c *Context) {
+		c.Set("key", "value")
+	}}
+	c.Next()
+	assert.Equal(t, int8(1), c.index)
+	value, exists := c.Get("key")
+	assert.True(t, exists)
+	assert.Equal(t, "value", value)
+
+	// Test with multiple handlers
+	c.handlers = HandlersChain{
+		func(c *Context) {
+			c.Set("key1", "value1")
+			c.Next()
+			c.Set("key2", "value2")
+		},
+		nil,
+		func(c *Context) {
+			c.Set("key3", "value3")
+		},
+	}
+	c.index = -1
+	c.Next()
+	assert.Equal(t, int8(4), c.index)
+	value, exists = c.Get("key1")
+	assert.True(t, exists)
+	assert.Equal(t, "value1", value)
+	value, exists = c.Get("key2")
+	assert.True(t, exists)
+	assert.Equal(t, "value2", value)
+	value, exists = c.Get("key3")
+	assert.True(t, exists)
+	assert.Equal(t, "value3", value)
+}
diff --git a/debug_test.go b/debug_test.go
index 1e57668..59b61be 100644
--- a/debug_test.go
+++ b/debug_test.go
@@ -10,6 +10,7 @@ import (
 	"html/template"
 	"io"
 	"log"
+	"net/http"
 	"os"
 	"runtime"
 	"strings"
@@ -17,6 +18,7 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 // TODO
@@ -59,7 +61,7 @@ func TestDebugPrintError(t *testing.T) {
 func TestDebugPrintRoutes(t *testing.T) {
 	re := captureOutput(t, func() {
 		SetMode(DebugMode)
-		debugPrintRoute("GET", "/path/to/route/:param", HandlersChain{func(c *Context) {}, handlerNameTest})
+		debugPrintRoute(http.MethodGet, "/path/to/route/:param", HandlersChain{func(c *Context) {}, handlerNameTest})
 		SetMode(TestMode)
 	})
 	assert.Regexp(t, `^\[GIN-debug\] GET    /path/to/route/:param     --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, re)
@@ -71,7 +73,7 @@ func TestDebugPrintRouteFunc(t *testing.T) {
 	}
 	re := captureOutput(t, func() {
 		SetMode(DebugMode)
-		debugPrintRoute("GET", "/path/to/route/:param1/:param2", HandlersChain{func(c *Context) {}, handlerNameTest})
+		debugPrintRoute(http.MethodGet, "/path/to/route/:param1/:param2", HandlersChain{func(c *Context) {}, handlerNameTest})
 		SetMode(TestMode)
 	})
 	assert.Regexp(t, `^\[GIN-debug\] GET    /path/to/route/:param1/:param2           --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, re)
@@ -104,7 +106,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) {
 	})
 	m, e := getMinVer(runtime.Version())
 	if e == nil && m < ginSupportMinGoVer {
-		assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.21+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
+		assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.23+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
 	} else {
 		assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
 	}
@@ -154,13 +156,13 @@ func TestGetMinVer(t *testing.T) {
 	var m uint64
 	var e error
 	_, e = getMinVer("go1")
-	assert.NotNil(t, e)
+	require.Error(t, e)
 	m, e = getMinVer("go1.1")
 	assert.Equal(t, uint64(1), m)
-	assert.Nil(t, e)
+	require.NoError(t, e)
 	m, e = getMinVer("go1.1.1")
-	assert.Nil(t, e)
+	require.NoError(t, e)
 	assert.Equal(t, uint64(1), m)
 	_, e = getMinVer("go1.1.1.1")
-	assert.NotNil(t, e)
+	require.Error(t, e)
 }
diff --git a/deprecated_test.go b/deprecated_test.go
index 0240b2e..6c8f2a7 100644
--- a/deprecated_test.go
+++ b/deprecated_test.go
@@ -18,7 +18,7 @@ func TestBindWith(t *testing.T) {
 	w := httptest.NewRecorder()
 	c, _ := CreateTestContext(w)
 
-	c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused"))
+	c.Request, _ = http.NewRequest(http.MethodPost, "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused"))
 
 	var obj struct {
 		Foo string `form:"foo"`
diff --git a/errors_test.go b/errors_test.go
index f77a634..72a3699 100644
--- a/errors_test.go
+++ b/errors_test.go
@@ -11,6 +11,7 @@ import (
 
 	"github.com/gin-gonic/gin/internal/json"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestError(t *testing.T) {
@@ -122,7 +123,7 @@ func TestErrorUnwrap(t *testing.T) {
 	})
 
 	// check that 'errors.Is()' and 'errors.As()' behave as expected :
-	assert.True(t, errors.Is(err, innerErr))
+	require.ErrorIs(t, err, innerErr)
 	var testErr TestErr
-	assert.True(t, errors.As(err, &testErr))
+	require.ErrorAs(t, err, &testErr)
 }
diff --git a/fs_test.go b/fs_test.go
index a1690cd..167ac1a 100644
--- a/fs_test.go
+++ b/fs_test.go
@@ -7,6 +7,7 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 type mockFileSystem struct {
@@ -28,7 +29,7 @@ func TestOnlyFilesFS_Open(t *testing.T) {
 
 	file, err := fs.Open("foo")
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, testFile, file.(neutralizedReaddirFile).File)
 }
 
@@ -43,7 +44,7 @@ func TestOnlyFilesFS_Open_err(t *testing.T) {
 
 	file, err := fs.Open("foo")
 
-	assert.ErrorIs(t, err, testError)
+	require.ErrorIs(t, err, testError)
 	assert.Nil(t, file)
 }
 
@@ -52,7 +53,7 @@ func Test_neuteredReaddirFile_Readdir(t *testing.T) {
 
 	res, err := n.Readdir(0)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Nil(t, res)
 }
 
diff --git a/gin_integration_test.go b/gin_integration_test.go
index 5398271..3082bc2 100644
--- a/gin_integration_test.go
+++ b/gin_integration_test.go
@@ -21,6 +21,7 @@ import (
 	"time"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 // params[0]=url example:http://127.0.0.1:8080/index (cannot be empty)
@@ -40,11 +41,11 @@ func testRequest(t *testing.T, params ...string) {
 	client := &http.Client{Transport: tr}
 
 	resp, err := client.Get(params[0])
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	defer resp.Body.Close()
 
 	body, ioerr := io.ReadAll(resp.Body)
-	assert.NoError(t, ioerr)
+	require.NoError(t, ioerr)
 
 	var responseStatus = "200 OK"
 	if len(params) > 1 && params[1] != "" {
@@ -73,13 +74,13 @@ func TestRunEmpty(t *testing.T) {
 	// otherwise the main thread will complete
 	time.Sleep(5 * time.Millisecond)
 
-	assert.Error(t, router.Run(":8080"))
+	require.Error(t, router.Run(":8080"))
 	testRequest(t, "http://localhost:8080/example")
 }
 
 func TestBadTrustedCIDRs(t *testing.T) {
 	router := New()
-	assert.Error(t, router.SetTrustedProxies([]string{"hello/world"}))
+	require.Error(t, router.SetTrustedProxies([]string{"hello/world"}))
 }
 
 /* legacy tests
@@ -87,7 +88,7 @@ func TestBadTrustedCIDRsForRun(t *testing.T) {
 	os.Setenv("PORT", "")
 	router := New()
 	router.TrustedProxies = []string{"hello/world"}
-	assert.Error(t, router.Run(":8080"))
+	require.Error(t, router.Run(":8080"))
 }
 
 func TestBadTrustedCIDRsForRunUnix(t *testing.T) {
@@ -100,7 +101,7 @@ func TestBadTrustedCIDRsForRunUnix(t *testing.T) {
 
 	go func() {
 		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
-		assert.Error(t, router.RunUnix(unixTestSocket))
+		require.Error(t, router.RunUnix(unixTestSocket))
 	}()
 	// have to wait for the goroutine to start and run the server
 	// otherwise the main thread will complete
@@ -112,15 +113,15 @@ func TestBadTrustedCIDRsForRunFd(t *testing.T) {
 	router.TrustedProxies = []string{"hello/world"}
 
 	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	listener, err := net.ListenTCP("tcp", addr)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	socketFile, err := listener.File()
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	go func() {
 		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
-		assert.Error(t, router.RunFd(int(socketFile.Fd())))
+		require.Error(t, router.RunFd(int(socketFile.Fd())))
 	}()
 	// have to wait for the goroutine to start and run the server
 	// otherwise the main thread will complete
@@ -132,12 +133,12 @@ func TestBadTrustedCIDRsForRunListener(t *testing.T) {
 	router.TrustedProxies = []string{"hello/world"}
 
 	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	listener, err := net.ListenTCP("tcp", addr)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	go func() {
 		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
-		assert.Error(t, router.RunListener(listener))
+		require.Error(t, router.RunListener(listener))
 	}()
 	// have to wait for the goroutine to start and run the server
 	// otherwise the main thread will complete
@@ -148,7 +149,7 @@ func TestBadTrustedCIDRsForRunTLS(t *testing.T) {
 	os.Setenv("PORT", "")
 	router := New()
 	router.TrustedProxies = []string{"hello/world"}
-	assert.Error(t, router.RunTLS(":8080", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
+	require.Error(t, router.RunTLS(":8080", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
 }
 */
 
@@ -164,7 +165,7 @@ func TestRunTLS(t *testing.T) {
 	// otherwise the main thread will complete
 	time.Sleep(5 * time.Millisecond)
 
-	assert.Error(t, router.RunTLS(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
+	require.Error(t, router.RunTLS(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
 	testRequest(t, "https://localhost:8443/example")
 }
 
@@ -201,7 +202,7 @@ func TestPusher(t *testing.T) {
 	// otherwise the main thread will complete
 	time.Sleep(5 * time.Millisecond)
 
-	assert.Error(t, router.RunTLS(":8449", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
+	require.Error(t, router.RunTLS(":8449", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
 	testRequest(t, "https://localhost:8449/pusher")
 }
 
@@ -216,14 +217,14 @@ func TestRunEmptyWithEnv(t *testing.T) {
 	// otherwise the main thread will complete
 	time.Sleep(5 * time.Millisecond)
 
-	assert.Error(t, router.Run(":3123"))
+	require.Error(t, router.Run(":3123"))
 	testRequest(t, "http://localhost:3123/example")
 }
 
 func TestRunTooMuchParams(t *testing.T) {
 	router := New()
 	assert.Panics(t, func() {
-		assert.NoError(t, router.Run("2", "2"))
+		require.NoError(t, router.Run("2", "2"))
 	})
 }
 
@@ -237,7 +238,7 @@ func TestRunWithPort(t *testing.T) {
 	// otherwise the main thread will complete
 	time.Sleep(5 * time.Millisecond)
 
-	assert.Error(t, router.Run(":5150"))
+	require.Error(t, router.Run(":5150"))
 	testRequest(t, "http://localhost:5150/example")
 }
 
@@ -257,7 +258,7 @@ func TestUnixSocket(t *testing.T) {
 	time.Sleep(5 * time.Millisecond)
 
 	c, err := net.Dial("unix", unixTestSocket)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	fmt.Fprint(c, "GET /example HTTP/1.0\r\n\r\n")
 	scanner := bufio.NewScanner(c)
@@ -271,7 +272,7 @@ func TestUnixSocket(t *testing.T) {
 
 func TestBadUnixSocket(t *testing.T) {
 	router := New()
-	assert.Error(t, router.RunUnix("#/tmp/unix_unit_test"))
+	require.Error(t, router.RunUnix("#/tmp/unix_unit_test"))
 }
 
 func TestRunQUIC(t *testing.T) {
@@ -286,7 +287,7 @@ func TestRunQUIC(t *testing.T) {
 	// otherwise the main thread will complete
 	time.Sleep(5 * time.Millisecond)
 
-	assert.Error(t, router.RunQUIC(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
+	require.Error(t, router.RunQUIC(":8443", "./testdata/certificate/cert.pem", "./testdata/certificate/key.pem"))
 	testRequest(t, "https://localhost:8443/example")
 }
 
@@ -294,15 +295,15 @@ func TestFileDescriptor(t *testing.T) {
 	router := New()
 
 	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	listener, err := net.ListenTCP("tcp", addr)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	socketFile, err := listener.File()
 	if isWindows() {
 		// not supported by windows, it is unimplemented now
-		assert.Error(t, err)
+		require.Error(t, err)
 	} else {
-		assert.NoError(t, err)
+		require.NoError(t, err)
 	}
 
 	if socketFile == nil {
@@ -318,7 +319,7 @@ func TestFileDescriptor(t *testing.T) {
 	time.Sleep(5 * time.Millisecond)
 
 	c, err := net.Dial("tcp", listener.Addr().String())
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n")
 	scanner := bufio.NewScanner(c)
@@ -332,15 +333,15 @@ func TestFileDescriptor(t *testing.T) {
 
 func TestBadFileDescriptor(t *testing.T) {
 	router := New()
-	assert.Error(t, router.RunFd(0))
+	require.Error(t, router.RunFd(0))
 }
 
 func TestListener(t *testing.T) {
 	router := New()
 	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	listener, err := net.ListenTCP("tcp", addr)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	go func() {
 		router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
 		assert.NoError(t, router.RunListener(listener))
@@ -350,7 +351,7 @@ func TestListener(t *testing.T) {
 	time.Sleep(5 * time.Millisecond)
 
 	c, err := net.Dial("tcp", listener.Addr().String())
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n")
 	scanner := bufio.NewScanner(c)
@@ -365,11 +366,11 @@ func TestListener(t *testing.T) {
 func TestBadListener(t *testing.T) {
 	router := New()
 	addr, err := net.ResolveTCPAddr("tcp", "localhost:10086")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	listener, err := net.ListenTCP("tcp", addr)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	listener.Close()
-	assert.Error(t, router.RunListener(listener))
+	require.Error(t, router.RunListener(listener))
 }
 
 func TestWithHttptestWithAutoSelectedPort(t *testing.T) {
@@ -395,7 +396,14 @@ func TestConcurrentHandleContext(t *testing.T) {
 	wg.Add(iterations)
 	for i := 0; i < iterations; i++ {
 		go func() {
-			testGetRequestHandler(t, router, "/")
+			req, err := http.NewRequest(http.MethodGet, "/", nil)
+			assert.NoError(t, err)
+
+			w := httptest.NewRecorder()
+			router.ServeHTTP(w, req)
+
+			assert.Equal(t, "it worked", w.Body.String(), "resp body should match")
+			assert.Equal(t, 200, w.Code, "should get a 200")
 			wg.Done()
 		}()
 	}
@@ -417,17 +425,6 @@ func TestConcurrentHandleContext(t *testing.T) {
 // 	testRequest(t, "http://localhost:8033/example")
 // }
 
-func testGetRequestHandler(t *testing.T, h http.Handler, url string) {
-	req, err := http.NewRequest(http.MethodGet, url, nil)
-	assert.NoError(t, err)
-
-	w := httptest.NewRecorder()
-	h.ServeHTTP(w, req)
-
-	assert.Equal(t, "it worked", w.Body.String(), "resp body should match")
-	assert.Equal(t, 200, w.Code, "should get a 200")
-}
-
 func TestTreeRunDynamicRouting(t *testing.T) {
 	router := New()
 	router.GET("/aa/*xx", func(c *Context) { c.String(http.StatusOK, "/aa/*xx") })
diff --git a/gin_test.go b/gin_test.go
index e68f1ce..850ae09 100644
--- a/gin_test.go
+++ b/gin_test.go
@@ -20,6 +20,7 @@ import (
 	"time"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"golang.org/x/net/http2"
 )
 
@@ -72,7 +73,7 @@ func TestLoadHTMLGlobDebugMode(t *testing.T) {
 	)
 	defer ts.Close()
 
-	res, err := http.Get(fmt.Sprintf("%s/test", ts.URL))
+	res, err := http.Get(ts.URL + "/test")
 	if err != nil {
 		t.Error(err)
 	}
@@ -130,7 +131,7 @@ func TestLoadHTMLGlobTestMode(t *testing.T) {
 	)
 	defer ts.Close()
 
-	res, err := http.Get(fmt.Sprintf("%s/test", ts.URL))
+	res, err := http.Get(ts.URL + "/test")
 	if err != nil {
 		t.Error(err)
 	}
@@ -150,7 +151,7 @@ func TestLoadHTMLGlobReleaseMode(t *testing.T) {
 	)
 	defer ts.Close()
 
-	res, err := http.Get(fmt.Sprintf("%s/test", ts.URL))
+	res, err := http.Get(ts.URL + "/test")
 	if err != nil {
 		t.Error(err)
 	}
@@ -177,7 +178,7 @@ func TestLoadHTMLGlobUsingTLS(t *testing.T) {
 		},
 	}
 	client := &http.Client{Transport: tr}
-	res, err := client.Get(fmt.Sprintf("%s/test", ts.URL))
+	res, err := client.Get(ts.URL + "/test")
 	if err != nil {
 		t.Error(err)
 	}
@@ -197,7 +198,7 @@ func TestLoadHTMLGlobFromFuncMap(t *testing.T) {
 	)
 	defer ts.Close()
 
-	res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL))
+	res, err := http.Get(ts.URL + "/raw")
 	if err != nil {
 		t.Error(err)
 	}
@@ -228,7 +229,7 @@ func TestLoadHTMLFilesTestMode(t *testing.T) {
 	)
 	defer ts.Close()
 
-	res, err := http.Get(fmt.Sprintf("%s/test", ts.URL))
+	res, err := http.Get(ts.URL + "/test")
 	if err != nil {
 		t.Error(err)
 	}
@@ -248,7 +249,7 @@ func TestLoadHTMLFilesDebugMode(t *testing.T) {
 	)
 	defer ts.Close()
 
-	res, err := http.Get(fmt.Sprintf("%s/test", ts.URL))
+	res, err := http.Get(ts.URL + "/test")
 	if err != nil {
 		t.Error(err)
 	}
@@ -268,7 +269,7 @@ func TestLoadHTMLFilesReleaseMode(t *testing.T) {
 	)
 	defer ts.Close()
 
-	res, err := http.Get(fmt.Sprintf("%s/test", ts.URL))
+	res, err := http.Get(ts.URL + "/test")
 	if err != nil {
 		t.Error(err)
 	}
@@ -295,7 +296,7 @@ func TestLoadHTMLFilesUsingTLS(t *testing.T) {
 		},
 	}
 	client := &http.Client{Transport: tr}
-	res, err := client.Get(fmt.Sprintf("%s/test", ts.URL))
+	res, err := client.Get(ts.URL + "/test")
 	if err != nil {
 		t.Error(err)
 	}
@@ -315,7 +316,7 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) {
 	)
 	defer ts.Close()
 
-	res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL))
+	res, err := http.Get(ts.URL + "/raw")
 	if err != nil {
 		t.Error(err)
 	}
@@ -326,31 +327,31 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) {
 
 func TestAddRoute(t *testing.T) {
 	router := New()
-	router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})
+	router.addRoute(http.MethodGet, "/", HandlersChain{func(_ *Context) {}})
 
 	assert.Len(t, router.trees, 1)
-	assert.NotNil(t, router.trees.get("GET"))
-	assert.Nil(t, router.trees.get("POST"))
+	assert.NotNil(t, router.trees.get(http.MethodGet))
+	assert.Nil(t, router.trees.get(http.MethodPost))
 
-	router.addRoute("POST", "/", HandlersChain{func(_ *Context) {}})
+	router.addRoute(http.MethodPost, "/", HandlersChain{func(_ *Context) {}})
 
 	assert.Len(t, router.trees, 2)
-	assert.NotNil(t, router.trees.get("GET"))
-	assert.NotNil(t, router.trees.get("POST"))
+	assert.NotNil(t, router.trees.get(http.MethodGet))
+	assert.NotNil(t, router.trees.get(http.MethodPost))
 
-	router.addRoute("POST", "/post", HandlersChain{func(_ *Context) {}})
+	router.addRoute(http.MethodPost, "/post", HandlersChain{func(_ *Context) {}})
 	assert.Len(t, router.trees, 2)
 }
 
 func TestAddRouteFails(t *testing.T) {
 	router := New()
 	assert.Panics(t, func() { router.addRoute("", "/", HandlersChain{func(_ *Context) {}}) })
-	assert.Panics(t, func() { router.addRoute("GET", "a", HandlersChain{func(_ *Context) {}}) })
-	assert.Panics(t, func() { router.addRoute("GET", "/", HandlersChain{}) })
+	assert.Panics(t, func() { router.addRoute(http.MethodGet, "a", HandlersChain{func(_ *Context) {}}) })
+	assert.Panics(t, func() { router.addRoute(http.MethodGet, "/", HandlersChain{}) })
 
-	router.addRoute("POST", "/post", HandlersChain{func(_ *Context) {}})
+	router.addRoute(http.MethodPost, "/post", HandlersChain{func(_ *Context) {}})
 	assert.Panics(t, func() {
-		router.addRoute("POST", "/post", HandlersChain{func(_ *Context) {}})
+		router.addRoute(http.MethodPost, "/post", HandlersChain{func(_ *Context) {}})
 	})
 }
 
@@ -492,27 +493,27 @@ func TestListOfRoutes(t *testing.T) {
 
 	assert.Len(t, list, 7)
 	assertRoutePresent(t, list, RouteInfo{
-		Method:  "GET",
+		Method:  http.MethodGet,
 		Path:    "/favicon.ico",
 		Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$",
 	})
 	assertRoutePresent(t, list, RouteInfo{
-		Method:  "GET",
+		Method:  http.MethodGet,
 		Path:    "/",
 		Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$",
 	})
 	assertRoutePresent(t, list, RouteInfo{
-		Method:  "GET",
+		Method:  http.MethodGet,
 		Path:    "/users/",
 		Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest2$",
 	})
 	assertRoutePresent(t, list, RouteInfo{
-		Method:  "GET",
+		Method:  http.MethodGet,
 		Path:    "/users/:id",
 		Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$",
 	})
 	assertRoutePresent(t, list, RouteInfo{
-		Method:  "POST",
+		Method:  http.MethodPost,
 		Path:    "/users/:id",
 		Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest2$",
 	})
@@ -530,7 +531,7 @@ func TestEngineHandleContext(t *testing.T) {
 	}
 
 	assert.NotPanics(t, func() {
-		w := PerformRequest(r, "GET", "/")
+		w := PerformRequest(r, http.MethodGet, "/")
 		assert.Equal(t, 301, w.Code)
 	})
 }
@@ -547,10 +548,10 @@ func TestEngineHandleContextManyReEntries(t *testing.T) {
 	r.GET("/:count", func(c *Context) {
 		countStr := c.Param("count")
 		count, err := strconv.Atoi(countStr)
-		assert.NoError(t, err)
+		require.NoError(t, err)
 
 		n, err := c.Writer.Write([]byte("."))
-		assert.NoError(t, err)
+		require.NoError(t, err)
 		assert.Equal(t, 1, n)
 
 		switch {
@@ -563,7 +564,7 @@ func TestEngineHandleContextManyReEntries(t *testing.T) {
 	})
 
 	assert.NotPanics(t, func() {
-		w := PerformRequest(r, "GET", "/"+strconv.Itoa(expectValue-1)) // include 0 value
+		w := PerformRequest(r, http.MethodGet, "/"+strconv.Itoa(expectValue-1)) // include 0 value
 		assert.Equal(t, 200, w.Code)
 		assert.Equal(t, expectValue, w.Body.Len())
 	})
@@ -572,6 +573,44 @@ func TestEngineHandleContextManyReEntries(t *testing.T) {
 	assert.Equal(t, int64(expectValue), middlewareCounter)
 }
 
+func TestEngineHandleContextPreventsMiddlewareReEntry(t *testing.T) {
+	// given
+	var handlerCounterV1, handlerCounterV2, middlewareCounterV1 int64
+
+	r := New()
+	v1 := r.Group("/v1")
+	{
+		v1.Use(func(c *Context) {
+			atomic.AddInt64(&middlewareCounterV1, 1)
+		})
+		v1.GET("/test", func(c *Context) {
+			atomic.AddInt64(&handlerCounterV1, 1)
+			c.Status(http.StatusOK)
+		})
+	}
+
+	v2 := r.Group("/v2")
+	{
+		v2.GET("/test", func(c *Context) {
+			c.Request.URL.Path = "/v1/test"
+			r.HandleContext(c)
+		}, func(c *Context) {
+			atomic.AddInt64(&handlerCounterV2, 1)
+		})
+	}
+
+	// when
+	responseV1 := PerformRequest(r, "GET", "/v1/test")
+	responseV2 := PerformRequest(r, "GET", "/v2/test")
+
+	// then
+	assert.Equal(t, 200, responseV1.Code)
+	assert.Equal(t, 200, responseV2.Code)
+	assert.Equal(t, int64(2), handlerCounterV1)
+	assert.Equal(t, int64(2), middlewareCounterV1)
+	assert.Equal(t, int64(1), handlerCounterV2)
+}
+
 func TestPrepareTrustedCIRDsWith(t *testing.T) {
 	r := New()
 
@@ -580,7 +619,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
 		expectedTrustedCIDRs := []*net.IPNet{parseCIDR("0.0.0.0/0")}
 		err := r.SetTrustedProxies([]string{"0.0.0.0/0"})
 
-		assert.NoError(t, err)
+		require.NoError(t, err)
 		assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs)
 	}
 
@@ -588,7 +627,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
 	{
 		err := r.SetTrustedProxies([]string{"192.168.1.33/33"})
 
-		assert.Error(t, err)
+		require.Error(t, err)
 	}
 
 	// valid ipv4 address
@@ -597,7 +636,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
 
 		err := r.SetTrustedProxies([]string{"192.168.1.33"})
 
-		assert.NoError(t, err)
+		require.NoError(t, err)
 		assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs)
 	}
 
@@ -605,7 +644,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
 	{
 		err := r.SetTrustedProxies([]string{"192.168.1.256"})
 
-		assert.Error(t, err)
+		require.Error(t, err)
 	}
 
 	// valid ipv6 address
@@ -613,7 +652,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
 		expectedTrustedCIDRs := []*net.IPNet{parseCIDR("2002:0000:0000:1234:abcd:ffff:c0a8:0101/128")}
 		err := r.SetTrustedProxies([]string{"2002:0000:0000:1234:abcd:ffff:c0a8:0101"})
 
-		assert.NoError(t, err)
+		require.NoError(t, err)
 		assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs)
 	}
 
@@ -621,7 +660,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
 	{
 		err := r.SetTrustedProxies([]string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101"})
 
-		assert.Error(t, err)
+		require.Error(t, err)
 	}
 
 	// valid ipv6 cidr
@@ -629,7 +668,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
 		expectedTrustedCIDRs := []*net.IPNet{parseCIDR("::/0")}
 		err := r.SetTrustedProxies([]string{"::/0"})
 
-		assert.NoError(t, err)
+		require.NoError(t, err)
 		assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs)
 	}
 
@@ -637,7 +676,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
 	{
 		err := r.SetTrustedProxies([]string{"gggg:0000:0000:1234:abcd:ffff:c0a8:0101/129"})
 
-		assert.Error(t, err)
+		require.Error(t, err)
 	}
 
 	// valid combination
@@ -653,7 +692,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
 			"172.16.0.1",
 		})
 
-		assert.NoError(t, err)
+		require.NoError(t, err)
 		assert.Equal(t, expectedTrustedCIDRs, r.trustedCIDRs)
 	}
 
@@ -665,7 +704,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
 			"172.16.0.256",
 		})
 
-		assert.Error(t, err)
+		require.Error(t, err)
 	}
 
 	// nil value
@@ -673,7 +712,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
 		err := r.SetTrustedProxies(nil)
 
 		assert.Nil(t, r.trustedCIDRs)
-		assert.Nil(t, err)
+		require.NoError(t, err)
 	}
 }
 
@@ -711,8 +750,8 @@ func TestNewOptionFunc(t *testing.T) {
 	r := New(fc)
 
 	routes := r.Routes()
-	assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest1"})
-	assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest2"})
+	assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: http.MethodGet, Handler: "github.com/gin-gonic/gin.handlerTest1"})
+	assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: http.MethodGet, Handler: "github.com/gin-gonic/gin.handlerTest2"})
 }
 
 func TestWithOptionFunc(t *testing.T) {
@@ -728,8 +767,8 @@ func TestWithOptionFunc(t *testing.T) {
 	})
 
 	routes := r.Routes()
-	assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest1"})
-	assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: "GET", Handler: "github.com/gin-gonic/gin.handlerTest2"})
+	assertRoutePresent(t, routes, RouteInfo{Path: "/test1", Method: http.MethodGet, Handler: "github.com/gin-gonic/gin.handlerTest1"})
+	assertRoutePresent(t, routes, RouteInfo{Path: "/test2", Method: http.MethodGet, Handler: "github.com/gin-gonic/gin.handlerTest2"})
 }
 
 type Birthday string
@@ -748,9 +787,20 @@ func TestCustomUnmarshalStruct(t *testing.T) {
 		_ = ctx.BindQuery(&request)
 		ctx.JSON(200, request.Birthday)
 	})
-	req := httptest.NewRequest("GET", "/test?birthday=2000-01-01", nil)
+	req := httptest.NewRequest(http.MethodGet, "/test?birthday=2000-01-01", nil)
 	w := httptest.NewRecorder()
 	route.ServeHTTP(w, req)
 	assert.Equal(t, 200, w.Code)
 	assert.Equal(t, `"2000/01/01"`, w.Body.String())
 }
+
+// Test the fix for https://github.com/gin-gonic/gin/issues/4002
+func TestMethodNotAllowedNoRoute(t *testing.T) {
+	g := New()
+	g.HandleMethodNotAllowed = true
+
+	req := httptest.NewRequest(http.MethodGet, "/", nil)
+	resp := httptest.NewRecorder()
+	assert.NotPanics(t, func() { g.ServeHTTP(resp, req) })
+	assert.Equal(t, http.StatusNotFound, resp.Code)
+}
diff --git a/githubapi_test.go b/githubapi_test.go
index 9276bed..0c86af2 100644
--- a/githubapi_test.go
+++ b/githubapi_test.go
@@ -10,10 +10,12 @@ import (
 	"net/http"
 	"net/http/httptest"
 	"os"
+	"strconv"
 	"strings"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 type route struct {
@@ -295,9 +297,9 @@ func TestShouldBindUri(t *testing.T) {
 	}
 	router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) {
 		var person Person
-		assert.NoError(t, c.ShouldBindUri(&person))
-		assert.True(t, person.Name != "")
-		assert.True(t, person.ID != "")
+		require.NoError(t, c.ShouldBindUri(&person))
+		assert.NotEqual(t, "", person.Name)
+		assert.NotEqual(t, "", person.ID)
 		c.String(http.StatusOK, "ShouldBindUri test OK")
 	})
 
@@ -317,9 +319,9 @@ func TestBindUri(t *testing.T) {
 	}
 	router.Handle(http.MethodGet, "/rest/:name/:id", func(c *Context) {
 		var person Person
-		assert.NoError(t, c.BindUri(&person))
-		assert.True(t, person.Name != "")
-		assert.True(t, person.ID != "")
+		require.NoError(t, c.BindUri(&person))
+		assert.NotEqual(t, "", person.Name)
+		assert.NotEqual(t, "", person.ID)
 		c.String(http.StatusOK, "BindUri test OK")
 	})
 
@@ -338,7 +340,7 @@ func TestBindUriError(t *testing.T) {
 	}
 	router.Handle(http.MethodGet, "/new/rest/:num", func(c *Context) {
 		var m Member
-		assert.Error(t, c.BindUri(&m))
+		require.Error(t, c.BindUri(&m))
 	})
 
 	path1, _ := exampleFromPath("/new/rest/:num")
@@ -410,7 +412,7 @@ func exampleFromPath(path string) (string, Params) {
 		}
 		if start >= 0 {
 			if c == '/' {
-				value := fmt.Sprint(rand.Intn(100000))
+				value := strconv.Itoa(rand.Intn(100000))
 				params = append(params, Param{
 					Key:   path[start:i],
 					Value: value,
@@ -424,7 +426,7 @@ func exampleFromPath(path string) (string, Params) {
 		}
 	}
 	if start >= 0 {
-		value := fmt.Sprint(rand.Intn(100000))
+		value := strconv.Itoa(rand.Intn(100000))
 		params = append(params, Param{
 			Key:   path[start:],
 			Value: value,
diff --git a/logger_test.go b/logger_test.go
index 6c1814d..de00c49 100644
--- a/logger_test.go
+++ b/logger_test.go
@@ -31,9 +31,9 @@ func TestLogger(t *testing.T) {
 	router.HEAD("/example", func(c *Context) {})
 	router.OPTIONS("/example", func(c *Context) {})
 
-	PerformRequest(router, "GET", "/example?a=100")
+	PerformRequest(router, http.MethodGet, "/example?a=100")
 	assert.Contains(t, buffer.String(), "200")
-	assert.Contains(t, buffer.String(), "GET")
+	assert.Contains(t, buffer.String(), http.MethodGet)
 	assert.Contains(t, buffer.String(), "/example")
 	assert.Contains(t, buffer.String(), "a=100")
 
@@ -41,21 +41,21 @@ func TestLogger(t *testing.T) {
 	// like integration tests because they test the whole logging process rather
 	// than individual functions.  Im not sure where these should go.
 	buffer.Reset()
-	PerformRequest(router, "POST", "/example")
+	PerformRequest(router, http.MethodPost, "/example")
 	assert.Contains(t, buffer.String(), "200")
-	assert.Contains(t, buffer.String(), "POST")
+	assert.Contains(t, buffer.String(), http.MethodPost)
 	assert.Contains(t, buffer.String(), "/example")
 
 	buffer.Reset()
-	PerformRequest(router, "PUT", "/example")
+	PerformRequest(router, http.MethodPut, "/example")
 	assert.Contains(t, buffer.String(), "200")
-	assert.Contains(t, buffer.String(), "PUT")
+	assert.Contains(t, buffer.String(), http.MethodPut)
 	assert.Contains(t, buffer.String(), "/example")
 
 	buffer.Reset()
-	PerformRequest(router, "DELETE", "/example")
+	PerformRequest(router, http.MethodDelete, "/example")
 	assert.Contains(t, buffer.String(), "200")
-	assert.Contains(t, buffer.String(), "DELETE")
+	assert.Contains(t, buffer.String(), http.MethodDelete)
 	assert.Contains(t, buffer.String(), "/example")
 
 	buffer.Reset()
@@ -77,9 +77,9 @@ func TestLogger(t *testing.T) {
 	assert.Contains(t, buffer.String(), "/example")
 
 	buffer.Reset()
-	PerformRequest(router, "GET", "/notfound")
+	PerformRequest(router, http.MethodGet, "/notfound")
 	assert.Contains(t, buffer.String(), "404")
-	assert.Contains(t, buffer.String(), "GET")
+	assert.Contains(t, buffer.String(), http.MethodGet)
 	assert.Contains(t, buffer.String(), "/notfound")
 }
 
@@ -95,9 +95,9 @@ func TestLoggerWithConfig(t *testing.T) {
 	router.HEAD("/example", func(c *Context) {})
 	router.OPTIONS("/example", func(c *Context) {})
 
-	PerformRequest(router, "GET", "/example?a=100")
+	PerformRequest(router, http.MethodGet, "/example?a=100")
 	assert.Contains(t, buffer.String(), "200")
-	assert.Contains(t, buffer.String(), "GET")
+	assert.Contains(t, buffer.String(), http.MethodGet)
 	assert.Contains(t, buffer.String(), "/example")
 	assert.Contains(t, buffer.String(), "a=100")
 
@@ -105,21 +105,21 @@ func TestLoggerWithConfig(t *testing.T) {
 	// like integration tests because they test the whole logging process rather
 	// than individual functions.  Im not sure where these should go.
 	buffer.Reset()
-	PerformRequest(router, "POST", "/example")
+	PerformRequest(router, http.MethodPost, "/example")
 	assert.Contains(t, buffer.String(), "200")
-	assert.Contains(t, buffer.String(), "POST")
+	assert.Contains(t, buffer.String(), http.MethodPost)
 	assert.Contains(t, buffer.String(), "/example")
 
 	buffer.Reset()
-	PerformRequest(router, "PUT", "/example")
+	PerformRequest(router, http.MethodPut, "/example")
 	assert.Contains(t, buffer.String(), "200")
-	assert.Contains(t, buffer.String(), "PUT")
+	assert.Contains(t, buffer.String(), http.MethodPut)
 	assert.Contains(t, buffer.String(), "/example")
 
 	buffer.Reset()
-	PerformRequest(router, "DELETE", "/example")
+	PerformRequest(router, http.MethodDelete, "/example")
 	assert.Contains(t, buffer.String(), "200")
-	assert.Contains(t, buffer.String(), "DELETE")
+	assert.Contains(t, buffer.String(), http.MethodDelete)
 	assert.Contains(t, buffer.String(), "/example")
 
 	buffer.Reset()
@@ -141,9 +141,9 @@ func TestLoggerWithConfig(t *testing.T) {
 	assert.Contains(t, buffer.String(), "/example")
 
 	buffer.Reset()
-	PerformRequest(router, "GET", "/notfound")
+	PerformRequest(router, http.MethodGet, "/notfound")
 	assert.Contains(t, buffer.String(), "404")
-	assert.Contains(t, buffer.String(), "GET")
+	assert.Contains(t, buffer.String(), http.MethodGet)
 	assert.Contains(t, buffer.String(), "/notfound")
 }
 
@@ -169,12 +169,12 @@ func TestLoggerWithFormatter(t *testing.T) {
 		)
 	}))
 	router.GET("/example", func(c *Context) {})
-	PerformRequest(router, "GET", "/example?a=100")
+	PerformRequest(router, http.MethodGet, "/example?a=100")
 
 	// output test
 	assert.Contains(t, buffer.String(), "[FORMATTER TEST]")
 	assert.Contains(t, buffer.String(), "200")
-	assert.Contains(t, buffer.String(), "GET")
+	assert.Contains(t, buffer.String(), http.MethodGet)
 	assert.Contains(t, buffer.String(), "/example")
 	assert.Contains(t, buffer.String(), "a=100")
 }
@@ -210,12 +210,12 @@ func TestLoggerWithConfigFormatting(t *testing.T) {
 		gotKeys = c.Keys
 		time.Sleep(time.Millisecond)
 	})
-	PerformRequest(router, "GET", "/example?a=100")
+	PerformRequest(router, http.MethodGet, "/example?a=100")
 
 	// output test
 	assert.Contains(t, buffer.String(), "[FORMATTER TEST]")
 	assert.Contains(t, buffer.String(), "200")
-	assert.Contains(t, buffer.String(), "GET")
+	assert.Contains(t, buffer.String(), http.MethodGet)
 	assert.Contains(t, buffer.String(), "/example")
 	assert.Contains(t, buffer.String(), "a=100")
 
@@ -225,7 +225,7 @@ func TestLoggerWithConfigFormatting(t *testing.T) {
 	assert.Equal(t, 200, gotParam.StatusCode)
 	assert.NotEmpty(t, gotParam.Latency)
 	assert.Equal(t, "20.20.20.20", gotParam.ClientIP)
-	assert.Equal(t, "GET", gotParam.Method)
+	assert.Equal(t, http.MethodGet, gotParam.Method)
 	assert.Equal(t, "/example?a=100", gotParam.Path)
 	assert.Empty(t, gotParam.ErrorMessage)
 	assert.Equal(t, gotKeys, gotParam.Keys)
@@ -239,7 +239,7 @@ func TestDefaultLogFormatter(t *testing.T) {
 		StatusCode:   200,
 		Latency:      time.Second * 5,
 		ClientIP:     "20.20.20.20",
-		Method:       "GET",
+		Method:       http.MethodGet,
 		Path:         "/",
 		ErrorMessage: "",
 		isTerm:       false,
@@ -250,7 +250,7 @@ func TestDefaultLogFormatter(t *testing.T) {
 		StatusCode:   200,
 		Latency:      time.Second * 5,
 		ClientIP:     "20.20.20.20",
-		Method:       "GET",
+		Method:       http.MethodGet,
 		Path:         "/",
 		ErrorMessage: "",
 		isTerm:       true,
@@ -260,7 +260,7 @@ func TestDefaultLogFormatter(t *testing.T) {
 		StatusCode:   200,
 		Latency:      time.Millisecond * 9876543210,
 		ClientIP:     "20.20.20.20",
-		Method:       "GET",
+		Method:       http.MethodGet,
 		Path:         "/",
 		ErrorMessage: "",
 		isTerm:       true,
@@ -271,7 +271,7 @@ func TestDefaultLogFormatter(t *testing.T) {
 		StatusCode:   200,
 		Latency:      time.Millisecond * 9876543210,
 		ClientIP:     "20.20.20.20",
-		Method:       "GET",
+		Method:       http.MethodGet,
 		Path:         "/",
 		ErrorMessage: "",
 		isTerm:       false,
@@ -292,10 +292,10 @@ func TestColorForMethod(t *testing.T) {
 		return p.MethodColor()
 	}
 
-	assert.Equal(t, blue, colorForMethod("GET"), "get should be blue")
-	assert.Equal(t, cyan, colorForMethod("POST"), "post should be cyan")
-	assert.Equal(t, yellow, colorForMethod("PUT"), "put should be yellow")
-	assert.Equal(t, red, colorForMethod("DELETE"), "delete should be red")
+	assert.Equal(t, blue, colorForMethod(http.MethodGet), "get should be blue")
+	assert.Equal(t, cyan, colorForMethod(http.MethodPost), "post should be cyan")
+	assert.Equal(t, yellow, colorForMethod(http.MethodPut), "put should be yellow")
+	assert.Equal(t, red, colorForMethod(http.MethodDelete), "delete should be red")
 	assert.Equal(t, green, colorForMethod("PATCH"), "patch should be green")
 	assert.Equal(t, magenta, colorForMethod("HEAD"), "head should be magenta")
 	assert.Equal(t, white, colorForMethod("OPTIONS"), "options should be white")
@@ -329,13 +329,13 @@ func TestIsOutputColor(t *testing.T) {
 	}
 
 	consoleColorMode = autoColor
-	assert.Equal(t, true, p.IsOutputColor())
+	assert.True(t, p.IsOutputColor())
 
 	ForceConsoleColor()
-	assert.Equal(t, true, p.IsOutputColor())
+	assert.True(t, p.IsOutputColor())
 
 	DisableConsoleColor()
-	assert.Equal(t, false, p.IsOutputColor())
+	assert.False(t, p.IsOutputColor())
 
 	// test with isTerm flag false.
 	p = LogFormatterParams{
@@ -343,13 +343,13 @@ func TestIsOutputColor(t *testing.T) {
 	}
 
 	consoleColorMode = autoColor
-	assert.Equal(t, false, p.IsOutputColor())
+	assert.False(t, p.IsOutputColor())
 
 	ForceConsoleColor()
-	assert.Equal(t, true, p.IsOutputColor())
+	assert.True(t, p.IsOutputColor())
 
 	DisableConsoleColor()
-	assert.Equal(t, false, p.IsOutputColor())
+	assert.False(t, p.IsOutputColor())
 
 	// reset console color mode.
 	consoleColorMode = autoColor
@@ -369,15 +369,15 @@ func TestErrorLogger(t *testing.T) {
 		c.String(http.StatusInternalServerError, "hola!")
 	})
 
-	w := PerformRequest(router, "GET", "/error")
+	w := PerformRequest(router, http.MethodGet, "/error")
 	assert.Equal(t, http.StatusOK, w.Code)
 	assert.Equal(t, "{\"error\":\"this is an error\"}", w.Body.String())
 
-	w = PerformRequest(router, "GET", "/abort")
+	w = PerformRequest(router, http.MethodGet, "/abort")
 	assert.Equal(t, http.StatusUnauthorized, w.Code)
 	assert.Equal(t, "{\"error\":\"no authorized\"}", w.Body.String())
 
-	w = PerformRequest(router, "GET", "/print")
+	w = PerformRequest(router, http.MethodGet, "/print")
 	assert.Equal(t, http.StatusInternalServerError, w.Code)
 	assert.Equal(t, "hola!{\"error\":\"this is an error\"}", w.Body.String())
 }
@@ -389,11 +389,11 @@ func TestLoggerWithWriterSkippingPaths(t *testing.T) {
 	router.GET("/logged", func(c *Context) {})
 	router.GET("/skipped", func(c *Context) {})
 
-	PerformRequest(router, "GET", "/logged")
+	PerformRequest(router, http.MethodGet, "/logged")
 	assert.Contains(t, buffer.String(), "200")
 
 	buffer.Reset()
-	PerformRequest(router, "GET", "/skipped")
+	PerformRequest(router, http.MethodGet, "/skipped")
 	assert.Contains(t, buffer.String(), "")
 }
 
@@ -407,11 +407,11 @@ func TestLoggerWithConfigSkippingPaths(t *testing.T) {
 	router.GET("/logged", func(c *Context) {})
 	router.GET("/skipped", func(c *Context) {})
 
-	PerformRequest(router, "GET", "/logged")
+	PerformRequest(router, http.MethodGet, "/logged")
 	assert.Contains(t, buffer.String(), "200")
 
 	buffer.Reset()
-	PerformRequest(router, "GET", "/skipped")
+	PerformRequest(router, http.MethodGet, "/skipped")
 	assert.Contains(t, buffer.String(), "")
 }
 
@@ -427,11 +427,11 @@ func TestLoggerWithConfigSkipper(t *testing.T) {
 	router.GET("/logged", func(c *Context) { c.Status(http.StatusOK) })
 	router.GET("/skipped", func(c *Context) { c.Status(http.StatusNoContent) })
 
-	PerformRequest(router, "GET", "/logged")
+	PerformRequest(router, http.MethodGet, "/logged")
 	assert.Contains(t, buffer.String(), "200")
 
 	buffer.Reset()
-	PerformRequest(router, "GET", "/skipped")
+	PerformRequest(router, http.MethodGet, "/skipped")
 	assert.Contains(t, buffer.String(), "")
 }
 
diff --git a/middleware_test.go b/middleware_test.go
index acdf89c..eafc60a 100644
--- a/middleware_test.go
+++ b/middleware_test.go
@@ -35,7 +35,7 @@ func TestMiddlewareGeneralCase(t *testing.T) {
 		signature += " XX "
 	})
 	// RUN
-	w := PerformRequest(router, "GET", "/")
+	w := PerformRequest(router, http.MethodGet, "/")
 
 	// TEST
 	assert.Equal(t, http.StatusOK, w.Code)
@@ -71,7 +71,7 @@ func TestMiddlewareNoRoute(t *testing.T) {
 		signature += " X "
 	})
 	// RUN
-	w := PerformRequest(router, "GET", "/")
+	w := PerformRequest(router, http.MethodGet, "/")
 
 	// TEST
 	assert.Equal(t, http.StatusNotFound, w.Code)
@@ -108,7 +108,7 @@ func TestMiddlewareNoMethodEnabled(t *testing.T) {
 		signature += " XX "
 	})
 	// RUN
-	w := PerformRequest(router, "GET", "/")
+	w := PerformRequest(router, http.MethodGet, "/")
 
 	// TEST
 	assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
@@ -149,7 +149,7 @@ func TestMiddlewareNoMethodDisabled(t *testing.T) {
 	})
 
 	// RUN
-	w := PerformRequest(router, "GET", "/")
+	w := PerformRequest(router, http.MethodGet, "/")
 
 	// TEST
 	assert.Equal(t, http.StatusNotFound, w.Code)
@@ -175,7 +175,7 @@ func TestMiddlewareAbort(t *testing.T) {
 	})
 
 	// RUN
-	w := PerformRequest(router, "GET", "/")
+	w := PerformRequest(router, http.MethodGet, "/")
 
 	// TEST
 	assert.Equal(t, http.StatusUnauthorized, w.Code)
@@ -196,7 +196,7 @@ func TestMiddlewareAbortHandlersChainAndNext(t *testing.T) {
 		c.Next()
 	})
 	// RUN
-	w := PerformRequest(router, "GET", "/")
+	w := PerformRequest(router, http.MethodGet, "/")
 
 	// TEST
 	assert.Equal(t, http.StatusGone, w.Code)
@@ -219,7 +219,7 @@ func TestMiddlewareFailHandlersChain(t *testing.T) {
 		signature += "C"
 	})
 	// RUN
-	w := PerformRequest(router, "GET", "/")
+	w := PerformRequest(router, http.MethodGet, "/")
 
 	// TEST
 	assert.Equal(t, http.StatusInternalServerError, w.Code)
@@ -246,7 +246,7 @@ func TestMiddlewareWrite(t *testing.T) {
 		})
 	})
 
-	w := PerformRequest(router, "GET", "/")
+	w := PerformRequest(router, http.MethodGet, "/")
 
 	assert.Equal(t, http.StatusBadRequest, w.Code)
 	assert.Equal(t, strings.Replace("hola\n<map><foo>bar</foo></map>{\"foo\":\"bar\"}{\"foo\":\"bar\"}event:test\ndata:message\n\n", " ", "", -1), strings.Replace(w.Body.String(), " ", "", -1))
diff --git a/path_test.go b/path_test.go
index 864302f..2269b78 100644
--- a/path_test.go
+++ b/path_test.go
@@ -87,7 +87,7 @@ func TestPathCleanMallocs(t *testing.T) {
 
 	for _, test := range cleanTests {
 		allocs := testing.AllocsPerRun(100, func() { cleanPath(test.result) })
-		assert.EqualValues(t, allocs, 0)
+		assert.InDelta(t, 0, allocs, 0.01)
 	}
 }
 
diff --git a/recovery_test.go b/recovery_test.go
index fa8ab89..08eec1e 100644
--- a/recovery_test.go
+++ b/recovery_test.go
@@ -5,7 +5,6 @@
 package gin
 
 import (
-	"fmt"
 	"net"
 	"net/http"
 	"os"
@@ -26,14 +25,14 @@ func TestPanicClean(t *testing.T) {
 		panic("Oupps, Houston, we have a problem")
 	})
 	// RUN
-	w := PerformRequest(router, "GET", "/recovery",
+	w := PerformRequest(router, http.MethodGet, "/recovery",
 		header{
 			Key:   "Host",
 			Value: "www.google.com",
 		},
 		header{
 			Key:   "Authorization",
-			Value: fmt.Sprintf("Bearer %s", password),
+			Value: "Bearer " + password,
 		},
 		header{
 			Key:   "Content-Type",
@@ -56,7 +55,7 @@ func TestPanicInHandler(t *testing.T) {
 		panic("Oupps, Houston, we have a problem")
 	})
 	// RUN
-	w := PerformRequest(router, "GET", "/recovery")
+	w := PerformRequest(router, http.MethodGet, "/recovery")
 	// TEST
 	assert.Equal(t, http.StatusInternalServerError, w.Code)
 	assert.Contains(t, buffer.String(), "panic recovered")
@@ -67,7 +66,7 @@ func TestPanicInHandler(t *testing.T) {
 	// Debug mode prints the request
 	SetMode(DebugMode)
 	// RUN
-	w = PerformRequest(router, "GET", "/recovery")
+	w = PerformRequest(router, http.MethodGet, "/recovery")
 	// TEST
 	assert.Equal(t, http.StatusInternalServerError, w.Code)
 	assert.Contains(t, buffer.String(), "GET /recovery")
@@ -84,7 +83,7 @@ func TestPanicWithAbort(t *testing.T) {
 		panic("Oupps, Houston, we have a problem")
 	})
 	// RUN
-	w := PerformRequest(router, "GET", "/recovery")
+	w := PerformRequest(router, http.MethodGet, "/recovery")
 	// TEST
 	assert.Equal(t, http.StatusBadRequest, w.Code)
 }
@@ -135,7 +134,7 @@ func TestPanicWithBrokenPipe(t *testing.T) {
 				panic(e)
 			})
 			// RUN
-			w := PerformRequest(router, "GET", "/recovery")
+			w := PerformRequest(router, http.MethodGet, "/recovery")
 			// TEST
 			assert.Equal(t, expectCode, w.Code)
 			assert.Contains(t, strings.ToLower(buf.String()), expectMsg)
@@ -156,7 +155,7 @@ func TestCustomRecoveryWithWriter(t *testing.T) {
 		panic("Oupps, Houston, we have a problem")
 	})
 	// RUN
-	w := PerformRequest(router, "GET", "/recovery")
+	w := PerformRequest(router, http.MethodGet, "/recovery")
 	// TEST
 	assert.Equal(t, http.StatusBadRequest, w.Code)
 	assert.Contains(t, buffer.String(), "panic recovered")
@@ -167,7 +166,7 @@ func TestCustomRecoveryWithWriter(t *testing.T) {
 	// Debug mode prints the request
 	SetMode(DebugMode)
 	// RUN
-	w = PerformRequest(router, "GET", "/recovery")
+	w = PerformRequest(router, http.MethodGet, "/recovery")
 	// TEST
 	assert.Equal(t, http.StatusBadRequest, w.Code)
 	assert.Contains(t, buffer.String(), "GET /recovery")
@@ -191,7 +190,7 @@ func TestCustomRecovery(t *testing.T) {
 		panic("Oupps, Houston, we have a problem")
 	})
 	// RUN
-	w := PerformRequest(router, "GET", "/recovery")
+	w := PerformRequest(router, http.MethodGet, "/recovery")
 	// TEST
 	assert.Equal(t, http.StatusBadRequest, w.Code)
 	assert.Contains(t, buffer.String(), "panic recovered")
@@ -202,7 +201,7 @@ func TestCustomRecovery(t *testing.T) {
 	// Debug mode prints the request
 	SetMode(DebugMode)
 	// RUN
-	w = PerformRequest(router, "GET", "/recovery")
+	w = PerformRequest(router, http.MethodGet, "/recovery")
 	// TEST
 	assert.Equal(t, http.StatusBadRequest, w.Code)
 	assert.Contains(t, buffer.String(), "GET /recovery")
@@ -226,7 +225,7 @@ func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) {
 		panic("Oupps, Houston, we have a problem")
 	})
 	// RUN
-	w := PerformRequest(router, "GET", "/recovery")
+	w := PerformRequest(router, http.MethodGet, "/recovery")
 	// TEST
 	assert.Equal(t, http.StatusBadRequest, w.Code)
 	assert.Contains(t, buffer.String(), "panic recovered")
@@ -237,7 +236,7 @@ func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) {
 	// Debug mode prints the request
 	SetMode(DebugMode)
 	// RUN
-	w = PerformRequest(router, "GET", "/recovery")
+	w = PerformRequest(router, http.MethodGet, "/recovery")
 	// TEST
 	assert.Equal(t, http.StatusBadRequest, w.Code)
 	assert.Contains(t, buffer.String(), "GET /recovery")
diff --git a/render/render_msgpack_test.go b/render/render_msgpack_test.go
index db4b71e..579897c 100644
--- a/render/render_msgpack_test.go
+++ b/render/render_msgpack_test.go
@@ -12,6 +12,7 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"github.com/ugorji/go/codec"
 )
 
@@ -29,7 +30,7 @@ func TestRenderMsgPack(t *testing.T) {
 
 	err := (MsgPack{data}).Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	h := new(codec.MsgpackHandle)
 	assert.NotNil(t, h)
@@ -37,7 +38,7 @@ func TestRenderMsgPack(t *testing.T) {
 	assert.NotNil(t, buf)
 	err = codec.NewEncoder(buf, h).Encode(data)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, w.Body.String(), buf.String())
 	assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type"))
 }
diff --git a/render/render_test.go b/render/render_test.go
index 145f131..ad633b0 100644
--- a/render/render_test.go
+++ b/render/render_test.go
@@ -18,6 +18,7 @@ import (
 	"github.com/gin-gonic/gin/internal/json"
 	testdata "github.com/gin-gonic/gin/testdata/protoexample"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"google.golang.org/protobuf/proto"
 )
 
@@ -36,7 +37,7 @@ func TestRenderJSON(t *testing.T) {
 
 	err := (JSON{data}).Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
 	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
 }
@@ -46,7 +47,7 @@ func TestRenderJSONError(t *testing.T) {
 	data := make(chan int)
 
 	// json: unsupported type: chan int
-	assert.Error(t, (JSON{data}).Render(w))
+	require.Error(t, (JSON{data}).Render(w))
 }
 
 func TestRenderIndentedJSON(t *testing.T) {
@@ -58,7 +59,7 @@ func TestRenderIndentedJSON(t *testing.T) {
 
 	err := (IndentedJSON{data}).Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "{\n    \"bar\": \"foo\",\n    \"foo\": \"bar\"\n}", w.Body.String())
 	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
 }
@@ -69,7 +70,7 @@ func TestRenderIndentedJSONPanics(t *testing.T) {
 
 	// json: unsupported type: chan int
 	err := (IndentedJSON{data}).Render(w)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestRenderSecureJSON(t *testing.T) {
@@ -83,7 +84,7 @@ func TestRenderSecureJSON(t *testing.T) {
 
 	err1 := (SecureJSON{"while(1);", data}).Render(w1)
 
-	assert.NoError(t, err1)
+	require.NoError(t, err1)
 	assert.Equal(t, "{\"foo\":\"bar\"}", w1.Body.String())
 	assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
 
@@ -95,7 +96,7 @@ func TestRenderSecureJSON(t *testing.T) {
 	}}
 
 	err2 := (SecureJSON{"while(1);", datas}).Render(w2)
-	assert.NoError(t, err2)
+	require.NoError(t, err2)
 	assert.Equal(t, "while(1);[{\"foo\":\"bar\"},{\"bar\":\"foo\"}]", w2.Body.String())
 	assert.Equal(t, "application/json; charset=utf-8", w2.Header().Get("Content-Type"))
 }
@@ -106,7 +107,7 @@ func TestRenderSecureJSONFail(t *testing.T) {
 
 	// json: unsupported type: chan int
 	err := (SecureJSON{"while(1);", data}).Render(w)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestRenderJsonpJSON(t *testing.T) {
@@ -120,7 +121,7 @@ func TestRenderJsonpJSON(t *testing.T) {
 
 	err1 := (JsonpJSON{"x", data}).Render(w1)
 
-	assert.NoError(t, err1)
+	require.NoError(t, err1)
 	assert.Equal(t, "x({\"foo\":\"bar\"});", w1.Body.String())
 	assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type"))
 
@@ -132,7 +133,7 @@ func TestRenderJsonpJSON(t *testing.T) {
 	}}
 
 	err2 := (JsonpJSON{"x", datas}).Render(w2)
-	assert.NoError(t, err2)
+	require.NoError(t, err2)
 	assert.Equal(t, "x([{\"foo\":\"bar\"},{\"bar\":\"foo\"}]);", w2.Body.String())
 	assert.Equal(t, "application/javascript; charset=utf-8", w2.Header().Get("Content-Type"))
 }
@@ -191,7 +192,7 @@ func TestRenderJsonpJSONError2(t *testing.T) {
 	assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
 
 	e := (JsonpJSON{"", data}).Render(w)
-	assert.NoError(t, e)
+	require.NoError(t, e)
 
 	assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
 	assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
@@ -203,7 +204,7 @@ func TestRenderJsonpJSONFail(t *testing.T) {
 
 	// json: unsupported type: chan int
 	err := (JsonpJSON{"x", data}).Render(w)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestRenderAsciiJSON(t *testing.T) {
@@ -215,7 +216,7 @@ func TestRenderAsciiJSON(t *testing.T) {
 
 	err := (AsciiJSON{data1}).Render(w1)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String())
 	assert.Equal(t, "application/json", w1.Header().Get("Content-Type"))
 
@@ -223,7 +224,7 @@ func TestRenderAsciiJSON(t *testing.T) {
 	data2 := 3.1415926
 
 	err = (AsciiJSON{data2}).Render(w2)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "3.1415926", w2.Body.String())
 }
 
@@ -232,7 +233,7 @@ func TestRenderAsciiJSONFail(t *testing.T) {
 	data := make(chan int)
 
 	// json: unsupported type: chan int
-	assert.Error(t, (AsciiJSON{data}).Render(w))
+	require.Error(t, (AsciiJSON{data}).Render(w))
 }
 
 func TestRenderPureJSON(t *testing.T) {
@@ -242,7 +243,7 @@ func TestRenderPureJSON(t *testing.T) {
 		"html": "<b>",
 	}
 	err := (PureJSON{data}).Render(w)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
 	assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
 }
@@ -283,7 +284,7 @@ b:
 	assert.Equal(t, "application/yaml; charset=utf-8", w.Header().Get("Content-Type"))
 
 	err := (YAML{data}).Render(w)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "|4-\n    a : Easy!\n    b:\n    \tc: 2\n    \td: [3, 4]\n    \t\n", w.Body.String())
 	assert.Equal(t, "application/yaml; charset=utf-8", w.Header().Get("Content-Type"))
 }
@@ -298,7 +299,7 @@ func (ft *fail) MarshalYAML() (any, error) {
 func TestRenderYAMLFail(t *testing.T) {
 	w := httptest.NewRecorder()
 	err := (YAML{&fail{}}).Render(w)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestRenderTOML(t *testing.T) {
@@ -311,7 +312,7 @@ func TestRenderTOML(t *testing.T) {
 	assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type"))
 
 	err := (TOML{data}).Render(w)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "foo = 'bar'\nhtml = '<b>'\n", w.Body.String())
 	assert.Equal(t, "application/toml; charset=utf-8", w.Header().Get("Content-Type"))
 }
@@ -319,7 +320,7 @@ func TestRenderTOML(t *testing.T) {
 func TestRenderTOMLFail(t *testing.T) {
 	w := httptest.NewRecorder()
 	err := (TOML{net.IPv4bcast}).Render(w)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 // test Protobuf rendering
@@ -334,12 +335,12 @@ func TestRenderProtoBuf(t *testing.T) {
 
 	(ProtoBuf{data}).WriteContentType(w)
 	protoData, err := proto.Marshal(data)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
 
 	err = (ProtoBuf{data}).Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, string(protoData), w.Body.String())
 	assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
 }
@@ -348,7 +349,7 @@ func TestRenderProtoBufFail(t *testing.T) {
 	w := httptest.NewRecorder()
 	data := &testdata.Test{}
 	err := (ProtoBuf{data}).Render(w)
-	assert.Error(t, err)
+	require.Error(t, err)
 }
 
 func TestRenderXML(t *testing.T) {
@@ -362,14 +363,14 @@ func TestRenderXML(t *testing.T) {
 
 	err := (XML{data}).Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
 	assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
 }
 
 func TestRenderRedirect(t *testing.T) {
-	req, err := http.NewRequest("GET", "/test-redirect", nil)
-	assert.NoError(t, err)
+	req, err := http.NewRequest(http.MethodGet, "/test-redirect", nil)
+	require.NoError(t, err)
 
 	data1 := Redirect{
 		Code:     http.StatusMovedPermanently,
@@ -379,7 +380,7 @@ func TestRenderRedirect(t *testing.T) {
 
 	w := httptest.NewRecorder()
 	err = data1.Render(w)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	data2 := Redirect{
 		Code:     http.StatusOK,
@@ -390,7 +391,7 @@ func TestRenderRedirect(t *testing.T) {
 	w = httptest.NewRecorder()
 	assert.PanicsWithValue(t, "Cannot redirect with status code 200", func() {
 		err := data2.Render(w)
-		assert.NoError(t, err)
+		require.NoError(t, err)
 	})
 
 	data3 := Redirect{
@@ -401,7 +402,7 @@ func TestRenderRedirect(t *testing.T) {
 
 	w = httptest.NewRecorder()
 	err = data3.Render(w)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	// only improve coverage
 	data2.WriteContentType(w)
@@ -416,7 +417,7 @@ func TestRenderData(t *testing.T) {
 		Data:        data,
 	}).Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "#!PNG some raw data", w.Body.String())
 	assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
 }
@@ -435,7 +436,7 @@ func TestRenderString(t *testing.T) {
 		Data:   []any{"manu", 2},
 	}).Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "hola manu 2", w.Body.String())
 	assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
 }
@@ -448,7 +449,7 @@ func TestRenderStringLenZero(t *testing.T) {
 		Data:   []any{},
 	}).Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "hola %s %d", w.Body.String())
 	assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
 }
@@ -464,7 +465,7 @@ func TestRenderHTMLTemplate(t *testing.T) {
 
 	err := instance.Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "Hello alexandernyquist", w.Body.String())
 	assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
 }
@@ -480,7 +481,7 @@ func TestRenderHTMLTemplateEmptyName(t *testing.T) {
 
 	err := instance.Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "Hello alexandernyquist", w.Body.String())
 	assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
 }
@@ -499,7 +500,7 @@ func TestRenderHTMLDebugFiles(t *testing.T) {
 
 	err := instance.Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "<h1>Hello thinkerou</h1>", w.Body.String())
 	assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
 }
@@ -518,7 +519,7 @@ func TestRenderHTMLDebugGlob(t *testing.T) {
 
 	err := instance.Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, "<h1>Hello thinkerou</h1>", w.Body.String())
 	assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
 }
@@ -548,7 +549,7 @@ func TestRenderReader(t *testing.T) {
 		Headers:       headers,
 	}).Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, body, w.Body.String())
 	assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
 	assert.Equal(t, strconv.Itoa(len(body)), w.Header().Get("Content-Length"))
@@ -571,7 +572,7 @@ func TestRenderReaderNoContentLength(t *testing.T) {
 		Headers:       headers,
 	}).Render(w)
 
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, body, w.Body.String())
 	assert.Equal(t, "image/png", w.Header().Get("Content-Type"))
 	assert.NotContains(t, "Content-Length", w.Header())
@@ -588,6 +589,6 @@ func TestRenderWriteError(t *testing.T) {
 		ResponseRecorder: httptest.NewRecorder(),
 	}
 	err := r.Render(ew)
-	assert.NotNil(t, err)
+	require.Error(t, err)
 	assert.Equal(t, `write "my-prefix:" error`, err.Error())
 }
diff --git a/response_writer_test.go b/response_writer_test.go
index 964aa30..259b8fa 100644
--- a/response_writer_test.go
+++ b/response_writer_test.go
@@ -10,6 +10,7 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 // TODO
@@ -95,13 +96,13 @@ func TestResponseWriterWrite(t *testing.T) {
 	assert.Equal(t, http.StatusOK, w.Status())
 	assert.Equal(t, http.StatusOK, testWriter.Code)
 	assert.Equal(t, "hola", testWriter.Body.String())
-	assert.NoError(t, err)
+	require.NoError(t, err)
 
 	n, err = w.Write([]byte(" adios"))
 	assert.Equal(t, 6, n)
 	assert.Equal(t, 10, w.Size())
 	assert.Equal(t, "hola adios", testWriter.Body.String())
-	assert.NoError(t, err)
+	require.NoError(t, err)
 }
 
 func TestResponseWriterHijack(t *testing.T) {
@@ -112,7 +113,7 @@ func TestResponseWriterHijack(t *testing.T) {
 
 	assert.Panics(t, func() {
 		_, _, err := w.Hijack()
-		assert.NoError(t, err)
+		require.NoError(t, err)
 	})
 	assert.True(t, w.Written())
 
@@ -135,7 +136,7 @@ func TestResponseWriterFlush(t *testing.T) {
 
 	// should return 500
 	resp, err := http.Get(testServer.URL)
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
 }
 
diff --git a/routes_test.go b/routes_test.go
index 73f393e..995ff51 100644
--- a/routes_test.go
+++ b/routes_test.go
@@ -13,6 +13,7 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 type header struct {
@@ -386,7 +387,7 @@ func TestRouteStaticFile(t *testing.T) {
 	}
 	defer os.Remove(f.Name())
 	_, err = f.WriteString("Gin Web Framework")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	f.Close()
 
 	dir, filename := filepath.Split(f.Name())
@@ -421,7 +422,7 @@ func TestRouteStaticFileFS(t *testing.T) {
 	}
 	defer os.Remove(f.Name())
 	_, err = f.WriteString("Gin Web Framework")
-	assert.NoError(t, err)
+	require.NoError(t, err)
 	f.Close()
 
 	dir, filename := filepath.Split(f.Name())
@@ -484,7 +485,7 @@ func TestRouterMiddlewareAndStatic(t *testing.T) {
 	// Content-Type='text/plain; charset=utf-8' when go version <= 1.16,
 	// else, Content-Type='text/x-go; charset=utf-8'
 	assert.NotEqual(t, "", w.Header().Get("Content-Type"))
-	assert.NotEqual(t, w.Header().Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST")
+	assert.NotEqual(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Last-Modified"))
 	assert.Equal(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Expires"))
 	assert.Equal(t, "Gin Framework", w.Header().Get("x-GIN"))
 }
@@ -522,8 +523,8 @@ func TestRouteNotAllowedEnabled3(t *testing.T) {
 	w := PerformRequest(router, http.MethodPut, "/path")
 	assert.Equal(t, http.StatusMethodNotAllowed, w.Code)
 	allowed := w.Header().Get("Allow")
-	assert.Contains(t, allowed, "GET")
-	assert.Contains(t, allowed, "POST")
+	assert.Contains(t, allowed, http.MethodGet)
+	assert.Contains(t, allowed, http.MethodPost)
 }
 
 func TestRouteNotAllowedDisabled(t *testing.T) {
@@ -556,10 +557,10 @@ func TestRouterNotFoundWithRemoveExtraSlash(t *testing.T) {
 		{"/nope", http.StatusNotFound, ""}, // NotFound
 	}
 	for _, tr := range testRoutes {
-		w := PerformRequest(router, "GET", tr.route)
+		w := PerformRequest(router, http.MethodGet, tr.route)
 		assert.Equal(t, tr.code, w.Code)
 		if w.Code != http.StatusNotFound {
-			assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location")))
+			assert.Equal(t, tr.location, w.Header().Get("Location"))
 		}
 	}
 }
@@ -589,7 +590,7 @@ func TestRouterNotFound(t *testing.T) {
 		w := PerformRequest(router, http.MethodGet, tr.route)
 		assert.Equal(t, tr.code, w.Code)
 		if w.Code != http.StatusNotFound {
-			assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location")))
+			assert.Equal(t, tr.location, w.Header().Get("Location"))
 		}
 	}
 
@@ -785,6 +786,6 @@ func TestEngineHandleMethodNotAllowedCornerCase(t *testing.T) {
 	v1.GET("/orgs/:id", handlerTest1)
 	v1.DELETE("/orgs/:id", handlerTest1)
 
-	w := PerformRequest(r, "GET", "/base/v1/user/groups")
+	w := PerformRequest(r, http.MethodGet, "/base/v1/user/groups")
 	assert.Equal(t, http.StatusNotFound, w.Code)
 }
diff --git a/tree_test.go b/tree_test.go
index 3aa3a59..74eb610 100644
--- a/tree_test.go
+++ b/tree_test.go
@@ -993,3 +993,28 @@ func TestTreeInvalidEscape(t *testing.T) {
 		}
 	}
 }
+
+func TestWildcardInvalidSlash(t *testing.T) {
+	const panicMsgPrefix = "no / before catch-all in path"
+
+	routes := map[string]bool{
+		"/foo/bar":  true,
+		"/foo/x*zy": false,
+		"/foo/b*r":  false,
+	}
+
+	for route, valid := range routes {
+		tree := &node{}
+		recv := catchPanic(func() {
+			tree.addRoute(route, nil)
+		})
+
+		if recv == nil != valid {
+			t.Fatalf("%s should be %t but got %v", route, valid, recv)
+		}
+
+		if rs, ok := recv.(string); recv != nil && (!ok || !strings.HasPrefix(rs, panicMsgPrefix)) {
+			t.Fatalf(`"Expected panic "%s" for route '%s', got "%v"`, panicMsgPrefix, route, recv)
+		}
+	}
+}
diff --git a/utils_test.go b/utils_test.go
index 058ddb9..8098c68 100644
--- a/utils_test.go
+++ b/utils_test.go
@@ -29,7 +29,7 @@ type testStruct struct {
 }
 
 func (t *testStruct) ServeHTTP(w http.ResponseWriter, req *http.Request) {
-	assert.Equal(t.T, "POST", req.Method)
+	assert.Equal(t.T, http.MethodPost, req.Method)
 	assert.Equal(t.T, "/path", req.URL.Path)
 	w.WriteHeader(http.StatusInternalServerError)
 	fmt.Fprint(w, "hello")
@@ -39,17 +39,17 @@ func TestWrap(t *testing.T) {
 	router := New()
 	router.POST("/path", WrapH(&testStruct{t}))
 	router.GET("/path2", WrapF(func(w http.ResponseWriter, req *http.Request) {
-		assert.Equal(t, "GET", req.Method)
+		assert.Equal(t, http.MethodGet, req.Method)
 		assert.Equal(t, "/path2", req.URL.Path)
 		w.WriteHeader(http.StatusBadRequest)
 		fmt.Fprint(w, "hola!")
 	}))
 
-	w := PerformRequest(router, "POST", "/path")
+	w := PerformRequest(router, http.MethodPost, "/path")
 	assert.Equal(t, http.StatusInternalServerError, w.Code)
 	assert.Equal(t, "hello", w.Body.String())
 
-	w = PerformRequest(router, "GET", "/path2")
+	w = PerformRequest(router, http.MethodGet, "/path2")
 	assert.Equal(t, http.StatusBadRequest, w.Code)
 	assert.Equal(t, "hola!", w.Body.String())
 }
@@ -119,13 +119,13 @@ func TestBindMiddleware(t *testing.T) {
 		called = true
 		value = c.MustGet(BindKey).(*bindTestStruct)
 	})
-	PerformRequest(router, "GET", "/?foo=hola&bar=10")
+	PerformRequest(router, http.MethodGet, "/?foo=hola&bar=10")
 	assert.True(t, called)
 	assert.Equal(t, "hola", value.Foo)
 	assert.Equal(t, 10, value.Bar)
 
 	called = false
-	PerformRequest(router, "GET", "/?foo=hola&bar=1")
+	PerformRequest(router, http.MethodGet, "/?foo=hola&bar=1")
 	assert.False(t, called)
 
 	assert.Panics(t, func() {
@@ -145,6 +145,6 @@ func TestMarshalXMLforH(t *testing.T) {
 }
 
 func TestIsASCII(t *testing.T) {
-	assert.Equal(t, isASCII("test"), true)
-	assert.Equal(t, isASCII("🧡💛💚💙💜"), false)
+	assert.True(t, isASCII("test"))
+	assert.False(t, isASCII("🧡💛💚💙💜"))
 }