mirror of https://github.com/gin-gonic/gin.git
Merge branch 'master' into master
This commit is contained in:
commit
84a97ef73e
|
@ -3,8 +3,6 @@ language: go
|
||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
include:
|
include:
|
||||||
- go: 1.12.x
|
|
||||||
env: GO111MODULE=on
|
|
||||||
- go: 1.13.x
|
- go: 1.13.x
|
||||||
- go: 1.13.x
|
- go: 1.13.x
|
||||||
env:
|
env:
|
||||||
|
@ -17,6 +15,10 @@ matrix:
|
||||||
- go: 1.15.x
|
- go: 1.15.x
|
||||||
env:
|
env:
|
||||||
- TESTTAGS=nomsgpack
|
- TESTTAGS=nomsgpack
|
||||||
|
- go: 1.16.x
|
||||||
|
- go: 1.16.x
|
||||||
|
env:
|
||||||
|
- TESTTAGS=nomsgpack
|
||||||
- go: master
|
- go: master
|
||||||
|
|
||||||
git:
|
git:
|
||||||
|
|
27
README.md
27
README.md
|
@ -23,7 +23,8 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
|
||||||
- [Quick start](#quick-start)
|
- [Quick start](#quick-start)
|
||||||
- [Benchmarks](#benchmarks)
|
- [Benchmarks](#benchmarks)
|
||||||
- [Gin v1. stable](#gin-v1-stable)
|
- [Gin v1. stable](#gin-v1-stable)
|
||||||
- [Build with jsoniter](#build-with-jsoniter)
|
- [Build with jsoniter/go-json](#build-with-json-replacement)
|
||||||
|
- [Build without `MsgPack` rendering feature](#build-without-msgpack-rendering-feature)
|
||||||
- [API Examples](#api-examples)
|
- [API Examples](#api-examples)
|
||||||
- [Using GET, POST, PUT, PATCH, DELETE and OPTIONS](#using-get-post-put-patch-delete-and-options)
|
- [Using GET, POST, PUT, PATCH, DELETE and OPTIONS](#using-get-post-put-patch-delete-and-options)
|
||||||
- [Parameters in path](#parameters-in-path)
|
- [Parameters in path](#parameters-in-path)
|
||||||
|
@ -84,7 +85,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.
|
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.12+ is required**), then you can use the below Go command to install Gin.
|
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.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ go get -u github.com/gin-gonic/gin
|
$ go get -u github.com/gin-gonic/gin
|
||||||
|
@ -182,13 +183,28 @@ Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httpr
|
||||||
- [x] Battle tested.
|
- [x] Battle tested.
|
||||||
- [x] API frozen, new releases will not break your code.
|
- [x] API frozen, new releases will not break your code.
|
||||||
|
|
||||||
## Build with [jsoniter](https://github.com/json-iterator/go)
|
## Build with json replacement
|
||||||
|
|
||||||
Gin uses `encoding/json` as default json package but you can change to [jsoniter](https://github.com/json-iterator/go) by build from other tags.
|
Gin uses `encoding/json` as default json package but you can change it by build from other tags.
|
||||||
|
|
||||||
|
[jsoniter](https://github.com/json-iterator/go)
|
||||||
```sh
|
```sh
|
||||||
$ go build -tags=jsoniter .
|
$ go build -tags=jsoniter .
|
||||||
```
|
```
|
||||||
|
[go-json](https://github.com/goccy/go-json)
|
||||||
|
```sh
|
||||||
|
$ go build -tags=go_json .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build without `MsgPack` rendering feature
|
||||||
|
|
||||||
|
Gin enables `MsgPack` rendering feature by default. But you can disable this feature by specifying `nomsgpack` build tag.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ go build -tags=nomsgpack .
|
||||||
|
```
|
||||||
|
|
||||||
|
This is useful to reduce the binary size of executable files. See the [detail information](https://github.com/gin-gonic/gin/pull/1852).
|
||||||
|
|
||||||
## API Examples
|
## API Examples
|
||||||
|
|
||||||
|
@ -925,7 +941,7 @@ func main() {
|
||||||
route.GET("/:name/:id", func(c *gin.Context) {
|
route.GET("/:name/:id", func(c *gin.Context) {
|
||||||
var person Person
|
var person Person
|
||||||
if err := c.ShouldBindUri(&person); err != nil {
|
if err := c.ShouldBindUri(&person); err != nil {
|
||||||
c.JSON(400, gin.H{"msg": err})
|
c.JSON(400, gin.H{"msg": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
|
c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})
|
||||||
|
@ -2215,3 +2231,4 @@ Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framewor
|
||||||
* [picfit](https://github.com/thoas/picfit): An image resizing server written in Go.
|
* [picfit](https://github.com/thoas/picfit): An image resizing server written in Go.
|
||||||
* [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes.
|
* [brigade](https://github.com/brigadecore/brigade): Event-based Scripting for Kubernetes.
|
||||||
* [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system.
|
* [dkron](https://github.com/distribworks/dkron): Distributed, fault tolerant job scheduling system.
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ package binding
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ func (jsonBinding) Name() string {
|
||||||
|
|
||||||
func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
|
func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
if req == nil || req.Body == nil {
|
if req == nil || req.Body == nil {
|
||||||
return fmt.Errorf("invalid request")
|
return errors.New("invalid request")
|
||||||
}
|
}
|
||||||
return decodeJSON(req.Body, obj)
|
return decodeJSON(req.Body, obj)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1018,7 +1018,9 @@ func TestContextRenderFile(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, http.StatusOK, w.Code)
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
assert.Contains(t, w.Body.String(), "func New() *Engine {")
|
assert.Contains(t, w.Body.String(), "func New() *Engine {")
|
||||||
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
// 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"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextRenderFileFromFS(t *testing.T) {
|
func TestContextRenderFileFromFS(t *testing.T) {
|
||||||
|
@ -1030,7 +1032,9 @@ func TestContextRenderFileFromFS(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, http.StatusOK, w.Code)
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
assert.Contains(t, w.Body.String(), "func New() *Engine {")
|
assert.Contains(t, w.Body.String(), "func New() *Engine {")
|
||||||
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
// 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.Equal(t, "/some/path", c.Request.URL.Path)
|
assert.Equal(t, "/some/path", c.Request.URL.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1044,7 +1048,7 @@ func TestContextRenderAttachment(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, 200, w.Code)
|
assert.Equal(t, 200, w.Code)
|
||||||
assert.Contains(t, w.Body.String(), "func New() *Engine {")
|
assert.Contains(t, w.Body.String(), "func New() *Engine {")
|
||||||
assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.HeaderMap.Get("Content-Disposition"))
|
assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestContextRenderYAML tests that the response is serialized as YAML
|
// TestContextRenderYAML tests that the response is serialized as YAML
|
||||||
|
|
4
debug.go
4
debug.go
|
@ -12,7 +12,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ginSupportMinGoVer = 12
|
const ginSupportMinGoVer = 13
|
||||||
|
|
||||||
// IsDebugging returns true if the framework is running in debug mode.
|
// IsDebugging returns true if the framework is running in debug mode.
|
||||||
// Use SetMode(gin.ReleaseMode) to disable debug mode.
|
// Use SetMode(gin.ReleaseMode) to disable debug mode.
|
||||||
|
@ -67,7 +67,7 @@ func getMinVer(v string) (uint64, error) {
|
||||||
|
|
||||||
func debugPrintWARNINGDefault() {
|
func debugPrintWARNINGDefault() {
|
||||||
if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer {
|
if v, e := getMinVer(runtime.Version()); e == nil && v <= ginSupportMinGoVer {
|
||||||
debugPrint(`[WARNING] Now Gin requires Go 1.12+.
|
debugPrint(`[WARNING] Now Gin requires Go 1.13+.
|
||||||
|
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ func TestDebugPrintWARNINGDefault(t *testing.T) {
|
||||||
})
|
})
|
||||||
m, e := getMinVer(runtime.Version())
|
m, e := getMinVer(runtime.Version())
|
||||||
if e == nil && m <= ginSupportMinGoVer {
|
if e == nil && m <= ginSupportMinGoVer {
|
||||||
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.12+.\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.13+.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
||||||
} else {
|
} else {
|
||||||
assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
assert.Equal(t, "[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
//go:build go1.13
|
|
||||||
// +build go1.13
|
|
||||||
|
|
||||||
package gin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TestErr string
|
|
||||||
|
|
||||||
func (e TestErr) Error() string { return string(e) }
|
|
||||||
|
|
||||||
// TestErrorUnwrap tests the behavior of gin.Error with "errors.Is()" and "errors.As()".
|
|
||||||
// "errors.Is()" and "errors.As()" have been added to the standard library in go 1.13,
|
|
||||||
// hence the "// +build go1.13" directive at the beginning of this file.
|
|
||||||
func TestErrorUnwrap(t *testing.T) {
|
|
||||||
innerErr := TestErr("somme error")
|
|
||||||
|
|
||||||
// 2 layers of wrapping : use 'fmt.Errorf("%w")' to wrap a gin.Error{}, which itself wraps innerErr
|
|
||||||
err := fmt.Errorf("wrapped: %w", &Error{
|
|
||||||
Err: innerErr,
|
|
||||||
Type: ErrorTypeAny,
|
|
||||||
})
|
|
||||||
|
|
||||||
// check that 'errors.Is()' and 'errors.As()' behave as expected :
|
|
||||||
assert.True(t, errors.Is(err, innerErr))
|
|
||||||
var testErr TestErr
|
|
||||||
assert.True(t, errors.As(err, &testErr))
|
|
||||||
}
|
|
|
@ -6,6 +6,7 @@ package gin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/internal/json"
|
"github.com/gin-gonic/gin/internal/json"
|
||||||
|
@ -104,3 +105,24 @@ Error #03: third
|
||||||
assert.Nil(t, errs.JSON())
|
assert.Nil(t, errs.JSON())
|
||||||
assert.Empty(t, errs.String())
|
assert.Empty(t, errs.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TestErr string
|
||||||
|
|
||||||
|
func (e TestErr) Error() string { return string(e) }
|
||||||
|
|
||||||
|
// TestErrorUnwrap tests the behavior of gin.Error with "errors.Is()" and "errors.As()".
|
||||||
|
// "errors.Is()" and "errors.As()" have been added to the standard library in go 1.13.
|
||||||
|
func TestErrorUnwrap(t *testing.T) {
|
||||||
|
innerErr := TestErr("somme error")
|
||||||
|
|
||||||
|
// 2 layers of wrapping : use 'fmt.Errorf("%w")' to wrap a gin.Error{}, which itself wraps innerErr
|
||||||
|
err := fmt.Errorf("wrapped: %w", &Error{
|
||||||
|
Err: innerErr,
|
||||||
|
Type: ErrorTypeAny,
|
||||||
|
})
|
||||||
|
|
||||||
|
// check that 'errors.Is()' and 'errors.As()' behave as expected :
|
||||||
|
assert.True(t, errors.Is(err, innerErr))
|
||||||
|
var testErr TestErr
|
||||||
|
assert.True(t, errors.As(err, &testErr))
|
||||||
|
}
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -11,6 +11,8 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||||
|
github.com/goccy/go-json v0.4.11 h1:92nyX606ZN/cUFwctfxwDWm8YWSA38Zlv9s7taFeLyo=
|
||||||
|
github.com/goccy/go-json v0.4.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
// 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
|
||||||
|
// +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
|
||||||
|
)
|
|
@ -2,8 +2,8 @@
|
||||||
// Use of this source code is governed by a MIT style
|
// Use of this source code is governed by a MIT style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !jsoniter
|
//go:build !jsoniter && !go_json
|
||||||
// +build !jsoniter
|
// +build !jsoniter,!go_json
|
||||||
|
|
||||||
package json
|
package json
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// reg match english letters for http method name
|
||||||
|
regEnLetter = regexp.MustCompile("^[A-Z]+$")
|
||||||
|
)
|
||||||
|
|
||||||
// IRouter defines all router handle interface includes single and group router.
|
// IRouter defines all router handle interface includes single and group router.
|
||||||
type IRouter interface {
|
type IRouter interface {
|
||||||
IRoutes
|
IRoutes
|
||||||
|
@ -87,7 +92,7 @@ func (group *RouterGroup) handle(httpMethod, relativePath string, handlers Handl
|
||||||
// frequently used, non-standardized or custom methods (e.g. for internal
|
// frequently used, non-standardized or custom methods (e.g. for internal
|
||||||
// communication with a proxy).
|
// communication with a proxy).
|
||||||
func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes {
|
func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
if matches, err := regexp.MatchString("^[A-Z]+$", httpMethod); !matches || err != nil {
|
if matched := regEnLetter.MatchString(httpMethod); !matched {
|
||||||
panic("http method " + httpMethod + " is not valid")
|
panic("http method " + httpMethod + " is not valid")
|
||||||
}
|
}
|
||||||
return group.handle(httpMethod, relativePath, handlers)
|
return group.handle(httpMethod, relativePath, handlers)
|
||||||
|
|
|
@ -360,7 +360,9 @@ func TestRouterMiddlewareAndStatic(t *testing.T) {
|
||||||
|
|
||||||
assert.Equal(t, http.StatusOK, w.Code)
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
assert.Contains(t, w.Body.String(), "package gin")
|
assert.Contains(t, w.Body.String(), "package gin")
|
||||||
assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type"))
|
// 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, w.Header().Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST")
|
||||||
assert.Equal(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Expires"))
|
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"))
|
assert.Equal(t, "Gin Framework", w.Header().Get("x-GIN"))
|
||||||
|
|
Loading…
Reference in New Issue