mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-16 13:22:09 +08:00
Merge branch 'gin-gonic:master' into gin_logger
This commit is contained in:
commit
340777746d
49
.github/workflows/codeql.yml
vendored
Normal file
49
.github/workflows/codeql.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '0 17 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Override automatic language detection by changing the below list
|
||||
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
|
||||
# TODO: Enable for javascript later
|
||||
language: [ 'go']
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
14
.github/workflows/gin.yml
vendored
14
.github/workflows/gin.yml
vendored
@ -17,18 +17,18 @@ jobs:
|
||||
with:
|
||||
go-version: '^1.16'
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
uses: golangci/golangci-lint-action@v3.1.0
|
||||
with:
|
||||
version: v1.43.0
|
||||
version: v1.45.0
|
||||
args: --verbose
|
||||
test:
|
||||
needs: lint
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
go: [1.13, 1.14, 1.15, 1.16, 1.17]
|
||||
go: [1.14, 1.15, 1.16, 1.17, 1.18]
|
||||
test-tags: ['', nomsgpack]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
@ -48,11 +48,11 @@ jobs:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- uses: actions/cache@v2
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
${{ matrix.go-build }}
|
||||
|
12
README.md
12
README.md
@ -86,7 +86,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
|
||||
|
||||
To install Gin package, you need to install Go and set your Go workspace first.
|
||||
|
||||
1. The first need [Go](https://golang.org/) installed (**version 1.13+ is required**), then you can use the below Go command to install Gin.
|
||||
1. The first need [Go](https://golang.org/) installed (**version 1.14+ is required**), then you can use the below Go command to install Gin.
|
||||
|
||||
```sh
|
||||
$ go get -u github.com/gin-gonic/gin
|
||||
@ -385,7 +385,7 @@ func main() {
|
||||
router.MaxMultipartMemory = 8 << 20 // 8 MiB
|
||||
router.POST("/upload", func(c *gin.Context) {
|
||||
// Single file
|
||||
file, _ := c.FormFile("Filename")
|
||||
file, _ := c.FormFile("file")
|
||||
log.Println(file.Filename)
|
||||
|
||||
// Upload the file to specific dst.
|
||||
@ -417,7 +417,7 @@ func main() {
|
||||
router.POST("/upload", func(c *gin.Context) {
|
||||
// Multipart form
|
||||
form, _ := c.MultipartForm()
|
||||
files := form.File["Filename[]"]
|
||||
files := form.File["upload[]"]
|
||||
|
||||
for _, file := range files {
|
||||
log.Println(file.Filename)
|
||||
@ -513,6 +513,7 @@ func main() {
|
||||
|
||||
// nested group
|
||||
testing := authorized.Group("testing")
|
||||
// visit 0.0.0.0:8080/testing/analytics
|
||||
testing.GET("/analytics", analyticsEndpoint)
|
||||
}
|
||||
|
||||
@ -1243,7 +1244,8 @@ func main() {
|
||||
router.Static("/assets", "./assets")
|
||||
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")
|
||||
}
|
||||
@ -1409,7 +1411,7 @@ import (
|
||||
|
||||
func formatAsDate(t time.Time) string {
|
||||
year, month, day := t.Date()
|
||||
return fmt.Sprintf("%d%02d/%02d", year, month, day)
|
||||
return fmt.Sprintf("%d/%02d/%02d", year, month, day)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
10
any.go
Normal file
10
any.go
Normal file
@ -0,0 +1,10 @@
|
||||
// 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 !go1.18
|
||||
// +build !go1.18
|
||||
|
||||
package gin
|
||||
|
||||
type any = interface{}
|
10
binding/any.go
Normal file
10
binding/any.go
Normal file
@ -0,0 +1,10 @@
|
||||
// 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 !go1.18
|
||||
// +build !go1.18
|
||||
|
||||
package binding
|
||||
|
||||
type any = interface{}
|
@ -29,21 +29,21 @@ const (
|
||||
// the form POST.
|
||||
type Binding interface {
|
||||
Name() string
|
||||
Bind(*http.Request, interface{}) error
|
||||
Bind(*http.Request, any) error
|
||||
}
|
||||
|
||||
// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
|
||||
// but it reads the body from supplied bytes instead of req.Body.
|
||||
type BindingBody interface {
|
||||
Binding
|
||||
BindBody([]byte, interface{}) error
|
||||
BindBody([]byte, any) error
|
||||
}
|
||||
|
||||
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
|
||||
// but it reads the Params.
|
||||
type BindingUri interface {
|
||||
Name() string
|
||||
BindUri(map[string][]string, interface{}) error
|
||||
BindUri(map[string][]string, any) error
|
||||
}
|
||||
|
||||
// StructValidator is the minimal interface which needs to be implemented in
|
||||
@ -57,11 +57,11 @@ type StructValidator interface {
|
||||
// If the received type is a struct or pointer to a struct, the validation should be performed.
|
||||
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
|
||||
// Otherwise nil must be returned.
|
||||
ValidateStruct(interface{}) error
|
||||
ValidateStruct(any) error
|
||||
|
||||
// Engine returns the underlying validator engine which powers the
|
||||
// StructValidator implementation.
|
||||
Engine() interface{}
|
||||
Engine() any
|
||||
}
|
||||
|
||||
// Validator is the default validator which implements the StructValidator
|
||||
@ -110,7 +110,7 @@ func Default(method, contentType string) Binding {
|
||||
}
|
||||
}
|
||||
|
||||
func validate(obj interface{}) error {
|
||||
func validate(obj any) error {
|
||||
if Validator == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -27,21 +27,21 @@ const (
|
||||
// the form POST.
|
||||
type Binding interface {
|
||||
Name() string
|
||||
Bind(*http.Request, interface{}) error
|
||||
Bind(*http.Request, any) error
|
||||
}
|
||||
|
||||
// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
|
||||
// but it reads the body from supplied bytes instead of req.Body.
|
||||
type BindingBody interface {
|
||||
Binding
|
||||
BindBody([]byte, interface{}) error
|
||||
BindBody([]byte, any) error
|
||||
}
|
||||
|
||||
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
|
||||
// but it reads the Params.
|
||||
type BindingUri interface {
|
||||
Name() string
|
||||
BindUri(map[string][]string, interface{}) error
|
||||
BindUri(map[string][]string, any) error
|
||||
}
|
||||
|
||||
// StructValidator is the minimal interface which needs to be implemented in
|
||||
@ -54,11 +54,11 @@ type StructValidator interface {
|
||||
// If the received type is a struct or pointer to a struct, the validation should be performed.
|
||||
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
|
||||
// Otherwise nil must be returned.
|
||||
ValidateStruct(interface{}) error
|
||||
ValidateStruct(any) error
|
||||
|
||||
// Engine returns the underlying validator engine which powers the
|
||||
// StructValidator implementation.
|
||||
Engine() interface{}
|
||||
Engine() any
|
||||
}
|
||||
|
||||
// Validator is the default validator which implements the StructValidator
|
||||
@ -104,7 +104,7 @@ func Default(method, contentType string) Binding {
|
||||
}
|
||||
}
|
||||
|
||||
func validate(obj interface{}) error {
|
||||
func validate(obj any) error {
|
||||
if Validator == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -61,11 +61,11 @@ type FooDefaultBarStruct struct {
|
||||
}
|
||||
|
||||
type FooStructUseNumber struct {
|
||||
Foo interface{} `json:"foo" binding:"required"`
|
||||
Foo any `json:"foo" binding:"required"`
|
||||
}
|
||||
|
||||
type FooStructDisallowUnknownFields struct {
|
||||
Foo interface{} `json:"foo" binding:"required"`
|
||||
Foo any `json:"foo" binding:"required"`
|
||||
}
|
||||
|
||||
type FooBarStructForTimeType struct {
|
||||
@ -93,7 +93,7 @@ type FooStructForTimeTypeFailLocation struct {
|
||||
}
|
||||
|
||||
type FooStructForMapType struct {
|
||||
MapFoo map[string]interface{} `form:"map_foo"`
|
||||
MapFoo map[string]any `form:"map_foo"`
|
||||
}
|
||||
|
||||
type FooStructForIgnoreFormTag struct {
|
||||
@ -106,7 +106,7 @@ type InvalidNameType struct {
|
||||
|
||||
type InvalidNameMapType struct {
|
||||
TestName struct {
|
||||
MapFoo map[string]interface{} `form:"map_foo"`
|
||||
MapFoo map[string]any `form:"map_foo"`
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,7 +128,7 @@ type FooStructForStructPointerType struct {
|
||||
|
||||
type FooStructForSliceMapType struct {
|
||||
// Unknown type: not support map
|
||||
SliceMapFoo []map[string]interface{} `form:"slice_map_foo"`
|
||||
SliceMapFoo []map[string]any `form:"slice_map_foo"`
|
||||
}
|
||||
|
||||
type FooStructForBoolType struct {
|
||||
@ -141,7 +141,7 @@ type FooStructForStringPtrType struct {
|
||||
}
|
||||
|
||||
type FooStructForMapPtrType struct {
|
||||
PtrBar *map[string]interface{} `form:"ptr_bar"`
|
||||
PtrBar *map[string]any `form:"ptr_bar"`
|
||||
}
|
||||
|
||||
func TestBindingDefault(t *testing.T) {
|
||||
@ -768,7 +768,7 @@ func TestHeaderBinding(t *testing.T) {
|
||||
req.Header.Add("fail", `{fail:fail}`)
|
||||
|
||||
type failStruct struct {
|
||||
Fail map[string]interface{} `header:"fail"`
|
||||
Fail map[string]any `header:"fail"`
|
||||
}
|
||||
|
||||
err := h.Bind(req, &failStruct{})
|
||||
@ -789,11 +789,11 @@ func TestUriBinding(t *testing.T) {
|
||||
assert.Equal(t, "thinkerou", tag.Name)
|
||||
|
||||
type NotSupportStruct struct {
|
||||
Name map[string]interface{} `uri:"name"`
|
||||
Name map[string]any `uri:"name"`
|
||||
}
|
||||
var not NotSupportStruct
|
||||
assert.Error(t, b.BindUri(m, ¬))
|
||||
assert.Equal(t, map[string]interface{}(nil), not.Name)
|
||||
assert.Equal(t, map[string]any(nil), not.Name)
|
||||
}
|
||||
|
||||
func TestUriInnerBinding(t *testing.T) {
|
||||
|
@ -46,7 +46,7 @@ func (err SliceValidationError) Error() string {
|
||||
var _ StructValidator = &defaultValidator{}
|
||||
|
||||
// ValidateStruct receives any kind of type, but only performed struct or pointer to struct type.
|
||||
func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
||||
func (v *defaultValidator) ValidateStruct(obj any) error {
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
@ -75,7 +75,7 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
||||
}
|
||||
|
||||
// validateStruct receives struct type
|
||||
func (v *defaultValidator) validateStruct(obj interface{}) error {
|
||||
func (v *defaultValidator) validateStruct(obj any) error {
|
||||
v.lazyinit()
|
||||
return v.validate.Struct(obj)
|
||||
}
|
||||
@ -84,7 +84,7 @@ func (v *defaultValidator) validateStruct(obj interface{}) error {
|
||||
// Validator instance. This is useful if you want to register custom validations
|
||||
// or struct level validations. See validator GoDoc for more info -
|
||||
// https://pkg.go.dev/github.com/go-playground/validator/v10
|
||||
func (v *defaultValidator) Engine() interface{} {
|
||||
func (v *defaultValidator) Engine() any {
|
||||
v.lazyinit()
|
||||
return v.validate
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ func TestDefaultValidator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
v *defaultValidator
|
||||
obj interface{}
|
||||
obj any
|
||||
wantErr bool
|
||||
}{
|
||||
{"validate nil obj", &defaultValidator{}, nil, false},
|
||||
|
@ -19,7 +19,7 @@ func (formBinding) Name() string {
|
||||
return "form"
|
||||
}
|
||||
|
||||
func (formBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
func (formBinding) Bind(req *http.Request, obj any) error {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -36,7 +36,7 @@ func (formPostBinding) Name() string {
|
||||
return "form-urlencoded"
|
||||
}
|
||||
|
||||
func (formPostBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
func (formPostBinding) Bind(req *http.Request, obj any) error {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -50,7 +50,7 @@ func (formMultipartBinding) Name() string {
|
||||
return "multipart/form-data"
|
||||
}
|
||||
|
||||
func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
func (formMultipartBinding) Bind(req *http.Request, obj any) error {
|
||||
if err := req.ParseMultipartForm(defaultMemory); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -26,24 +26,24 @@ var (
|
||||
ErrConvertToMapString = errors.New("can not convert to map of strings")
|
||||
)
|
||||
|
||||
func mapURI(ptr interface{}, m map[string][]string) error {
|
||||
func mapURI(ptr any, m map[string][]string) error {
|
||||
return mapFormByTag(ptr, m, "uri")
|
||||
}
|
||||
|
||||
func mapForm(ptr interface{}, form map[string][]string) error {
|
||||
func mapForm(ptr any, form map[string][]string) error {
|
||||
return mapFormByTag(ptr, form, "form")
|
||||
}
|
||||
|
||||
func MapFormWithTag(ptr interface{}, form map[string][]string, tag string) error {
|
||||
func MapFormWithTag(ptr any, form map[string][]string, tag string) error {
|
||||
return mapFormByTag(ptr, form, tag)
|
||||
}
|
||||
|
||||
var emptyField = reflect.StructField{}
|
||||
|
||||
func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
|
||||
func mapFormByTag(ptr any, form map[string][]string, tag string) error {
|
||||
// Check if ptr is a map
|
||||
ptrVal := reflect.ValueOf(ptr)
|
||||
var pointed interface{}
|
||||
var pointed any
|
||||
if ptrVal.Kind() == reflect.Ptr {
|
||||
ptrVal = ptrVal.Elem()
|
||||
pointed = ptrVal.Interface()
|
||||
@ -73,7 +73,7 @@ func (form formSource) TrySet(value reflect.Value, field reflect.StructField, ta
|
||||
return setByForm(value, field, form, tagValue, opt)
|
||||
}
|
||||
|
||||
func mappingByPtr(ptr interface{}, setter setter, tag string) error {
|
||||
func mappingByPtr(ptr any, setter setter, tag string) error {
|
||||
_, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag)
|
||||
return err
|
||||
}
|
||||
@ -376,7 +376,7 @@ func head(str, sep string) (head string, tail string) {
|
||||
return str[:idx], str[idx+len(sep):]
|
||||
}
|
||||
|
||||
func setFormMap(ptr interface{}, form map[string][]string) error {
|
||||
func setFormMap(ptr any, form map[string][]string) error {
|
||||
el := reflect.TypeOf(ptr).Elem()
|
||||
|
||||
if el.Kind() == reflect.Slice {
|
||||
|
@ -18,9 +18,9 @@ func TestMappingBaseTypes(t *testing.T) {
|
||||
}
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
value interface{}
|
||||
value any
|
||||
form string
|
||||
expect interface{}
|
||||
expect any
|
||||
}{
|
||||
{"base type", struct{ F int }{}, "9", int(9)},
|
||||
{"base type", struct{ F int8 }{}, "9", int8(9)},
|
||||
|
@ -12,7 +12,7 @@ func (headerBinding) Name() string {
|
||||
return "header"
|
||||
}
|
||||
|
||||
func (headerBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
func (headerBinding) Bind(req *http.Request, obj any) error {
|
||||
|
||||
if err := mapHeader(obj, req.Header); err != nil {
|
||||
return err
|
||||
@ -21,7 +21,7 @@ func (headerBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
return validate(obj)
|
||||
}
|
||||
|
||||
func mapHeader(ptr interface{}, h map[string][]string) error {
|
||||
func mapHeader(ptr any, h map[string][]string) error {
|
||||
return mappingByPtr(ptr, headerSource(h), "header")
|
||||
}
|
||||
|
||||
|
@ -30,18 +30,18 @@ func (jsonBinding) Name() string {
|
||||
return "json"
|
||||
}
|
||||
|
||||
func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
func (jsonBinding) Bind(req *http.Request, obj any) error {
|
||||
if req == nil || req.Body == nil {
|
||||
return errors.New("invalid request")
|
||||
}
|
||||
return decodeJSON(req.Body, obj)
|
||||
}
|
||||
|
||||
func (jsonBinding) BindBody(body []byte, obj interface{}) error {
|
||||
func (jsonBinding) BindBody(body []byte, obj any) error {
|
||||
return decodeJSON(bytes.NewReader(body), obj)
|
||||
}
|
||||
|
||||
func decodeJSON(r io.Reader, obj interface{}) error {
|
||||
func decodeJSON(r io.Reader, obj any) error {
|
||||
decoder := json.NewDecoder(r)
|
||||
if EnableDecoderUseNumber {
|
||||
decoder.UseNumber()
|
||||
|
@ -21,15 +21,15 @@ func (msgpackBinding) Name() string {
|
||||
return "msgpack"
|
||||
}
|
||||
|
||||
func (msgpackBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
func (msgpackBinding) Bind(req *http.Request, obj any) error {
|
||||
return decodeMsgPack(req.Body, obj)
|
||||
}
|
||||
|
||||
func (msgpackBinding) BindBody(body []byte, obj interface{}) error {
|
||||
func (msgpackBinding) BindBody(body []byte, obj any) error {
|
||||
return decodeMsgPack(bytes.NewReader(body), obj)
|
||||
}
|
||||
|
||||
func decodeMsgPack(r io.Reader, obj interface{}) error {
|
||||
func decodeMsgPack(r io.Reader, obj any) error {
|
||||
cdc := new(codec.MsgpackHandle)
|
||||
if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil {
|
||||
return err
|
||||
|
@ -26,7 +26,7 @@ func TestMsgpackBindingBindBody(t *testing.T) {
|
||||
assert.Equal(t, "FOO", s.Foo)
|
||||
}
|
||||
|
||||
func msgpackBody(t *testing.T, obj interface{}) []byte {
|
||||
func msgpackBody(t *testing.T, obj any) []byte {
|
||||
var bs bytes.Buffer
|
||||
h := &codec.MsgpackHandle{}
|
||||
err := codec.NewEncoder(&bs, h).Encode(obj)
|
||||
|
@ -76,7 +76,7 @@ func TestFormMultipartBindingBindError(t *testing.T) {
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
s interface{}
|
||||
s any
|
||||
}{
|
||||
{"wrong type", &struct {
|
||||
Files int `form:"file"`
|
||||
|
@ -18,7 +18,7 @@ func (protobufBinding) Name() string {
|
||||
return "protobuf"
|
||||
}
|
||||
|
||||
func (b protobufBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
func (b protobufBinding) Bind(req *http.Request, obj any) error {
|
||||
buf, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -26,7 +26,7 @@ func (b protobufBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
return b.BindBody(buf, obj)
|
||||
}
|
||||
|
||||
func (protobufBinding) BindBody(body []byte, obj interface{}) error {
|
||||
func (protobufBinding) BindBody(body []byte, obj any) error {
|
||||
msg, ok := obj.(proto.Message)
|
||||
if !ok {
|
||||
return errors.New("obj is not ProtoMessage")
|
||||
|
@ -12,7 +12,7 @@ func (queryBinding) Name() string {
|
||||
return "query"
|
||||
}
|
||||
|
||||
func (queryBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
func (queryBinding) Bind(req *http.Request, obj any) error {
|
||||
values := req.URL.Query()
|
||||
if err := mapForm(obj, values); err != nil {
|
||||
return err
|
||||
|
@ -10,7 +10,7 @@ func (uriBinding) Name() string {
|
||||
return "uri"
|
||||
}
|
||||
|
||||
func (uriBinding) BindUri(m map[string][]string, obj interface{}) error {
|
||||
func (uriBinding) BindUri(m map[string][]string, obj any) error {
|
||||
if err := mapURI(obj, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ type structNoValidationValues struct {
|
||||
StructSlice []substructNoValidation
|
||||
InterfaceSlice []testInterface
|
||||
|
||||
UniversalInterface interface{}
|
||||
UniversalInterface any
|
||||
CustomInterface testInterface
|
||||
|
||||
FloatMap map[string]float32
|
||||
@ -169,7 +169,7 @@ func TestValidateNoValidationPointers(t *testing.T) {
|
||||
//assert.Equal(t, origin, test)
|
||||
}
|
||||
|
||||
type Object map[string]interface{}
|
||||
type Object map[string]any
|
||||
|
||||
func TestValidatePrimitives(t *testing.T) {
|
||||
obj := Object{"foo": "bar", "bar": 1}
|
||||
|
@ -17,14 +17,14 @@ func (xmlBinding) Name() string {
|
||||
return "xml"
|
||||
}
|
||||
|
||||
func (xmlBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
func (xmlBinding) Bind(req *http.Request, obj any) error {
|
||||
return decodeXML(req.Body, obj)
|
||||
}
|
||||
|
||||
func (xmlBinding) BindBody(body []byte, obj interface{}) error {
|
||||
func (xmlBinding) BindBody(body []byte, obj any) error {
|
||||
return decodeXML(bytes.NewReader(body), obj)
|
||||
}
|
||||
func decodeXML(r io.Reader, obj interface{}) error {
|
||||
func decodeXML(r io.Reader, obj any) error {
|
||||
decoder := xml.NewDecoder(r)
|
||||
if err := decoder.Decode(obj); err != nil {
|
||||
return err
|
||||
|
@ -18,15 +18,15 @@ func (yamlBinding) Name() string {
|
||||
return "yaml"
|
||||
}
|
||||
|
||||
func (yamlBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
func (yamlBinding) Bind(req *http.Request, obj any) error {
|
||||
return decodeYAML(req.Body, obj)
|
||||
}
|
||||
|
||||
func (yamlBinding) BindBody(body []byte, obj interface{}) error {
|
||||
func (yamlBinding) BindBody(body []byte, obj any) error {
|
||||
return decodeYAML(bytes.NewReader(body), obj)
|
||||
}
|
||||
|
||||
func decodeYAML(r io.Reader, obj interface{}) error {
|
||||
func decodeYAML(r io.Reader, obj any) error {
|
||||
decoder := yaml.NewDecoder(r)
|
||||
if err := decoder.Decode(obj); err != nil {
|
||||
return err
|
||||
|
107
context.go
107
context.go
@ -6,7 +6,6 @@ package gin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@ -63,7 +62,7 @@ type Context struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
// Keys is a key/value pair exclusively for the context of each request.
|
||||
Keys map[string]interface{}
|
||||
Keys map[string]any
|
||||
|
||||
// Errors is a list of errors attached to all the handlers/middlewares who used this context.
|
||||
Errors errorMsgs
|
||||
@ -116,7 +115,7 @@ func (c *Context) Copy() *Context {
|
||||
cp.Writer = &cp.writermem
|
||||
cp.index = abortIndex
|
||||
cp.handlers = nil
|
||||
cp.Keys = map[string]interface{}{}
|
||||
cp.Keys = map[string]any{}
|
||||
for k, v := range c.Keys {
|
||||
cp.Keys[k] = v
|
||||
}
|
||||
@ -195,7 +194,7 @@ func (c *Context) AbortWithStatus(code int) {
|
||||
// AbortWithStatusJSON calls `Abort()` and then `JSON` internally.
|
||||
// This method stops the chain, writes the status code and return a JSON body.
|
||||
// It also sets the Content-Type as "application/json".
|
||||
func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) {
|
||||
func (c *Context) AbortWithStatusJSON(code int, jsonObj any) {
|
||||
c.Abort()
|
||||
c.JSON(code, jsonObj)
|
||||
}
|
||||
@ -241,10 +240,10 @@ func (c *Context) Error(err error) *Error {
|
||||
|
||||
// Set is used to store a new key/value pair exclusively for this context.
|
||||
// It also lazy initializes c.Keys if it was not used previously.
|
||||
func (c *Context) Set(key string, value interface{}) {
|
||||
func (c *Context) Set(key string, value any) {
|
||||
c.mu.Lock()
|
||||
if c.Keys == nil {
|
||||
c.Keys = make(map[string]interface{})
|
||||
c.Keys = make(map[string]any)
|
||||
}
|
||||
|
||||
c.Keys[key] = value
|
||||
@ -253,7 +252,7 @@ func (c *Context) Set(key string, value interface{}) {
|
||||
|
||||
// Get returns the value for the given key, ie: (value, true).
|
||||
// If the value does not exist it returns (nil, false)
|
||||
func (c *Context) Get(key string) (value interface{}, exists bool) {
|
||||
func (c *Context) Get(key string) (value any, exists bool) {
|
||||
c.mu.RLock()
|
||||
value, exists = c.Keys[key]
|
||||
c.mu.RUnlock()
|
||||
@ -261,7 +260,7 @@ func (c *Context) Get(key string) (value interface{}, exists bool) {
|
||||
}
|
||||
|
||||
// MustGet returns the value for the given key if it exists, otherwise it panics.
|
||||
func (c *Context) MustGet(key string) interface{} {
|
||||
func (c *Context) MustGet(key string) any {
|
||||
if value, exists := c.Get(key); exists {
|
||||
return value
|
||||
}
|
||||
@ -349,9 +348,9 @@ func (c *Context) GetStringSlice(key string) (ss []string) {
|
||||
}
|
||||
|
||||
// GetStringMap returns the value associated with the key as a map of interfaces.
|
||||
func (c *Context) GetStringMap(key string) (sm map[string]interface{}) {
|
||||
func (c *Context) GetStringMap(key string) (sm map[string]any) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
sm, _ = val.(map[string]interface{})
|
||||
sm, _ = val.(map[string]any)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -601,47 +600,46 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
|
||||
return err
|
||||
}
|
||||
|
||||
// Bind checks the Content-Type to select a binding engine automatically,
|
||||
// Depending on the "Content-Type" header different bindings are used:
|
||||
// Bind checks the Method and Content-Type to select a binding engine automatically,
|
||||
// Depending on the "Content-Type" header different bindings are used, for example:
|
||||
// "application/json" --> JSON binding
|
||||
// "application/xml" --> XML binding
|
||||
// otherwise --> returns an error.
|
||||
// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
|
||||
// It decodes the json payload into the struct specified as a pointer.
|
||||
// It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid.
|
||||
func (c *Context) Bind(obj interface{}) error {
|
||||
func (c *Context) Bind(obj any) error {
|
||||
b := binding.Default(c.Request.Method, c.ContentType())
|
||||
return c.MustBindWith(obj, b)
|
||||
}
|
||||
|
||||
// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON).
|
||||
func (c *Context) BindJSON(obj interface{}) error {
|
||||
func (c *Context) BindJSON(obj any) error {
|
||||
return c.MustBindWith(obj, binding.JSON)
|
||||
}
|
||||
|
||||
// BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML).
|
||||
func (c *Context) BindXML(obj interface{}) error {
|
||||
func (c *Context) BindXML(obj any) error {
|
||||
return c.MustBindWith(obj, binding.XML)
|
||||
}
|
||||
|
||||
// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query).
|
||||
func (c *Context) BindQuery(obj interface{}) error {
|
||||
func (c *Context) BindQuery(obj any) error {
|
||||
return c.MustBindWith(obj, binding.Query)
|
||||
}
|
||||
|
||||
// BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML).
|
||||
func (c *Context) BindYAML(obj interface{}) error {
|
||||
func (c *Context) BindYAML(obj any) error {
|
||||
return c.MustBindWith(obj, binding.YAML)
|
||||
}
|
||||
|
||||
// BindHeader is a shortcut for c.MustBindWith(obj, binding.Header).
|
||||
func (c *Context) BindHeader(obj interface{}) error {
|
||||
func (c *Context) BindHeader(obj any) error {
|
||||
return c.MustBindWith(obj, binding.Header)
|
||||
}
|
||||
|
||||
// BindUri binds the passed struct pointer using binding.Uri.
|
||||
// It will abort the request with HTTP 400 if any error occurs.
|
||||
func (c *Context) BindUri(obj interface{}) error {
|
||||
func (c *Context) BindUri(obj any) error {
|
||||
if err := c.ShouldBindUri(obj); err != nil {
|
||||
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck
|
||||
return err
|
||||
@ -652,7 +650,7 @@ func (c *Context) BindUri(obj interface{}) error {
|
||||
// MustBindWith binds the passed struct pointer using the specified binding engine.
|
||||
// It will abort the request with HTTP 400 if any error occurs.
|
||||
// See the binding package.
|
||||
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error {
|
||||
func (c *Context) MustBindWith(obj any, b binding.Binding) error {
|
||||
if err := c.ShouldBindWith(obj, b); err != nil {
|
||||
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck
|
||||
return err
|
||||
@ -660,46 +658,45 @@ func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShouldBind checks the Content-Type to select a binding engine automatically,
|
||||
// Depending on the "Content-Type" header different bindings are used:
|
||||
// ShouldBind checks the Method and Content-Type to select a binding engine automatically,
|
||||
// Depending on the "Content-Type" header different bindings are used, for example:
|
||||
// "application/json" --> JSON binding
|
||||
// "application/xml" --> XML binding
|
||||
// otherwise --> returns an error
|
||||
// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
|
||||
// It decodes the json payload into the struct specified as a pointer.
|
||||
// Like c.Bind() but this method does not set the response status code to 400 and abort if the json is not valid.
|
||||
func (c *Context) ShouldBind(obj interface{}) error {
|
||||
// Like c.Bind() but this method does not set the response status code to 400 or abort if input is not valid.
|
||||
func (c *Context) ShouldBind(obj any) error {
|
||||
b := binding.Default(c.Request.Method, c.ContentType())
|
||||
return c.ShouldBindWith(obj, b)
|
||||
}
|
||||
|
||||
// ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
|
||||
func (c *Context) ShouldBindJSON(obj interface{}) error {
|
||||
func (c *Context) ShouldBindJSON(obj any) error {
|
||||
return c.ShouldBindWith(obj, binding.JSON)
|
||||
}
|
||||
|
||||
// ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML).
|
||||
func (c *Context) ShouldBindXML(obj interface{}) error {
|
||||
func (c *Context) ShouldBindXML(obj any) error {
|
||||
return c.ShouldBindWith(obj, binding.XML)
|
||||
}
|
||||
|
||||
// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
|
||||
func (c *Context) ShouldBindQuery(obj interface{}) error {
|
||||
func (c *Context) ShouldBindQuery(obj any) error {
|
||||
return c.ShouldBindWith(obj, binding.Query)
|
||||
}
|
||||
|
||||
// ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).
|
||||
func (c *Context) ShouldBindYAML(obj interface{}) error {
|
||||
func (c *Context) ShouldBindYAML(obj any) error {
|
||||
return c.ShouldBindWith(obj, binding.YAML)
|
||||
}
|
||||
|
||||
// ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header).
|
||||
func (c *Context) ShouldBindHeader(obj interface{}) error {
|
||||
func (c *Context) ShouldBindHeader(obj any) error {
|
||||
return c.ShouldBindWith(obj, binding.Header)
|
||||
}
|
||||
|
||||
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
|
||||
func (c *Context) ShouldBindUri(obj interface{}) error {
|
||||
func (c *Context) ShouldBindUri(obj any) error {
|
||||
m := make(map[string][]string)
|
||||
for _, v := range c.Params {
|
||||
m[v.Key] = []string{v.Value}
|
||||
@ -709,7 +706,7 @@ func (c *Context) ShouldBindUri(obj interface{}) error {
|
||||
|
||||
// ShouldBindWith binds the passed struct pointer using the specified binding engine.
|
||||
// See the binding package.
|
||||
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
|
||||
func (c *Context) ShouldBindWith(obj any, b binding.Binding) error {
|
||||
return b.Bind(c.Request, obj)
|
||||
}
|
||||
|
||||
@ -718,7 +715,7 @@ func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
|
||||
//
|
||||
// NOTE: This method reads the body before binding. So you should use
|
||||
// ShouldBindWith for better performance if you need to call only once.
|
||||
func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error) {
|
||||
func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error) {
|
||||
var body []byte
|
||||
if cb, ok := c.Get(BodyBytesKey); ok {
|
||||
if cbb, ok := cb.([]byte); ok {
|
||||
@ -903,7 +900,7 @@ func (c *Context) Render(code int, r render.Render) {
|
||||
// HTML renders the HTTP template specified by its file name.
|
||||
// It also updates the HTTP code and sets the Content-Type as "text/html".
|
||||
// See http://golang.org/doc/articles/wiki/
|
||||
func (c *Context) HTML(code int, name string, obj interface{}) {
|
||||
func (c *Context) HTML(code int, name string, obj any) {
|
||||
instance := c.engine.HTMLRender.Instance(name, obj)
|
||||
c.Render(code, instance)
|
||||
}
|
||||
@ -912,21 +909,21 @@ func (c *Context) HTML(code int, name string, obj interface{}) {
|
||||
// It also sets the Content-Type as "application/json".
|
||||
// WARNING: we recommend using this only for development purposes since printing pretty JSON is
|
||||
// more CPU and bandwidth consuming. Use Context.JSON() instead.
|
||||
func (c *Context) IndentedJSON(code int, obj interface{}) {
|
||||
func (c *Context) IndentedJSON(code int, obj any) {
|
||||
c.Render(code, render.IndentedJSON{Data: obj})
|
||||
}
|
||||
|
||||
// SecureJSON serializes the given struct as Secure JSON into the response body.
|
||||
// Default prepends "while(1)," to response body if the given struct is array values.
|
||||
// It also sets the Content-Type as "application/json".
|
||||
func (c *Context) SecureJSON(code int, obj interface{}) {
|
||||
func (c *Context) SecureJSON(code int, obj any) {
|
||||
c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj})
|
||||
}
|
||||
|
||||
// JSONP serializes the given struct as JSON into the response body.
|
||||
// It adds padding to response body to request data from a server residing in a different domain than the client.
|
||||
// It also sets the Content-Type as "application/javascript".
|
||||
func (c *Context) JSONP(code int, obj interface{}) {
|
||||
func (c *Context) JSONP(code int, obj any) {
|
||||
callback := c.DefaultQuery("callback", "")
|
||||
if callback == "" {
|
||||
c.Render(code, render.JSON{Data: obj})
|
||||
@ -937,40 +934,40 @@ func (c *Context) JSONP(code int, obj interface{}) {
|
||||
|
||||
// JSON serializes the given struct as JSON into the response body.
|
||||
// It also sets the Content-Type as "application/json".
|
||||
func (c *Context) JSON(code int, obj interface{}) {
|
||||
func (c *Context) JSON(code int, obj any) {
|
||||
c.Render(code, render.JSON{Data: obj})
|
||||
}
|
||||
|
||||
// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string.
|
||||
// It also sets the Content-Type as "application/json".
|
||||
func (c *Context) AsciiJSON(code int, obj interface{}) {
|
||||
func (c *Context) AsciiJSON(code int, obj any) {
|
||||
c.Render(code, render.AsciiJSON{Data: obj})
|
||||
}
|
||||
|
||||
// PureJSON serializes the given struct as JSON into the response body.
|
||||
// PureJSON, unlike JSON, does not replace special html characters with their unicode entities.
|
||||
func (c *Context) PureJSON(code int, obj interface{}) {
|
||||
func (c *Context) PureJSON(code int, obj any) {
|
||||
c.Render(code, render.PureJSON{Data: obj})
|
||||
}
|
||||
|
||||
// XML serializes the given struct as XML into the response body.
|
||||
// It also sets the Content-Type as "application/xml".
|
||||
func (c *Context) XML(code int, obj interface{}) {
|
||||
func (c *Context) XML(code int, obj any) {
|
||||
c.Render(code, render.XML{Data: obj})
|
||||
}
|
||||
|
||||
// YAML serializes the given struct as YAML into the response body.
|
||||
func (c *Context) YAML(code int, obj interface{}) {
|
||||
func (c *Context) YAML(code int, obj any) {
|
||||
c.Render(code, render.YAML{Data: obj})
|
||||
}
|
||||
|
||||
// ProtoBuf serializes the given struct as ProtoBuf into the response body.
|
||||
func (c *Context) ProtoBuf(code int, obj interface{}) {
|
||||
func (c *Context) ProtoBuf(code int, obj any) {
|
||||
c.Render(code, render.ProtoBuf{Data: obj})
|
||||
}
|
||||
|
||||
// String writes the given string into the response body.
|
||||
func (c *Context) String(code int, format string, values ...interface{}) {
|
||||
func (c *Context) String(code int, format string, values ...any) {
|
||||
c.Render(code, render.String{Format: format, Data: values})
|
||||
}
|
||||
|
||||
@ -1020,12 +1017,16 @@ func (c *Context) FileFromFS(filepath string, fs http.FileSystem) {
|
||||
// FileAttachment writes the specified file into the body stream in an efficient way
|
||||
// On the client side, the file will typically be downloaded with the given filename
|
||||
func (c *Context) FileAttachment(filepath, filename string) {
|
||||
c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
|
||||
if isASCII(filename) {
|
||||
c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+filename+`"`)
|
||||
} else {
|
||||
c.Writer.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename))
|
||||
}
|
||||
http.ServeFile(c.Writer, c.Request, filepath)
|
||||
}
|
||||
|
||||
// SSEvent writes a Server-Sent Event into the body stream.
|
||||
func (c *Context) SSEvent(name string, message interface{}) {
|
||||
func (c *Context) SSEvent(name string, message any) {
|
||||
c.Render(-1, sse.Event{
|
||||
Event: name,
|
||||
Data: message,
|
||||
@ -1059,11 +1060,11 @@ func (c *Context) Stream(step func(w io.Writer) bool) bool {
|
||||
type Negotiate struct {
|
||||
Offered []string
|
||||
HTMLName string
|
||||
HTMLData interface{}
|
||||
JSONData interface{}
|
||||
XMLData interface{}
|
||||
YAMLData interface{}
|
||||
Data interface{}
|
||||
HTMLData any
|
||||
JSONData any
|
||||
XMLData any
|
||||
YAMLData any
|
||||
Data any
|
||||
}
|
||||
|
||||
// Negotiate calls different Render according to acceptable Accept format.
|
||||
@ -1157,7 +1158,7 @@ func (c *Context) Err() error {
|
||||
// Value returns the value associated with this context for key, or nil
|
||||
// if no value is associated with key. Successive calls to Value with
|
||||
// the same key returns the same result.
|
||||
func (c *Context) Value(key interface{}) interface{} {
|
||||
func (c *Context) Value(key any) any {
|
||||
if key == 0 {
|
||||
return c.Request
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
@ -211,7 +212,7 @@ func TestContextSetGetValues(t *testing.T) {
|
||||
c.Set("uint64", uint64(42))
|
||||
c.Set("float32", float32(4.2))
|
||||
c.Set("float64", 4.2)
|
||||
var a interface{} = 1
|
||||
var a any = 1
|
||||
c.Set("intInterface", a)
|
||||
|
||||
assert.Exactly(t, c.MustGet("string").(string), "this is a string")
|
||||
@ -286,7 +287,7 @@ func TestContextGetStringSlice(t *testing.T) {
|
||||
|
||||
func TestContextGetStringMap(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
m := make(map[string]interface{})
|
||||
m := make(map[string]any)
|
||||
m["foo"] = 1
|
||||
c.Set("map", m)
|
||||
|
||||
@ -1033,6 +1034,19 @@ func TestContextRenderAttachment(t *testing.T) {
|
||||
assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition"))
|
||||
}
|
||||
|
||||
func TestContextRenderUTF8Attachment(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
newFilename := "new🧡_filename.go"
|
||||
|
||||
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||
c.FileAttachment("./gin.go", newFilename)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Contains(t, w.Body.String(), "func New() *Engine {")
|
||||
assert.Equal(t, `attachment; filename*=UTF-8''`+url.QueryEscape(newFilename), w.Header().Get("Content-Disposition"))
|
||||
}
|
||||
|
||||
// TestContextRenderYAML tests that the response is serialized as YAML
|
||||
// and Content-Type is set to application/x-yaml
|
||||
func TestContextRenderYAML(t *testing.T) {
|
||||
@ -2111,12 +2125,12 @@ type contextKey string
|
||||
func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
getContextAndKey func() (*Context, interface{})
|
||||
value interface{}
|
||||
getContextAndKey func() (*Context, any)
|
||||
value any
|
||||
}{
|
||||
{
|
||||
name: "c with struct context key",
|
||||
getContextAndKey: func() (*Context, interface{}) {
|
||||
getContextAndKey: func() (*Context, any) {
|
||||
var key struct{}
|
||||
c := &Context{}
|
||||
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||
@ -2127,7 +2141,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "c with string context key",
|
||||
getContextAndKey: func() (*Context, interface{}) {
|
||||
getContextAndKey: func() (*Context, any) {
|
||||
c := &Context{}
|
||||
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||
c.Request = c.Request.WithContext(context.WithValue(context.TODO(), contextKey("key"), "value"))
|
||||
@ -2137,7 +2151,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "c with nil http.Request",
|
||||
getContextAndKey: func() (*Context, interface{}) {
|
||||
getContextAndKey: func() (*Context, any) {
|
||||
c := &Context{}
|
||||
return c, "key"
|
||||
},
|
||||
@ -2145,7 +2159,7 @@ func TestContextWithFallbackValueFromRequestContext(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "c with nil http.Request.Context()",
|
||||
getContextAndKey: func() (*Context, interface{}) {
|
||||
getContextAndKey: func() (*Context, any) {
|
||||
c := &Context{}
|
||||
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||
return c, "key"
|
||||
|
6
debug.go
6
debug.go
@ -12,7 +12,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const ginSupportMinGoVer = 13
|
||||
const ginSupportMinGoVer = 14
|
||||
|
||||
// IsDebugging returns true if the framework is running in debug mode.
|
||||
// Use SetMode(gin.ReleaseMode) to disable debug mode.
|
||||
@ -47,7 +47,7 @@ func debugPrintLoadTemplate(tmpl *template.Template) {
|
||||
}
|
||||
}
|
||||
|
||||
func debugPrint(format string, values ...interface{}) {
|
||||
func debugPrint(format string, values ...any) {
|
||||
if IsDebugging() {
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
@ -67,7 +67,7 @@ func getMinVer(v string) (uint64, error) {
|
||||
|
||||
func debugPrintWARNINGDefault() {
|
||||
if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer {
|
||||
debugPrint(`[WARNING] Now Gin requires Go 1.13+.
|
||||
debugPrint(`[WARNING] Now Gin requires Go 1.14+.
|
||||
|
||||
`)
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) {
|
||||
})
|
||||
m, e := getMinVer(runtime.Version())
|
||||
if e == nil && m <= ginSupportMinGoVer {
|
||||
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.13+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
||||
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.14+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
||||
} else {
|
||||
assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
|
||||
// BindWith binds the passed struct pointer using the specified binding engine.
|
||||
// See the binding package.
|
||||
func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
|
||||
func (c *Context) BindWith(obj any, b binding.Binding) error {
|
||||
log.Println(`BindWith(\"interface{}, binding.Binding\") error is going to
|
||||
be deprecated, please check issue #662 and either use MustBindWith() if you
|
||||
want HTTP 400 to be automatically returned if any error occur, or use
|
||||
|
10
errors.go
10
errors.go
@ -34,7 +34,7 @@ const (
|
||||
type Error struct {
|
||||
Err error
|
||||
Type ErrorType
|
||||
Meta interface{}
|
||||
Meta any
|
||||
}
|
||||
|
||||
type errorMsgs []*Error
|
||||
@ -48,13 +48,13 @@ func (msg *Error) SetType(flags ErrorType) *Error {
|
||||
}
|
||||
|
||||
// SetMeta sets the error's meta data.
|
||||
func (msg *Error) SetMeta(data interface{}) *Error {
|
||||
func (msg *Error) SetMeta(data any) *Error {
|
||||
msg.Meta = data
|
||||
return msg
|
||||
}
|
||||
|
||||
// JSON creates a properly formatted JSON
|
||||
func (msg *Error) JSON() interface{} {
|
||||
func (msg *Error) JSON() any {
|
||||
jsonData := H{}
|
||||
if msg.Meta != nil {
|
||||
value := reflect.ValueOf(msg.Meta)
|
||||
@ -139,14 +139,14 @@ func (a errorMsgs) Errors() []string {
|
||||
return errorStrings
|
||||
}
|
||||
|
||||
func (a errorMsgs) JSON() interface{} {
|
||||
func (a errorMsgs) JSON() any {
|
||||
switch length := len(a); length {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return a.Last().JSON()
|
||||
default:
|
||||
jsonData := make([]interface{}, length)
|
||||
jsonData := make([]any, length)
|
||||
for i, err := range a {
|
||||
jsonData[i] = err.JSON()
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ Error #02: second
|
||||
Error #03: third
|
||||
Meta: map[status:400]
|
||||
`, errs.String())
|
||||
assert.Equal(t, []interface{}{
|
||||
assert.Equal(t, []any{
|
||||
H{"error": "first"},
|
||||
H{"error": "second", "meta": "some data"},
|
||||
H{"error": "third", "status": "400"},
|
||||
|
49
gin.go
49
gin.go
@ -16,6 +16,8 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin/internal/bytesconv"
|
||||
"github.com/gin-gonic/gin/render"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
)
|
||||
|
||||
const defaultMultipartMemory = 32 << 20 // 32 MB
|
||||
@ -65,10 +67,10 @@ type RoutesInfo []RouteInfo
|
||||
|
||||
// Trusted platforms
|
||||
const (
|
||||
// When running on Google App Engine. Trust X-Appengine-Remote-Addr
|
||||
// PlatformGoogleAppEngine when running on Google App Engine. Trust X-Appengine-Remote-Addr
|
||||
// for determining the client's IP
|
||||
PlatformGoogleAppEngine = "X-Appengine-Remote-Addr"
|
||||
// When using Cloudflare's CDN. Trust CF-Connecting-IP for determining
|
||||
// PlatformCloudflare when using Cloudflare's CDN. Trust CF-Connecting-IP for determining
|
||||
// the client's IP
|
||||
PlatformCloudflare = "CF-Connecting-IP"
|
||||
)
|
||||
@ -78,14 +80,14 @@ const (
|
||||
type Engine struct {
|
||||
RouterGroup
|
||||
|
||||
// Enables automatic redirection if the current route can't be matched but a
|
||||
// RedirectTrailingSlash enables automatic redirection if the current route can't be matched but a
|
||||
// handler for the path with (without) the trailing slash exists.
|
||||
// For example if /foo/ is requested but a route only exists for /foo, the
|
||||
// client is redirected to /foo with http status code 301 for GET requests
|
||||
// and 307 for all other request methods.
|
||||
RedirectTrailingSlash bool
|
||||
|
||||
// If enabled, the router tries to fix the current request path, if no
|
||||
// RedirectFixedPath if enabled, the router tries to fix the current request path, if no
|
||||
// handle is registered for it.
|
||||
// First superfluous path elements like ../ or // are removed.
|
||||
// Afterwards the router does a case-insensitive lookup of the cleaned path.
|
||||
@ -96,7 +98,7 @@ type Engine struct {
|
||||
// RedirectTrailingSlash is independent of this option.
|
||||
RedirectFixedPath bool
|
||||
|
||||
// If enabled, the router checks if another method is allowed for the
|
||||
// HandleMethodNotAllowed if enabled, the router checks if another method is allowed for the
|
||||
// current route, if the current request can not be routed.
|
||||
// If this is the case, the request is answered with 'Method Not Allowed'
|
||||
// and HTTP status code 405.
|
||||
@ -104,21 +106,22 @@ type Engine struct {
|
||||
// handler.
|
||||
HandleMethodNotAllowed bool
|
||||
|
||||
// If enabled, client IP will be parsed from the request's headers that
|
||||
// ForwardedByClientIP if enabled, client IP will be parsed from the request's headers that
|
||||
// match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was
|
||||
// fetched, it falls back to the IP obtained from
|
||||
// `(*gin.Context).Request.RemoteAddr`.
|
||||
ForwardedByClientIP bool
|
||||
|
||||
// DEPRECATED: USE `TrustedPlatform` WITH VALUE `gin.PlatformGoogleAppEngine` INSTEAD
|
||||
// AppEngine was deprecated.
|
||||
// Deprecated: USE `TrustedPlatform` WITH VALUE `gin.PlatformGoogleAppEngine` INSTEAD
|
||||
// #726 #755 If enabled, it will trust some headers starting with
|
||||
// 'X-AppEngine...' for better integration with that PaaS.
|
||||
AppEngine bool
|
||||
|
||||
// If enabled, the url.RawPath will be used to find parameters.
|
||||
// UseRawPath if enabled, the url.RawPath will be used to find parameters.
|
||||
UseRawPath bool
|
||||
|
||||
// If true, the path value will be unescaped.
|
||||
// UnescapePathValues if true, the path value will be unescaped.
|
||||
// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
|
||||
// as url.Path gonna be used, which is already unescaped.
|
||||
UnescapePathValues bool
|
||||
@ -127,20 +130,23 @@ type Engine struct {
|
||||
// See the PR #1817 and issue #1644
|
||||
RemoveExtraSlash bool
|
||||
|
||||
// List of headers used to obtain the client IP when
|
||||
// RemoteIPHeaders list of headers used to obtain the client IP when
|
||||
// `(*gin.Engine).ForwardedByClientIP` is `true` and
|
||||
// `(*gin.Context).Request.RemoteAddr` is matched by at least one of the
|
||||
// network origins of list defined by `(*gin.Engine).SetTrustedProxies()`.
|
||||
RemoteIPHeaders []string
|
||||
|
||||
// If set to a constant of value gin.Platform*, trusts the headers set by
|
||||
// TrustedPlatform if set to a constant of value gin.Platform*, trusts the headers set by
|
||||
// that platform, for example to determine the client IP
|
||||
TrustedPlatform string
|
||||
|
||||
// Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
|
||||
// MaxMultipartMemory value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
|
||||
// method call.
|
||||
MaxMultipartMemory int64
|
||||
|
||||
// UseH2C enable h2c support.
|
||||
UseH2C bool
|
||||
|
||||
delims render.Delims
|
||||
secureJSONPrefix string
|
||||
HTMLRender render.HTMLRender
|
||||
@ -193,7 +199,7 @@ func New() *Engine {
|
||||
trustedCIDRs: defaultTrustedCIDRs,
|
||||
}
|
||||
engine.RouterGroup.engine = engine
|
||||
engine.pool.New = func() interface{} {
|
||||
engine.pool.New = func() any {
|
||||
return engine.allocateContext()
|
||||
}
|
||||
return engine
|
||||
@ -207,6 +213,15 @@ func Default() *Engine {
|
||||
return engine
|
||||
}
|
||||
|
||||
func (engine *Engine) Handler() http.Handler {
|
||||
if !engine.UseH2C {
|
||||
return engine
|
||||
}
|
||||
|
||||
h2s := &http2.Server{}
|
||||
return h2c.NewHandler(engine, h2s)
|
||||
}
|
||||
|
||||
func (engine *Engine) allocateContext() *Context {
|
||||
v := make(Params, 0, engine.maxParams)
|
||||
skippedNodes := make([]skippedNode, 0, engine.maxSections)
|
||||
@ -361,7 +376,7 @@ func (engine *Engine) Run(addr ...string) (err error) {
|
||||
|
||||
address := resolveAddress(addr)
|
||||
debugPrint("Listening and serving HTTP on %s\n", address)
|
||||
err = http.ListenAndServe(address, engine)
|
||||
err = http.ListenAndServe(address, engine.Handler())
|
||||
return
|
||||
}
|
||||
|
||||
@ -480,7 +495,7 @@ func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {
|
||||
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
|
||||
}
|
||||
|
||||
err = http.ListenAndServeTLS(addr, certFile, keyFile, engine)
|
||||
err = http.ListenAndServeTLS(addr, certFile, keyFile, engine.Handler())
|
||||
return
|
||||
}
|
||||
|
||||
@ -503,7 +518,7 @@ func (engine *Engine) RunUnix(file string) (err error) {
|
||||
defer listener.Close()
|
||||
defer os.Remove(file)
|
||||
|
||||
err = http.Serve(listener, engine)
|
||||
err = http.Serve(listener, engine.Handler())
|
||||
return
|
||||
}
|
||||
|
||||
@ -540,7 +555,7 @@ func (engine *Engine) RunListener(listener net.Listener) (err error) {
|
||||
"Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
|
||||
}
|
||||
|
||||
err = http.Serve(listener, engine)
|
||||
err = http.Serve(listener, engine.Handler())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ func StaticFS(relativePath string, fs http.FileSystem) gin.IRoutes {
|
||||
return engine().StaticFS(relativePath, fs)
|
||||
}
|
||||
|
||||
// Use attaches a global middleware to the router. i.e. the middlewares attached though Use() will be
|
||||
// Use attaches a global middleware to the router. i.e. the middlewares attached through Use() will be
|
||||
// included in the handlers chain for every single request. Even 404, 405, static files...
|
||||
// For example, this is the right place for a logger or error management middleware.
|
||||
func Use(middlewares ...gin.HandlerFunc) gin.IRoutes {
|
||||
|
43
gin_test.go
43
gin_test.go
@ -19,6 +19,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
func formatAsDate(t time.Time) string {
|
||||
@ -42,7 +43,7 @@ func setupHTMLFiles(t *testing.T, mode string, tls bool, loadMethod func(*Engine
|
||||
c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"})
|
||||
})
|
||||
router.GET("/raw", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
|
||||
c.HTML(http.StatusOK, "raw.tmpl", map[string]any{
|
||||
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
|
||||
})
|
||||
})
|
||||
@ -79,6 +80,44 @@ func TestLoadHTMLGlobDebugMode(t *testing.T) {
|
||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
|
||||
}
|
||||
|
||||
func TestH2c(t *testing.T) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
r := Default()
|
||||
r.UseH2C = true
|
||||
r.GET("/", func(c *Context) {
|
||||
c.String(200, "<h1>Hello world</h1>")
|
||||
})
|
||||
go func() {
|
||||
err := http.Serve(ln, r.Handler())
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}()
|
||||
defer ln.Close()
|
||||
|
||||
url := "http://" + ln.Addr().String() + "/"
|
||||
|
||||
http := http.Client{
|
||||
Transport: &http2.Transport{
|
||||
AllowHTTP: true,
|
||||
DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||
return net.Dial(netw, addr)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
res, err := http.Get(url)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
resp, _ := ioutil.ReadAll(res.Body)
|
||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
|
||||
}
|
||||
|
||||
func TestLoadHTMLGlobTestMode(t *testing.T) {
|
||||
ts := setupHTMLFiles(
|
||||
t,
|
||||
@ -428,7 +467,7 @@ func TestNoMethodWithGlobalHandlers(t *testing.T) {
|
||||
compareFunc(t, router.allNoMethod[2], middleware0)
|
||||
}
|
||||
|
||||
func compareFunc(t *testing.T, a, b interface{}) {
|
||||
func compareFunc(t *testing.T, a, b any) {
|
||||
sf1 := reflect.ValueOf(a)
|
||||
sf2 := reflect.ValueOf(b)
|
||||
if sf1.Pointer() != sf2.Pointer() {
|
||||
|
23
go.mod
23
go.mod
@ -1,17 +1,30 @@
|
||||
module github.com/gin-gonic/gin
|
||||
|
||||
go 1.13
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/sse v0.1.0
|
||||
github.com/go-playground/validator/v10 v10.9.0
|
||||
github.com/goccy/go-json v0.8.1
|
||||
github.com/go-playground/validator/v10 v10.10.0
|
||||
github.com/goccy/go-json v0.9.5
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/mattn/go-isatty v0.0.14
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/ugorji/go/codec v1.2.6
|
||||
github.com/ugorji/go/codec v1.2.7
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
retract v1.7.5
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
16
go.sum
16
go.sum
@ -10,10 +10,10 @@ github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A=
|
||||
github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI=
|
||||
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/goccy/go-json v0.9.5 h1:ooSMW526ZjK+EaL5elrSyN2EzIfi/3V0m4+HJEDYLik=
|
||||
github.com/goccy/go-json v0.9.5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
@ -47,12 +47,12 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
|
||||
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
|
||||
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
|
||||
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -45,7 +45,7 @@ type LoggerConfig struct {
|
||||
// Optional. Default value is gin.DefaultWriter.
|
||||
Output io.Writer
|
||||
|
||||
// SkipPaths is a url path array which logs are not written.
|
||||
// SkipPaths is an url path array which logs are not written.
|
||||
// Optional.
|
||||
SkipPaths []string
|
||||
}
|
||||
@ -76,7 +76,7 @@ type LogFormatterParams struct {
|
||||
// BodySize is the size of the Response Body
|
||||
BodySize int
|
||||
// Keys are the keys set on the request's context.
|
||||
Keys map[string]interface{}
|
||||
Keys map[string]any
|
||||
}
|
||||
|
||||
// StatusCodeColor is the ANSI color for appropriately logging http status code to a terminal.
|
||||
@ -162,12 +162,12 @@ func ForceConsoleColor() {
|
||||
consoleColorMode = forceColor
|
||||
}
|
||||
|
||||
// ErrorLogger returns a handlerfunc for any error type.
|
||||
// ErrorLogger returns a HandlerFunc for any error type.
|
||||
func ErrorLogger() HandlerFunc {
|
||||
return ErrorLoggerT(ErrorTypeAny)
|
||||
}
|
||||
|
||||
// ErrorLoggerT returns a handlerfunc for a given error type.
|
||||
// ErrorLoggerT returns a HandlerFunc for a given error type.
|
||||
func ErrorLoggerT(typ ErrorType) HandlerFunc {
|
||||
return func(c *Context) {
|
||||
c.Next()
|
||||
|
@ -181,7 +181,7 @@ func TestLoggerWithFormatter(t *testing.T) {
|
||||
|
||||
func TestLoggerWithConfigFormatting(t *testing.T) {
|
||||
var gotParam LogFormatterParams
|
||||
var gotKeys map[string]interface{}
|
||||
var gotKeys map[string]any
|
||||
buffer := new(bytes.Buffer)
|
||||
|
||||
router := New()
|
||||
|
@ -28,7 +28,7 @@ var (
|
||||
)
|
||||
|
||||
// RecoveryFunc defines the function passable to CustomRecovery.
|
||||
type RecoveryFunc func(c *Context, err interface{})
|
||||
type RecoveryFunc func(c *Context, err any)
|
||||
|
||||
// Recovery returns a middleware that recovers from any panics and writes a 500 if there was one.
|
||||
func Recovery() HandlerFunc {
|
||||
@ -102,7 +102,7 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func defaultHandleRecovery(c *Context, err interface{}) {
|
||||
func defaultHandleRecovery(c *Context, err any) {
|
||||
c.AbortWithStatus(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ func TestCustomRecoveryWithWriter(t *testing.T) {
|
||||
errBuffer := new(bytes.Buffer)
|
||||
buffer := new(bytes.Buffer)
|
||||
router := New()
|
||||
handleRecovery := func(c *Context, err interface{}) {
|
||||
handleRecovery := func(c *Context, err any) {
|
||||
errBuffer.WriteString(err.(string))
|
||||
c.AbortWithStatus(http.StatusBadRequest)
|
||||
}
|
||||
@ -183,7 +183,7 @@ func TestCustomRecovery(t *testing.T) {
|
||||
buffer := new(bytes.Buffer)
|
||||
router := New()
|
||||
DefaultErrorWriter = buffer
|
||||
handleRecovery := func(c *Context, err interface{}) {
|
||||
handleRecovery := func(c *Context, err any) {
|
||||
errBuffer.WriteString(err.(string))
|
||||
c.AbortWithStatus(http.StatusBadRequest)
|
||||
}
|
||||
@ -218,7 +218,7 @@ func TestRecoveryWithWriterWithCustomRecovery(t *testing.T) {
|
||||
buffer := new(bytes.Buffer)
|
||||
router := New()
|
||||
DefaultErrorWriter = buffer
|
||||
handleRecovery := func(c *Context, err interface{}) {
|
||||
handleRecovery := func(c *Context, err any) {
|
||||
errBuffer.WriteString(err.(string))
|
||||
c.AbortWithStatus(http.StatusBadRequest)
|
||||
}
|
||||
|
10
render/any.go
Normal file
10
render/any.go
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright 2021 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 !go1.18
|
||||
// +build !go1.18
|
||||
|
||||
package render
|
||||
|
||||
type any = interface{}
|
@ -20,7 +20,7 @@ type Delims struct {
|
||||
// HTMLRender interface is to be implemented by HTMLProduction and HTMLDebug.
|
||||
type HTMLRender interface {
|
||||
// Instance returns an HTML instance.
|
||||
Instance(string, interface{}) Render
|
||||
Instance(string, any) Render
|
||||
}
|
||||
|
||||
// HTMLProduction contains template reference and its delims.
|
||||
@ -41,13 +41,13 @@ type HTMLDebug struct {
|
||||
type HTML struct {
|
||||
Template *template.Template
|
||||
Name string
|
||||
Data interface{}
|
||||
Data any
|
||||
}
|
||||
|
||||
var htmlContentType = []string{"text/html; charset=utf-8"}
|
||||
|
||||
// Instance (HTMLProduction) returns an HTML instance which it realizes Render interface.
|
||||
func (r HTMLProduction) Instance(name string, data interface{}) Render {
|
||||
func (r HTMLProduction) Instance(name string, data any) Render {
|
||||
return HTML{
|
||||
Template: r.Template,
|
||||
Name: name,
|
||||
@ -56,7 +56,7 @@ func (r HTMLProduction) Instance(name string, data interface{}) Render {
|
||||
}
|
||||
|
||||
// Instance (HTMLDebug) returns an HTML instance which it realizes Render interface.
|
||||
func (r HTMLDebug) Instance(name string, data interface{}) Render {
|
||||
func (r HTMLDebug) Instance(name string, data any) Render {
|
||||
return HTML{
|
||||
Template: r.loadTemplate(),
|
||||
Name: name,
|
||||
|
@ -16,34 +16,34 @@ import (
|
||||
|
||||
// JSON contains the given interface object.
|
||||
type JSON struct {
|
||||
Data interface{}
|
||||
Data any
|
||||
}
|
||||
|
||||
// IndentedJSON contains the given interface object.
|
||||
type IndentedJSON struct {
|
||||
Data interface{}
|
||||
Data any
|
||||
}
|
||||
|
||||
// SecureJSON contains the given interface object and its prefix.
|
||||
type SecureJSON struct {
|
||||
Prefix string
|
||||
Data interface{}
|
||||
Data any
|
||||
}
|
||||
|
||||
// JsonpJSON contains the given interface object its callback.
|
||||
type JsonpJSON struct {
|
||||
Callback string
|
||||
Data interface{}
|
||||
Data any
|
||||
}
|
||||
|
||||
// AsciiJSON contains the given interface object.
|
||||
type AsciiJSON struct {
|
||||
Data interface{}
|
||||
Data any
|
||||
}
|
||||
|
||||
// PureJSON contains the given interface object.
|
||||
type PureJSON struct {
|
||||
Data interface{}
|
||||
Data any
|
||||
}
|
||||
|
||||
var (
|
||||
@ -66,7 +66,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 interface{}) error {
|
||||
func WriteJSON(w http.ResponseWriter, obj any) error {
|
||||
writeContentType(w, jsonContentType)
|
||||
jsonBytes, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
|
@ -21,7 +21,7 @@ var (
|
||||
|
||||
// MsgPack contains the given interface object.
|
||||
type MsgPack struct {
|
||||
Data interface{}
|
||||
Data any
|
||||
}
|
||||
|
||||
var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
|
||||
@ -37,7 +37,7 @@ func (r MsgPack) Render(w http.ResponseWriter) error {
|
||||
}
|
||||
|
||||
// WriteMsgPack writes MsgPack ContentType and encodes the given interface object.
|
||||
func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
|
||||
func WriteMsgPack(w http.ResponseWriter, obj any) error {
|
||||
writeContentType(w, msgpackContentType)
|
||||
var mh codec.MsgpackHandle
|
||||
return codec.NewEncoder(w, &mh).Encode(obj)
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
|
||||
// ProtoBuf contains the given interface object.
|
||||
type ProtoBuf struct {
|
||||
Data interface{}
|
||||
Data any
|
||||
}
|
||||
|
||||
var protobufContentType = []string{"application/x-protobuf"}
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
|
||||
func TestRenderMsgPack(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
data := map[string]interface{}{
|
||||
data := map[string]any{
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
|
||||
func TestRenderJSON(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
data := map[string]interface{}{
|
||||
data := map[string]any{
|
||||
"foo": "bar",
|
||||
"html": "<b>",
|
||||
}
|
||||
@ -49,7 +49,7 @@ func TestRenderJSONPanics(t *testing.T) {
|
||||
|
||||
func TestRenderIndentedJSON(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
data := map[string]interface{}{
|
||||
data := map[string]any{
|
||||
"foo": "bar",
|
||||
"bar": "foo",
|
||||
}
|
||||
@ -72,7 +72,7 @@ func TestRenderIndentedJSONPanics(t *testing.T) {
|
||||
|
||||
func TestRenderSecureJSON(t *testing.T) {
|
||||
w1 := httptest.NewRecorder()
|
||||
data := map[string]interface{}{
|
||||
data := map[string]any{
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ func TestRenderSecureJSON(t *testing.T) {
|
||||
assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
|
||||
|
||||
w2 := httptest.NewRecorder()
|
||||
datas := []map[string]interface{}{{
|
||||
datas := []map[string]any{{
|
||||
"foo": "bar",
|
||||
}, {
|
||||
"bar": "foo",
|
||||
@ -109,7 +109,7 @@ func TestRenderSecureJSONFail(t *testing.T) {
|
||||
|
||||
func TestRenderJsonpJSON(t *testing.T) {
|
||||
w1 := httptest.NewRecorder()
|
||||
data := map[string]interface{}{
|
||||
data := map[string]any{
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@ func TestRenderJsonpJSON(t *testing.T) {
|
||||
assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type"))
|
||||
|
||||
w2 := httptest.NewRecorder()
|
||||
datas := []map[string]interface{}{{
|
||||
datas := []map[string]any{{
|
||||
"foo": "bar",
|
||||
}, {
|
||||
"bar": "foo",
|
||||
@ -137,7 +137,7 @@ func TestRenderJsonpJSON(t *testing.T) {
|
||||
|
||||
func TestRenderJsonpJSONError2(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
data := map[string]interface{}{
|
||||
data := map[string]any{
|
||||
"foo": "bar",
|
||||
}
|
||||
(JsonpJSON{"", data}).WriteContentType(w)
|
||||
@ -161,7 +161,7 @@ func TestRenderJsonpJSONFail(t *testing.T) {
|
||||
|
||||
func TestRenderAsciiJSON(t *testing.T) {
|
||||
w1 := httptest.NewRecorder()
|
||||
data1 := map[string]interface{}{
|
||||
data1 := map[string]any{
|
||||
"lang": "GO语言",
|
||||
"tag": "<br>",
|
||||
}
|
||||
@ -190,7 +190,7 @@ func TestRenderAsciiJSONFail(t *testing.T) {
|
||||
|
||||
func TestRenderPureJSON(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
data := map[string]interface{}{
|
||||
data := map[string]any{
|
||||
"foo": "bar",
|
||||
"html": "<b>",
|
||||
}
|
||||
@ -200,7 +200,7 @@ func TestRenderPureJSON(t *testing.T) {
|
||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
type xmlmap map[string]interface{}
|
||||
type xmlmap map[string]any
|
||||
|
||||
// Allows type H to be used with xml.Marshal
|
||||
func (h xmlmap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
@ -244,7 +244,7 @@ b:
|
||||
type fail struct{}
|
||||
|
||||
// Hook MarshalYAML
|
||||
func (ft *fail) MarshalYAML() (interface{}, error) {
|
||||
func (ft *fail) MarshalYAML() (any, error) {
|
||||
return nil, errors.New("fail")
|
||||
}
|
||||
|
||||
@ -358,13 +358,13 @@ func TestRenderString(t *testing.T) {
|
||||
|
||||
(String{
|
||||
Format: "hello %s %d",
|
||||
Data: []interface{}{},
|
||||
Data: []any{},
|
||||
}).WriteContentType(w)
|
||||
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
|
||||
err := (String{
|
||||
Format: "hola %s %d",
|
||||
Data: []interface{}{"manu", 2},
|
||||
Data: []any{"manu", 2},
|
||||
}).Render(w)
|
||||
|
||||
assert.NoError(t, err)
|
||||
@ -377,7 +377,7 @@ func TestRenderStringLenZero(t *testing.T) {
|
||||
|
||||
err := (String{
|
||||
Format: "hola %s %d",
|
||||
Data: []interface{}{},
|
||||
Data: []any{},
|
||||
}).Render(w)
|
||||
|
||||
assert.NoError(t, err)
|
||||
@ -390,7 +390,7 @@ func TestRenderHTMLTemplate(t *testing.T) {
|
||||
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
||||
|
||||
htmlRender := HTMLProduction{Template: templ}
|
||||
instance := htmlRender.Instance("t", map[string]interface{}{
|
||||
instance := htmlRender.Instance("t", map[string]any{
|
||||
"name": "alexandernyquist",
|
||||
})
|
||||
|
||||
@ -406,7 +406,7 @@ func TestRenderHTMLTemplateEmptyName(t *testing.T) {
|
||||
templ := template.Must(template.New("").Parse(`Hello {{.name}}`))
|
||||
|
||||
htmlRender := HTMLProduction{Template: templ}
|
||||
instance := htmlRender.Instance("", map[string]interface{}{
|
||||
instance := htmlRender.Instance("", map[string]any{
|
||||
"name": "alexandernyquist",
|
||||
})
|
||||
|
||||
@ -425,7 +425,7 @@ func TestRenderHTMLDebugFiles(t *testing.T) {
|
||||
Delims: Delims{Left: "{[{", Right: "}]}"},
|
||||
FuncMap: nil,
|
||||
}
|
||||
instance := htmlRender.Instance("hello.tmpl", map[string]interface{}{
|
||||
instance := htmlRender.Instance("hello.tmpl", map[string]any{
|
||||
"name": "thinkerou",
|
||||
})
|
||||
|
||||
@ -444,7 +444,7 @@ func TestRenderHTMLDebugGlob(t *testing.T) {
|
||||
Delims: Delims{Left: "{[{", Right: "}]}"},
|
||||
FuncMap: nil,
|
||||
}
|
||||
instance := htmlRender.Instance("hello.tmpl", map[string]interface{}{
|
||||
instance := htmlRender.Instance("hello.tmpl", map[string]any{
|
||||
"name": "thinkerou",
|
||||
})
|
||||
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
// String contains the given interface object slice and its format.
|
||||
type String struct {
|
||||
Format string
|
||||
Data []interface{}
|
||||
Data []any
|
||||
}
|
||||
|
||||
var plainContentType = []string{"text/plain; charset=utf-8"}
|
||||
@ -30,7 +30,7 @@ func (r String) WriteContentType(w http.ResponseWriter) {
|
||||
}
|
||||
|
||||
// WriteString writes data according to its format and write custom ContentType.
|
||||
func WriteString(w http.ResponseWriter, format string, data []interface{}) (err error) {
|
||||
func WriteString(w http.ResponseWriter, format string, data []any) (err error) {
|
||||
writeContentType(w, plainContentType)
|
||||
if len(data) > 0 {
|
||||
_, err = fmt.Fprintf(w, format, data...)
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
|
||||
// XML contains the given interface object.
|
||||
type XML struct {
|
||||
Data interface{}
|
||||
Data any
|
||||
}
|
||||
|
||||
var xmlContentType = []string{"application/xml; charset=utf-8"}
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
|
||||
// YAML contains the given interface object.
|
||||
type YAML struct {
|
||||
Data interface{}
|
||||
Data any
|
||||
}
|
||||
|
||||
var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
|
||||
|
@ -23,23 +23,23 @@ type ResponseWriter interface {
|
||||
http.Flusher
|
||||
http.CloseNotifier
|
||||
|
||||
// Returns the HTTP response status code of the current request.
|
||||
// Status returns the HTTP response status code of the current request.
|
||||
Status() int
|
||||
|
||||
// Returns the number of bytes already written into the response http body.
|
||||
// Size returns the number of bytes already written into the response http body.
|
||||
// See Written()
|
||||
Size() int
|
||||
|
||||
// Writes the string into the response body.
|
||||
// WriteString writes the string into the response body.
|
||||
WriteString(string) (int, error)
|
||||
|
||||
// Returns true if the response body was already written.
|
||||
// Written returns true if the response body was already written.
|
||||
Written() bool
|
||||
|
||||
// Forces to write the http header (status code + headers).
|
||||
// WriteHeaderNow forces to write the http header (status code + headers).
|
||||
WriteHeaderNow()
|
||||
|
||||
// get the http.Pusher for server push
|
||||
// Pusher get the http.Pusher for server push
|
||||
Pusher() http.Pusher
|
||||
}
|
||||
|
||||
@ -107,12 +107,12 @@ func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return w.ResponseWriter.(http.Hijacker).Hijack()
|
||||
}
|
||||
|
||||
// CloseNotify implements the http.CloseNotify interface.
|
||||
// CloseNotify implements the http.CloseNotifier interface.
|
||||
func (w *responseWriter) CloseNotify() <-chan bool {
|
||||
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
// Flush implements the http.Flush interface.
|
||||
// Flush implements the http.Flusher interface.
|
||||
func (w *responseWriter) Flush() {
|
||||
w.WriteHeaderNow()
|
||||
w.ResponseWriter.(http.Flusher).Flush()
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// reg match english letters for http method name
|
||||
// regEnLetter matches english letters for http method name
|
||||
regEnLetter = regexp.MustCompile("^[A-Z]+$")
|
||||
|
||||
// anyMethods for RouterGroup Any method
|
||||
@ -44,6 +44,7 @@ type IRoutes interface {
|
||||
HEAD(string, ...HandlerFunc) IRoutes
|
||||
|
||||
StaticFile(string, string) IRoutes
|
||||
StaticFileFS(string, string, http.FileSystem) IRoutes
|
||||
Static(string, string) IRoutes
|
||||
StaticFS(string, http.FileSystem) IRoutes
|
||||
}
|
||||
@ -153,12 +154,24 @@ func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRou
|
||||
// StaticFile registers a single route in order to serve a single file of the local filesystem.
|
||||
// router.StaticFile("favicon.ico", "./resources/favicon.ico")
|
||||
func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes {
|
||||
return group.staticFileHandler(relativePath, func(c *Context) {
|
||||
c.File(filepath)
|
||||
})
|
||||
}
|
||||
|
||||
// StaticFileFS works just like `StaticFile` but a custom `http.FileSystem` can be used instead..
|
||||
// router.StaticFileFS("favicon.ico", "./resources/favicon.ico", Dir{".", false})
|
||||
// Gin by default user: gin.Dir()
|
||||
func (group *RouterGroup) StaticFileFS(relativePath, filepath string, fs http.FileSystem) IRoutes {
|
||||
return group.staticFileHandler(relativePath, func(c *Context) {
|
||||
c.FileFromFS(filepath, fs)
|
||||
})
|
||||
}
|
||||
|
||||
func (group *RouterGroup) staticFileHandler(relativePath string, handler HandlerFunc) IRoutes {
|
||||
if strings.Contains(relativePath, ":") || strings.Contains(relativePath, "*") {
|
||||
panic("URL parameters can not be used when serving a static file")
|
||||
}
|
||||
handler := func(c *Context) {
|
||||
c.File(filepath)
|
||||
}
|
||||
group.GET(relativePath, handler)
|
||||
group.HEAD(relativePath, handler)
|
||||
return group.returnObj()
|
||||
|
@ -111,6 +111,17 @@ func TestRouterGroupInvalidStaticFile(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRouterGroupInvalidStaticFileFS(t *testing.T) {
|
||||
router := New()
|
||||
assert.Panics(t, func() {
|
||||
router.StaticFileFS("/path/:param", "favicon.ico", Dir(".", false))
|
||||
})
|
||||
|
||||
assert.Panics(t, func() {
|
||||
router.StaticFileFS("/path/*param", "favicon.ico", Dir(".", false))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRouterGroupTooManyHandlers(t *testing.T) {
|
||||
const (
|
||||
panicValue = "too many handlers"
|
||||
@ -177,6 +188,7 @@ func testRoutesInterface(t *testing.T, r IRoutes) {
|
||||
assert.Equal(t, r, r.HEAD("/", handler))
|
||||
|
||||
assert.Equal(t, r, r.StaticFile("/file", "."))
|
||||
assert.Equal(t, r, r.StaticFileFS("/static2", ".", Dir(".", false)))
|
||||
assert.Equal(t, r, r.Static("/static", "."))
|
||||
assert.Equal(t, r, r.StaticFS("/static2", Dir(".", false)))
|
||||
}
|
||||
|
@ -325,6 +325,40 @@ func TestRouteStaticFile(t *testing.T) {
|
||||
assert.Equal(t, http.StatusOK, w3.Code)
|
||||
}
|
||||
|
||||
// TestHandleStaticFile - ensure the static file handles properly
|
||||
func TestRouteStaticFileFS(t *testing.T) {
|
||||
// SETUP file
|
||||
testRoot, _ := os.Getwd()
|
||||
f, err := ioutil.TempFile(testRoot, "")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
_, err = f.WriteString("Gin Web Framework")
|
||||
assert.NoError(t, err)
|
||||
f.Close()
|
||||
|
||||
dir, filename := filepath.Split(f.Name())
|
||||
// SETUP gin
|
||||
router := New()
|
||||
router.Static("/using_static", dir)
|
||||
router.StaticFileFS("/result_fs", filename, Dir(dir, false))
|
||||
|
||||
w := PerformRequest(router, http.MethodGet, "/using_static/"+filename)
|
||||
w2 := PerformRequest(router, http.MethodGet, "/result_fs")
|
||||
|
||||
assert.Equal(t, w, w2)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, "Gin Web Framework", w.Body.String())
|
||||
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
|
||||
w3 := PerformRequest(router, http.MethodHead, "/using_static/"+filename)
|
||||
w4 := PerformRequest(router, http.MethodHead, "/result_fs")
|
||||
|
||||
assert.Equal(t, w3, w4)
|
||||
assert.Equal(t, http.StatusOK, w3.Code)
|
||||
}
|
||||
|
||||
// TestHandleStaticDir - ensure the root/sub dir handles properly
|
||||
func TestRouteStaticListingDir(t *testing.T) {
|
||||
router := New()
|
||||
|
10
testdata/protoexample/any.go
vendored
Normal file
10
testdata/protoexample/any.go
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright 2021 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 !go1.18
|
||||
// +build !go1.18
|
||||
|
||||
package protoexample
|
||||
|
||||
type any = interface{}
|
6
testdata/protoexample/test.pb.go
vendored
6
testdata/protoexample/test.pb.go
vendored
@ -231,7 +231,7 @@ func file_test_proto_rawDescGZIP() []byte {
|
||||
|
||||
var file_test_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_test_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_test_proto_goTypes = []interface{}{
|
||||
var file_test_proto_goTypes = []any{
|
||||
(FOO)(0), // 0: protoexample.FOO
|
||||
(*Test)(nil), // 1: protoexample.Test
|
||||
(*Test_OptionalGroup)(nil), // 2: protoexample.Test.OptionalGroup
|
||||
@ -251,7 +251,7 @@ func file_test_proto_init() {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_test_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
file_test_proto_msgTypes[0].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*Test); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@ -263,7 +263,7 @@ func file_test_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_test_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
file_test_proto_msgTypes[1].Exporter = func(v any, i int) any {
|
||||
switch v := v.(*Test_OptionalGroup); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
|
6
tree.go
6
tree.go
@ -81,7 +81,7 @@ func longestCommonPrefix(a, b string) int {
|
||||
return i
|
||||
}
|
||||
|
||||
// addChild will add a child node, keeping wildcards at the end
|
||||
// addChild will add a child node, keeping wildcardChild at the end
|
||||
func (n *node) addChild(child *node) {
|
||||
if n.wildChild && len(n.children) > 0 {
|
||||
wildcardChild := n.children[len(n.children)-1]
|
||||
@ -296,7 +296,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain)
|
||||
break
|
||||
}
|
||||
|
||||
// The wildcard name must not contain ':' and '*'
|
||||
// The wildcard name must only contain one ':' or '*' character
|
||||
if !valid {
|
||||
panic("only one wildcard per path segment is allowed, has: '" +
|
||||
wildcard + "' in path '" + fullPath + "'")
|
||||
@ -325,7 +325,7 @@ func (n *node) insertChild(path string, fullPath string, handlers HandlersChain)
|
||||
n.priority++
|
||||
|
||||
// if the path doesn't end with the wildcard, then there
|
||||
// will be another non-wildcard subpath starting with '/'
|
||||
// will be another subpath starting with '/'
|
||||
if len(wildcard) < len(path) {
|
||||
path = path[len(wildcard):]
|
||||
|
||||
|
@ -357,7 +357,7 @@ func TestUnescapeParameters(t *testing.T) {
|
||||
checkPriorities(t, tree)
|
||||
}
|
||||
|
||||
func catchPanic(testFunc func()) (recv interface{}) {
|
||||
func catchPanic(testFunc func()) (recv any) {
|
||||
defer func() {
|
||||
recv = recover()
|
||||
}()
|
||||
|
19
utils.go
19
utils.go
@ -12,13 +12,14 @@ import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// BindKey indicates a default bind key.
|
||||
const BindKey = "_gin-gonic/gin/bindkey"
|
||||
|
||||
// Bind is a helper function for given interface object and returns a Gin middleware.
|
||||
func Bind(val interface{}) HandlerFunc {
|
||||
func Bind(val any) HandlerFunc {
|
||||
value := reflect.ValueOf(val)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
panic(`Bind struct can not be a pointer. Example:
|
||||
@ -50,7 +51,7 @@ func WrapH(h http.Handler) HandlerFunc {
|
||||
}
|
||||
|
||||
// H is a shortcut for map[string]interface{}
|
||||
type H map[string]interface{}
|
||||
type H map[string]any
|
||||
|
||||
// MarshalXML allows type H to be used with xml.Marshal.
|
||||
func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||
@ -89,7 +90,7 @@ func filterFlags(content string) string {
|
||||
return content
|
||||
}
|
||||
|
||||
func chooseData(custom, wildcard interface{}) interface{} {
|
||||
func chooseData(custom, wildcard any) any {
|
||||
if custom != nil {
|
||||
return custom
|
||||
}
|
||||
@ -120,7 +121,7 @@ func lastChar(str string) uint8 {
|
||||
return str[len(str)-1]
|
||||
}
|
||||
|
||||
func nameOfFunction(f interface{}) string {
|
||||
func nameOfFunction(f any) string {
|
||||
return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
|
||||
}
|
||||
|
||||
@ -151,3 +152,13 @@ func resolveAddress(addr []string) string {
|
||||
panic("too many parameters")
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/53069040/checking-a-string-contains-only-ascii-characters
|
||||
func isASCII(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] > unicode.MaxASCII {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -143,3 +143,8 @@ func TestMarshalXMLforH(t *testing.T) {
|
||||
e := h.MarshalXML(enc, x)
|
||||
assert.Error(t, e)
|
||||
}
|
||||
|
||||
func TestIsASCII(t *testing.T) {
|
||||
assert.Equal(t, isASCII("test"), true)
|
||||
assert.Equal(t, isASCII("🧡💛💚💙💜"), false)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user