mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-17 22:32:26 +08:00
Merge branch 'master' into utree
This commit is contained in:
commit
fab69a93f4
@ -178,8 +178,8 @@ Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httpr
|
||||
|
||||
- [x] Zero allocation router.
|
||||
- [x] Still the fastest http router and framework. From routing to writing.
|
||||
- [x] Complete suite of unit tests
|
||||
- [x] Battle tested
|
||||
- [x] Complete suite of unit tests.
|
||||
- [x] Battle tested.
|
||||
- [x] API frozen, new releases will not break your code.
|
||||
|
||||
## Build with [jsoniter](https://github.com/json-iterator/go)
|
||||
@ -340,7 +340,7 @@ func main() {
|
||||
```
|
||||
|
||||
```
|
||||
ids: map[b:hello a:1234], names: map[second:tianou first:thinkerou]
|
||||
ids: map[b:hello a:1234]; names: map[second:tianou first:thinkerou]
|
||||
```
|
||||
|
||||
### Upload files
|
||||
@ -1255,6 +1255,7 @@ func main() {
|
||||
}
|
||||
|
||||
reader := response.Body
|
||||
defer reader.Close()
|
||||
contentLength := response.ContentLength
|
||||
contentType := response.Header.Get("Content-Type")
|
||||
|
||||
|
20
context.go
20
context.go
@ -295,6 +295,22 @@ func (c *Context) GetInt64(key string) (i64 int64) {
|
||||
return
|
||||
}
|
||||
|
||||
// GetUint returns the value associated with the key as an unsigned integer.
|
||||
func (c *Context) GetUint(key string) (ui uint) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
ui, _ = val.(uint)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetUint64 returns the value associated with the key as an unsigned integer.
|
||||
func (c *Context) GetUint64(key string) (ui64 uint64) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
ui64, _ = val.(uint64)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetFloat64 returns the value associated with the key as a float64.
|
||||
func (c *Context) GetFloat64(key string) (f64 float64) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
@ -957,7 +973,7 @@ func (c *Context) File(filepath string) {
|
||||
http.ServeFile(c.Writer, c.Request, filepath)
|
||||
}
|
||||
|
||||
// FileFromFS writes the specified file from http.FileSytem into the body stream in an efficient way.
|
||||
// FileFromFS writes the specified file from http.FileSystem into the body stream in an efficient way.
|
||||
func (c *Context) FileFromFS(filepath string, fs http.FileSystem) {
|
||||
defer func(old string) {
|
||||
c.Request.URL.Path = old
|
||||
@ -971,7 +987,7 @@ 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))
|
||||
c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
|
||||
http.ServeFile(c.Writer, c.Request, filepath)
|
||||
}
|
||||
|
||||
|
@ -261,6 +261,18 @@ func TestContextGetInt64(t *testing.T) {
|
||||
assert.Equal(t, int64(42424242424242), c.GetInt64("int64"))
|
||||
}
|
||||
|
||||
func TestContextGetUint(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Set("uint", uint(1))
|
||||
assert.Equal(t, uint(1), c.GetUint("uint"))
|
||||
}
|
||||
|
||||
func TestContextGetUint64(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Set("uint64", uint64(18446744073709551615))
|
||||
assert.Equal(t, uint64(18446744073709551615), c.GetUint64("uint64"))
|
||||
}
|
||||
|
||||
func TestContextGetFloat64(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Set("float64", 4.2)
|
||||
@ -1270,7 +1282,7 @@ func TestContextIsAborted(t *testing.T) {
|
||||
assert.True(t, c.IsAborted())
|
||||
}
|
||||
|
||||
// TestContextData tests that the response can be written from `bytesting`
|
||||
// TestContextData tests that the response can be written from `bytestring`
|
||||
// with specified MIME type
|
||||
func TestContextAbortWithStatus(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
|
@ -90,6 +90,11 @@ func (msg *Error) IsType(flags ErrorType) bool {
|
||||
return (msg.Type & flags) > 0
|
||||
}
|
||||
|
||||
// Unwrap returns the wrapped error, to allow interoperability with errors.Is(), errors.As() and errors.Unwrap()
|
||||
func (msg *Error) Unwrap() error {
|
||||
return msg.Err
|
||||
}
|
||||
|
||||
// ByType returns a readonly copy filtered the byte.
|
||||
// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic.
|
||||
func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
|
||||
|
33
errors_1.13_test.go
Normal file
33
errors_1.13_test.go
Normal file
@ -0,0 +1,33 @@
|
||||
// +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))
|
||||
}
|
2
go.mod
2
go.mod
@ -4,7 +4,7 @@ go 1.13
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/sse v0.1.0
|
||||
github.com/go-playground/validator/v10 v10.2.0
|
||||
github.com/go-playground/validator/v10 v10.4.1
|
||||
github.com/golang/protobuf v1.3.3
|
||||
github.com/json-iterator/go v1.1.9
|
||||
github.com/mattn/go-isatty v0.0.12
|
||||
|
11
go.sum
11
go.sum
@ -9,8 +9,8 @@ github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8c
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
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/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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@ -34,8 +34,15 @@ github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
@ -76,11 +76,12 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc {
|
||||
headers[idx] = current[0] + ": *"
|
||||
}
|
||||
}
|
||||
headersToStr := strings.Join(headers, "\r\n")
|
||||
if brokenPipe {
|
||||
logger.Printf("%s\n%s%s", err, string(httpRequest), reset)
|
||||
logger.Printf("%s\n%s%s", err, headersToStr, reset)
|
||||
} else if IsDebugging() {
|
||||
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s",
|
||||
timeFormat(time.Now()), strings.Join(headers, "\r\n"), err, stack, reset)
|
||||
timeFormat(time.Now()), headersToStr, err, stack, reset)
|
||||
} else {
|
||||
logger.Printf("[Recovery] %s panic recovered:\n%s\n%s%s",
|
||||
timeFormat(time.Now()), err, stack, reset)
|
||||
|
@ -7,6 +7,8 @@ package render
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin/internal/bytesconv"
|
||||
)
|
||||
|
||||
// String contains the given interface object slice and its format.
|
||||
@ -34,6 +36,6 @@ func WriteString(w http.ResponseWriter, format string, data []interface{}) (err
|
||||
_, err = fmt.Fprintf(w, format, data...)
|
||||
return
|
||||
}
|
||||
_, err = w.Write([]byte(format))
|
||||
_, err = w.Write(bytesconv.StringToBytes(format))
|
||||
return
|
||||
}
|
||||
|
5
utils.go
5
utils.go
@ -103,7 +103,10 @@ func parseAccept(acceptHeader string) []string {
|
||||
parts := strings.Split(acceptHeader, ",")
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
if part = strings.TrimSpace(strings.Split(part, ";")[0]); part != "" {
|
||||
if i := strings.IndexByte(part, ';'); i > 0 {
|
||||
part = part[:i]
|
||||
}
|
||||
if part = strings.TrimSpace(part); part != "" {
|
||||
out = append(out, part)
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,12 @@ func init() {
|
||||
SetMode(TestMode)
|
||||
}
|
||||
|
||||
func BenchmarkParseAccept(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
parseAccept("text/html , application/xhtml+xml,application/xml;q=0.9, */* ;q=0.8")
|
||||
}
|
||||
}
|
||||
|
||||
type testStruct struct {
|
||||
T *testing.T
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user