mirror of https://github.com/gin-gonic/gin.git
Compare commits
23 Commits
eeaa150f35
...
525e4354a4
Author | SHA1 | Date |
---|---|---|
TimAndy | 525e4354a4 | |
TimAndy | 8363db7e38 | |
TimAndy | 672d629c23 | |
haesuo566 | e46bd52185 | |
Matthieu MOREL | e8d34d053f | |
Matthieu MOREL | 02c1144f31 | |
Bo-Yi Wu | f875d87283 | |
Konovalov Maxim | c8a3adc657 | |
Xinyu Kuo | ea53388e6e | |
Oskar Karpiński | 9d11234efe | |
Xinyu Kuo | 647311aba2 | |
tsukasa-ino | 299c6f30e3 | |
Enzo Lanzellotti | b080116a7f | |
wangjingcun | ad740d508f | |
takanuva15 | f05f966a08 | |
CC11001100 | 9d7c0e9e1a | |
demouth | f2c861a24f | |
Ahmad Saeed Goda | 28e57f58b1 | |
Jo YoHan | 3cb30679b5 | |
dependabot[bot] | cc4e11438c | |
Matthieu MOREL | 5f55c6a711 | |
Pierre-Henri Symoneaux | 626d55b0c0 | |
demouth | 9c081de9cd |
|
@ -26,7 +26,7 @@ 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
|
||||
|
|
|
@ -16,7 +16,10 @@ linters:
|
|||
- nakedret
|
||||
- nilerr
|
||||
- nolintlint
|
||||
- perfsprint
|
||||
- revive
|
||||
- testifylint
|
||||
- usestdlibvars
|
||||
- wastedassign
|
||||
|
||||
linters-settings:
|
||||
|
@ -33,6 +36,15 @@ linters-settings:
|
|||
- G112
|
||||
- G201
|
||||
- G203
|
||||
perfsprint:
|
||||
err-error: true
|
||||
errorf: true
|
||||
fiximports: true
|
||||
int-conversion: true
|
||||
sprintf1: true
|
||||
strconcat: true
|
||||
testifylint:
|
||||
enable-all: true
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
10
auth_test.go
10
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)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,8 +13,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin/codec/json"
|
||||
"github.com/gin-gonic/gin/internal/bytesconv"
|
||||
"github.com/gin-gonic/gin/internal/json"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -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
|
||||
|
@ -270,9 +333,9 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
|
|||
case multipart.FileHeader:
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||
return json.Api.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||
case reflect.Map:
|
||||
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||
return json.Api.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||
case reflect.Ptr:
|
||||
if !value.Elem().IsValid() {
|
||||
value.Set(reflect.New(value.Type().Elem()))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin/internal/json"
|
||||
"github.com/gin-gonic/gin/codec/json"
|
||||
)
|
||||
|
||||
// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
|
||||
|
@ -42,7 +42,7 @@ func (jsonBinding) BindBody(body []byte, obj any) error {
|
|||
}
|
||||
|
||||
func decodeJSON(r io.Reader, obj any) error {
|
||||
decoder := json.NewDecoder(r)
|
||||
decoder := json.Api.NewDecoder(r)
|
||||
if EnableDecoderUseNumber {
|
||||
decoder.UseNumber()
|
||||
}
|
||||
|
|
|
@ -5,8 +5,17 @@
|
|||
package binding
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gin-gonic/gin/codec/api"
|
||||
"github.com/gin-gonic/gin/codec/json"
|
||||
"github.com/gin-gonic/gin/render"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/modern-go/reflect2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -28,3 +37,184 @@ func TestJSONBindingBindBodyMap(t *testing.T) {
|
|||
assert.Equal(t, "FOO", s["foo"])
|
||||
assert.Equal(t, "world", s["hello"])
|
||||
}
|
||||
|
||||
func TestCustomJsonCodec(t *testing.T) {
|
||||
//Restore json encoding configuration after testing
|
||||
oldMarshal := json.Api
|
||||
defer func() {
|
||||
json.Api = oldMarshal
|
||||
}()
|
||||
//Custom json api
|
||||
json.Api = customJsonApi{}
|
||||
|
||||
//test decode json
|
||||
obj := customReq{}
|
||||
err := jsonBinding{}.BindBody([]byte(`{"time_empty":null,"time_struct": "2001-12-05 10:01:02.345","time_nil":null,"time_pointer":"2002-12-05 10:01:02.345"}`), &obj)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, zeroTime, obj.TimeEmpty)
|
||||
assert.Equal(t, time.Date(2001, 12, 05, 10, 01, 02, 345000000, time.Local), obj.TimeStruct)
|
||||
assert.Nil(t, obj.TimeNil)
|
||||
assert.Equal(t, time.Date(2002, 12, 05, 10, 01, 02, 345000000, time.Local), *obj.TimePointer)
|
||||
//test encode json
|
||||
w := httptest.NewRecorder()
|
||||
err2 := (render.PureJSON{Data: obj}).Render(w)
|
||||
require.NoError(t, err2)
|
||||
assert.Equal(t, "{\"time_empty\":null,\"time_struct\":\"2001-12-05 10:01:02.345\",\"time_nil\":null,\"time_pointer\":\"2002-12-05 10:01:02.345\"}\n", w.Body.String())
|
||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
type customReq struct {
|
||||
TimeEmpty time.Time `json:"time_empty"`
|
||||
TimeStruct time.Time `json:"time_struct"`
|
||||
TimeNil *time.Time `json:"time_nil"`
|
||||
TimePointer *time.Time `json:"time_pointer"`
|
||||
}
|
||||
|
||||
var customConfig = jsoniter.Config{
|
||||
EscapeHTML: true,
|
||||
SortMapKeys: true,
|
||||
ValidateJsonRawMessage: true,
|
||||
}.Froze()
|
||||
|
||||
func init() {
|
||||
customConfig.RegisterExtension(&TimeEx{})
|
||||
customConfig.RegisterExtension(&TimePointerEx{})
|
||||
}
|
||||
|
||||
type customJsonApi struct {
|
||||
}
|
||||
|
||||
func (j customJsonApi) Marshal(v any) ([]byte, error) {
|
||||
return customConfig.Marshal(v)
|
||||
}
|
||||
|
||||
func (j customJsonApi) Unmarshal(data []byte, v any) error {
|
||||
return customConfig.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func (j customJsonApi) MarshalIndent(v any, prefix, indent string) ([]byte, error) {
|
||||
return customConfig.MarshalIndent(v, prefix, indent)
|
||||
}
|
||||
|
||||
func (j customJsonApi) NewEncoder(writer io.Writer) api.JsonEncoder {
|
||||
return customConfig.NewEncoder(writer)
|
||||
}
|
||||
|
||||
func (j customJsonApi) NewDecoder(reader io.Reader) api.JsonDecoder {
|
||||
return customConfig.NewDecoder(reader)
|
||||
}
|
||||
|
||||
//region Time Extension
|
||||
|
||||
var (
|
||||
zeroTime = time.Time{}
|
||||
timeType = reflect2.TypeOfPtr((*time.Time)(nil)).Elem()
|
||||
defaultTimeCodec = &timeCodec{}
|
||||
)
|
||||
|
||||
type TimeEx struct {
|
||||
jsoniter.DummyExtension
|
||||
}
|
||||
|
||||
func (te *TimeEx) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
|
||||
if typ == timeType {
|
||||
return defaultTimeCodec
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (te *TimeEx) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
|
||||
if typ == timeType {
|
||||
return defaultTimeCodec
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type timeCodec struct {
|
||||
}
|
||||
|
||||
func (tc timeCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
t := *((*time.Time)(ptr))
|
||||
return t == zeroTime
|
||||
}
|
||||
|
||||
func (tc timeCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
t := *((*time.Time)(ptr))
|
||||
if t == zeroTime {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
stream.WriteString(t.In(time.Local).Format("2006-01-02 15:04:05.000"))
|
||||
}
|
||||
|
||||
func (tc timeCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
ts := iter.ReadString()
|
||||
if len(ts) == 0 {
|
||||
*((*time.Time)(ptr)) = zeroTime
|
||||
return
|
||||
}
|
||||
t, err := time.ParseInLocation("2006-01-02 15:04:05.000", ts, time.Local)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*((*time.Time)(ptr)) = t
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region *Time Extension
|
||||
|
||||
var (
|
||||
timePointerType = reflect2.TypeOfPtr((**time.Time)(nil)).Elem()
|
||||
defaultTimePointerCodec = &timePointerCodec{}
|
||||
)
|
||||
|
||||
type TimePointerEx struct {
|
||||
jsoniter.DummyExtension
|
||||
}
|
||||
|
||||
func (tpe *TimePointerEx) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
|
||||
if typ == timePointerType {
|
||||
return defaultTimePointerCodec
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tpe *TimePointerEx) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
|
||||
if typ == timePointerType {
|
||||
return defaultTimePointerCodec
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type timePointerCodec struct {
|
||||
}
|
||||
|
||||
func (tpc timePointerCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
t := *((**time.Time)(ptr))
|
||||
return t == nil || *t == zeroTime
|
||||
}
|
||||
|
||||
func (tpc timePointerCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
t := *((**time.Time)(ptr))
|
||||
if t == nil || *t == zeroTime {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
stream.WriteString(t.In(time.Local).Format("2006-01-02 15:04:05.000"))
|
||||
}
|
||||
|
||||
func (tpc timePointerCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
ts := iter.ReadString()
|
||||
if len(ts) == 0 {
|
||||
*((**time.Time)(ptr)) = nil
|
||||
return
|
||||
}
|
||||
t, err := time.ParseInLocation("2006-01-02 15:04:05.000", ts, time.Local)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*((**time.Time)(ptr)) = &t
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2022 Gin Core Team. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package api
|
||||
|
||||
import "io"
|
||||
|
||||
// JsonApi the api for json codec.
|
||||
type JsonApi interface {
|
||||
Marshal(v any) ([]byte, error)
|
||||
Unmarshal(data []byte, v any) error
|
||||
MarshalIndent(v any, prefix, indent string) ([]byte, error)
|
||||
NewEncoder(writer io.Writer) JsonEncoder
|
||||
NewDecoder(reader io.Reader) JsonDecoder
|
||||
}
|
||||
|
||||
// A JsonEncoder interface writes JSON values to an output stream.
|
||||
type JsonEncoder interface {
|
||||
// SetEscapeHTML specifies whether problematic HTML characters
|
||||
// should be escaped inside JSON quoted strings.
|
||||
// The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e
|
||||
// to avoid certain safety problems that can arise when embedding JSON in HTML.
|
||||
//
|
||||
// In non-HTML settings where the escaping interferes with the readability
|
||||
// of the output, SetEscapeHTML(false) disables this behavior.
|
||||
SetEscapeHTML(on bool)
|
||||
|
||||
// Encode writes the JSON encoding of v to the stream,
|
||||
// followed by a newline character.
|
||||
//
|
||||
// See the documentation for Marshal for details about the
|
||||
// conversion of Go values to JSON.
|
||||
Encode(v interface{}) error
|
||||
}
|
||||
|
||||
// A JsonDecoder interface reads and decodes JSON values from an input stream.
|
||||
type JsonDecoder interface {
|
||||
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
|
||||
// Number instead of as a float64.
|
||||
UseNumber()
|
||||
|
||||
// DisallowUnknownFields causes the Decoder to return an error when the destination
|
||||
// is a struct and the input contains object keys which do not match any
|
||||
// non-ignored, exported fields in the destination.
|
||||
DisallowUnknownFields()
|
||||
|
||||
// Decode reads the next JSON-encoded value from its
|
||||
// input and stores it in the value pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about
|
||||
// the conversion of JSON into a Go value.
|
||||
Decode(v interface{}) error
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package json
|
||||
|
||||
import "github.com/gin-gonic/gin/codec/api"
|
||||
|
||||
var Api api.JsonApi
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2017 Bo-Yi Wu. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go_json
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/gin-gonic/gin/codec/api"
|
||||
"github.com/goccy/go-json"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Api = gojsonApi{}
|
||||
}
|
||||
|
||||
type gojsonApi struct {
|
||||
}
|
||||
|
||||
func (j gojsonApi) Marshal(v any) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
func (j gojsonApi) Unmarshal(data []byte, v any) error {
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func (j gojsonApi) MarshalIndent(v any, prefix, indent string) ([]byte, error) {
|
||||
return json.MarshalIndent(v, prefix, indent)
|
||||
}
|
||||
|
||||
func (j gojsonApi) NewEncoder(writer io.Writer) api.JsonEncoder {
|
||||
return json.NewEncoder(writer)
|
||||
}
|
||||
|
||||
func (j gojsonApi) NewDecoder(reader io.Reader) api.JsonDecoder {
|
||||
return json.NewDecoder(reader)
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2017 Bo-Yi Wu. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !jsoniter && !go_json && !(sonic && avx && (linux || windows || darwin) && amd64)
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/gin-gonic/gin/codec/api"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Api = jsonApi{}
|
||||
}
|
||||
|
||||
type jsonApi struct {
|
||||
}
|
||||
|
||||
func (j jsonApi) Marshal(v any) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
func (j jsonApi) Unmarshal(data []byte, v any) error {
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func (j jsonApi) MarshalIndent(v any, prefix, indent string) ([]byte, error) {
|
||||
return json.MarshalIndent(v, prefix, indent)
|
||||
}
|
||||
|
||||
func (j jsonApi) NewEncoder(writer io.Writer) api.JsonEncoder {
|
||||
return json.NewEncoder(writer)
|
||||
}
|
||||
|
||||
func (j jsonApi) NewDecoder(reader io.Reader) api.JsonDecoder {
|
||||
return json.NewDecoder(reader)
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2017 Bo-Yi Wu. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build jsoniter
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/gin-gonic/gin/codec/api"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Api = jsoniterApi{}
|
||||
}
|
||||
|
||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
|
||||
type jsoniterApi struct {
|
||||
}
|
||||
|
||||
func (j jsoniterApi) Marshal(v any) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
func (j jsoniterApi) Unmarshal(data []byte, v any) error {
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func (j jsoniterApi) MarshalIndent(v any, prefix, indent string) ([]byte, error) {
|
||||
return json.MarshalIndent(v, prefix, indent)
|
||||
}
|
||||
|
||||
func (j jsoniterApi) NewEncoder(writer io.Writer) api.JsonEncoder {
|
||||
return json.NewEncoder(writer)
|
||||
}
|
||||
|
||||
func (j jsoniterApi) NewDecoder(reader io.Reader) api.JsonDecoder {
|
||||
return json.NewDecoder(reader)
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2022 Gin Core Team. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build sonic && avx && (linux || windows || darwin) && amd64
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/gin-gonic/gin/codec/api"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Api = sonicApi{}
|
||||
}
|
||||
|
||||
var json = sonic.ConfigStd
|
||||
|
||||
type sonicApi struct {
|
||||
}
|
||||
|
||||
func (j sonicApi) Marshal(v any) ([]byte, error) {
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
func (j sonicApi) Unmarshal(data []byte, v any) error {
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func (j sonicApi) MarshalIndent(v any, prefix, indent string) ([]byte, error) {
|
||||
return json.MarshalIndent(v, prefix, indent)
|
||||
}
|
||||
|
||||
func (j sonicApi) NewEncoder(writer io.Writer) api.JsonEncoder {
|
||||
return json.NewEncoder(writer)
|
||||
}
|
||||
|
||||
func (j sonicApi) NewDecoder(reader io.Reader) api.JsonDecoder {
|
||||
return json.NewDecoder(reader)
|
||||
}
|
189
context.go
189
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)
|
||||
}
|
||||
|
||||
// GetInt64 returns the value associated with the key as an integer.
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if len(perm) <= 0 {
|
||||
perm = append(perm, 0o750)
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(filepath.Dir(dst), perm[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.Chmod(filepath.Dir(dst), perm[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -811,7 +882,7 @@ 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)
|
||||
}
|
||||
|
|
641
context_test.go
641
context_test.go
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
|
|
193
docs/doc.md
193
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)
|
||||
|
@ -61,6 +63,7 @@
|
|||
- [http2 server push](#http2-server-push)
|
||||
- [Define format for the log of routes](#define-format-for-the-log-of-routes)
|
||||
- [Set and get a cookie](#set-and-get-a-cookie)
|
||||
- [Custom json codec at runtime](#custom-json-codec-at-runtime)
|
||||
- [Don't trust all proxies](#dont-trust-all-proxies)
|
||||
- [Testing](#testing)
|
||||
|
||||
|
@ -170,7 +173,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 +341,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 +517,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 +544,7 @@ func main() {
|
|||
router.GET("/data", func(c *gin.Context) {
|
||||
c.Status(http.StatusNotImplemented)
|
||||
})
|
||||
|
||||
|
||||
router.Run(":8080")
|
||||
}
|
||||
|
||||
|
@ -613,7 +616,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"`
|
||||
}
|
||||
|
||||
|
@ -861,6 +864,106 @@ Test it with:
|
|||
curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033"
|
||||
```
|
||||
|
||||
|
||||
### 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
|
||||
|
||||
See the [detail information](https://github.com/gin-gonic/gin/issues/846).
|
||||
|
@ -1150,7 +1253,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 +1302,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 +1337,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")
|
||||
}
|
||||
|
@ -2207,6 +2310,66 @@ func main() {
|
|||
}
|
||||
```
|
||||
|
||||
### Custom json codec at runtime
|
||||
|
||||
Gin support custom json serialization and deserialization logic without using compile tags.
|
||||
|
||||
1. Define a custom struct implements the `api.JsonApi` interface.
|
||||
|
||||
2. Before your engine starts, assign values to `json.Api` using the custom struct.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/codec/api"
|
||||
"github.com/gin-gonic/gin/codec/json"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
var customConfig = jsoniter.Config{
|
||||
EscapeHTML: true,
|
||||
SortMapKeys: true,
|
||||
ValidateJsonRawMessage: true,
|
||||
}.Froze()
|
||||
|
||||
// implement api.JsonApi
|
||||
type customJsonApi struct {
|
||||
}
|
||||
|
||||
func (j customJsonApi) Marshal(v any) ([]byte, error) {
|
||||
return customConfig.Marshal(v)
|
||||
}
|
||||
|
||||
func (j customJsonApi) Unmarshal(data []byte, v any) error {
|
||||
return customConfig.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func (j customJsonApi) MarshalIndent(v any, prefix, indent string) ([]byte, error) {
|
||||
return customConfig.MarshalIndent(v, prefix, indent)
|
||||
}
|
||||
|
||||
func (j customJsonApi) NewEncoder(writer io.Writer) api.JsonEncoder {
|
||||
return customConfig.NewEncoder(writer)
|
||||
}
|
||||
|
||||
func (j customJsonApi) NewDecoder(reader io.Reader) api.JsonDecoder {
|
||||
return customConfig.NewDecoder(reader)
|
||||
}
|
||||
|
||||
func main() {
|
||||
//Replace the default json api
|
||||
json.Api = customJsonApi{}
|
||||
|
||||
//Start your gin engine
|
||||
router := gin.Default()
|
||||
router.Run(":8080")
|
||||
}
|
||||
```
|
||||
|
||||
## Don't trust all proxies
|
||||
|
||||
Gin lets you specify which headers to hold the real client IP (if any),
|
||||
|
@ -2218,7 +2381,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 +2410,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
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin/internal/json"
|
||||
"github.com/gin-gonic/gin/codec/json"
|
||||
)
|
||||
|
||||
// ErrorType is an unsigned 64-bit error code as defined in the gin spec.
|
||||
|
@ -77,7 +77,7 @@ func (msg *Error) JSON() any {
|
|||
|
||||
// MarshalJSON implements the json.Marshaller interface.
|
||||
func (msg *Error) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(msg.JSON())
|
||||
return json.Api.Marshal(msg.JSON())
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
|
@ -157,7 +157,7 @@ func (a errorMsgs) JSON() any {
|
|||
|
||||
// MarshalJSON implements the json.Marshaller interface.
|
||||
func (a errorMsgs) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(a.JSON())
|
||||
return json.Api.Marshal(a.JSON())
|
||||
}
|
||||
|
||||
func (a errorMsgs) String() string {
|
||||
|
|
|
@ -9,8 +9,9 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin/internal/json"
|
||||
"github.com/gin-gonic/gin/codec/json"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
|
@ -32,7 +33,7 @@ func TestError(t *testing.T) {
|
|||
"meta": "some data",
|
||||
}, err.JSON())
|
||||
|
||||
jsonBytes, _ := json.Marshal(err)
|
||||
jsonBytes, _ := json.Api.Marshal(err)
|
||||
assert.Equal(t, "{\"error\":\"test error\",\"meta\":\"some data\"}", string(jsonBytes))
|
||||
|
||||
err.SetMeta(H{ //nolint: errcheck
|
||||
|
@ -91,13 +92,13 @@ Error #03: third
|
|||
H{"error": "second", "meta": "some data"},
|
||||
H{"error": "third", "status": "400"},
|
||||
}, errs.JSON())
|
||||
jsonBytes, _ := json.Marshal(errs)
|
||||
jsonBytes, _ := json.Api.Marshal(errs)
|
||||
assert.Equal(t, "[{\"error\":\"first\"},{\"error\":\"second\",\"meta\":\"some data\"},{\"error\":\"third\",\"status\":\"400\"}]", string(jsonBytes))
|
||||
errs = errorMsgs{
|
||||
{Err: errors.New("first"), Type: ErrorTypePrivate},
|
||||
}
|
||||
assert.Equal(t, H{"error": "first"}, errs.JSON())
|
||||
jsonBytes, _ = json.Marshal(errs)
|
||||
jsonBytes, _ = json.Api.Marshal(errs)
|
||||
assert.Equal(t, "{\"error\":\"first\"}", string(jsonBytes))
|
||||
|
||||
errs = errorMsgs{}
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
4
gin.go
4
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())
|
||||
|
@ -687,7 +687,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)
|
||||
|
|
|
@ -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") })
|
||||
|
|
104
gin_test.go
104
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())
|
||||
})
|
||||
|
@ -580,7 +581,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 +589,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 +598,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 +606,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 +614,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 +622,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 +630,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 +638,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 +654,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 +666,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
|||
"172.16.0.256",
|
||||
})
|
||||
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
// nil value
|
||||
|
@ -673,7 +674,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 +712,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 +729,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 +749,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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
14
go.mod
14
go.mod
|
@ -9,11 +9,12 @@ require (
|
|||
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/modern-go/reflect2 v1.0.2
|
||||
github.com/pelletier/go-toml/v2 v2.2.2
|
||||
github.com/quic-go/quic-go v0.43.1
|
||||
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.27.0
|
||||
google.golang.org/protobuf v1.34.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
@ -32,17 +33,16 @@ require (
|
|||
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/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/crypto v0.25.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/mod v0.17.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
)
|
||||
|
|
30
go.sum
30
go.sum
|
@ -33,8 +33,8 @@ 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=
|
||||
|
@ -92,24 +92,26 @@ 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/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
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/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.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
// Copyright 2017 Bo-Yi Wu. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go_json
|
||||
|
||||
package json
|
||||
|
||||
import json "github.com/goccy/go-json"
|
||||
|
||||
var (
|
||||
// Marshal is exported by gin/json package.
|
||||
Marshal = json.Marshal
|
||||
// Unmarshal is exported by gin/json package.
|
||||
Unmarshal = json.Unmarshal
|
||||
// MarshalIndent is exported by gin/json package.
|
||||
MarshalIndent = json.MarshalIndent
|
||||
// NewDecoder is exported by gin/json package.
|
||||
NewDecoder = json.NewDecoder
|
||||
// NewEncoder is exported by gin/json package.
|
||||
NewEncoder = json.NewEncoder
|
||||
)
|
|
@ -1,22 +0,0 @@
|
|||
// Copyright 2017 Bo-Yi Wu. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !jsoniter && !go_json && !(sonic && avx && (linux || windows || darwin) && amd64)
|
||||
|
||||
package json
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
var (
|
||||
// Marshal is exported by gin/json package.
|
||||
Marshal = json.Marshal
|
||||
// Unmarshal is exported by gin/json package.
|
||||
Unmarshal = json.Unmarshal
|
||||
// MarshalIndent is exported by gin/json package.
|
||||
MarshalIndent = json.MarshalIndent
|
||||
// NewDecoder is exported by gin/json package.
|
||||
NewDecoder = json.NewDecoder
|
||||
// NewEncoder is exported by gin/json package.
|
||||
NewEncoder = json.NewEncoder
|
||||
)
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright 2017 Bo-Yi Wu. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build jsoniter
|
||||
|
||||
package json
|
||||
|
||||
import jsoniter "github.com/json-iterator/go"
|
||||
|
||||
var (
|
||||
json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
// Marshal is exported by gin/json package.
|
||||
Marshal = json.Marshal
|
||||
// Unmarshal is exported by gin/json package.
|
||||
Unmarshal = json.Unmarshal
|
||||
// MarshalIndent is exported by gin/json package.
|
||||
MarshalIndent = json.MarshalIndent
|
||||
// NewDecoder is exported by gin/json package.
|
||||
NewDecoder = json.NewDecoder
|
||||
// NewEncoder is exported by gin/json package.
|
||||
NewEncoder = json.NewEncoder
|
||||
)
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright 2022 Gin Core Team. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build sonic && avx && (linux || windows || darwin) && amd64
|
||||
|
||||
package json
|
||||
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
var (
|
||||
json = sonic.ConfigStd
|
||||
// Marshal is exported by gin/json package.
|
||||
Marshal = json.Marshal
|
||||
// Unmarshal is exported by gin/json package.
|
||||
Unmarshal = json.Unmarshal
|
||||
// MarshalIndent is exported by gin/json package.
|
||||
MarshalIndent = json.MarshalIndent
|
||||
// NewDecoder is exported by gin/json package.
|
||||
NewDecoder = json.NewDecoder
|
||||
// NewEncoder is exported by gin/json package.
|
||||
NewEncoder = json.NewEncoder
|
||||
)
|
|
@ -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(), "")
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin/codec/json"
|
||||
"github.com/gin-gonic/gin/internal/bytesconv"
|
||||
"github.com/gin-gonic/gin/internal/json"
|
||||
)
|
||||
|
||||
// JSON contains the given interface object.
|
||||
|
@ -65,7 +65,7 @@ func (r JSON) WriteContentType(w http.ResponseWriter) {
|
|||
// WriteJSON marshals the given interface object and writes it with custom ContentType.
|
||||
func WriteJSON(w http.ResponseWriter, obj any) error {
|
||||
writeContentType(w, jsonContentType)
|
||||
jsonBytes, err := json.Marshal(obj)
|
||||
jsonBytes, err := json.Api.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ func WriteJSON(w http.ResponseWriter, obj any) error {
|
|||
// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType.
|
||||
func (r IndentedJSON) Render(w http.ResponseWriter) error {
|
||||
r.WriteContentType(w)
|
||||
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
|
||||
jsonBytes, err := json.Api.MarshalIndent(r.Data, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
|
|||
// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType.
|
||||
func (r SecureJSON) Render(w http.ResponseWriter) error {
|
||||
r.WriteContentType(w)
|
||||
jsonBytes, err := json.Marshal(r.Data)
|
||||
jsonBytes, err := json.Api.Marshal(r.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
|
|||
// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType.
|
||||
func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
|
||||
r.WriteContentType(w)
|
||||
ret, err := json.Marshal(r.Data)
|
||||
ret, err := json.Api.Marshal(r.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
|
|||
// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType.
|
||||
func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
|
||||
r.WriteContentType(w)
|
||||
ret, err := json.Marshal(r.Data)
|
||||
ret, err := json.Api.Marshal(r.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
|
|||
// Render (PureJSON) writes custom ContentType and encodes the given interface object.
|
||||
func (r PureJSON) Render(w http.ResponseWriter) error {
|
||||
r.WriteContentType(w)
|
||||
encoder := json.NewEncoder(w)
|
||||
encoder := json.Api.NewEncoder(w)
|
||||
encoder.SetEscapeHTML(false)
|
||||
return encoder.Encode(r.Data)
|
||||
}
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
|
|
|
@ -15,9 +15,10 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin/internal/json"
|
||||
"github.com/gin-gonic/gin/codec/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"))
|
||||
}
|
||||
|
@ -172,7 +173,7 @@ func TestRenderJsonpJSONError(t *testing.T) {
|
|||
err = jsonpJSON.Render(ew)
|
||||
assert.Equal(t, `write "`+`(`+`" error`, err.Error())
|
||||
|
||||
data, _ := json.Marshal(jsonpJSON.Data) // error was returned while writing data
|
||||
data, _ := json.Api.Marshal(jsonpJSON.Data) // error was returned while writing data
|
||||
ew.bufString = string(data)
|
||||
err = jsonpJSON.Render(ew)
|
||||
assert.Equal(t, `write "`+string(data)+`" error`, err.Error())
|
||||
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
2
tree.go
2
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 + "'")
|
||||
}
|
||||
|
||||
|
|
25
tree_test.go
25
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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("🧡💛💚💙💜"))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue