mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-24 02:32:17 +08:00
Merge branch 'master' into mew-catch-all-v2
This commit is contained in:
commit
e2e0717646
@ -4,6 +4,7 @@ go:
|
|||||||
- 1.6.x
|
- 1.6.x
|
||||||
- 1.7.x
|
- 1.7.x
|
||||||
- 1.8.x
|
- 1.8.x
|
||||||
|
- 1.9.x
|
||||||
- master
|
- master
|
||||||
|
|
||||||
git:
|
git:
|
||||||
|
39
README.md
39
README.md
@ -369,6 +369,7 @@ r := gin.New()
|
|||||||
instead of
|
instead of
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
// Default With the Logger and Recovery middleware already attached
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -380,7 +381,11 @@ func main() {
|
|||||||
r := gin.New()
|
r := gin.New()
|
||||||
|
|
||||||
// Global middleware
|
// Global middleware
|
||||||
|
// Logger middleware will write the logs to gin.DefaultWriter even you set with GIN_MODE=release.
|
||||||
|
// By default gin.DefaultWriter = os.Stdout
|
||||||
r.Use(gin.Logger())
|
r.Use(gin.Logger())
|
||||||
|
|
||||||
|
// Recovery middleware recovers from any panics and writes a 500 if there was one.
|
||||||
r.Use(gin.Recovery())
|
r.Use(gin.Recovery())
|
||||||
|
|
||||||
// Per route middleware, you can add as many as you desire.
|
// Per route middleware, you can add as many as you desire.
|
||||||
@ -408,6 +413,28 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### How to write log file
|
||||||
|
```go
|
||||||
|
func main() {
|
||||||
|
// Disable Console Color, you don't need console color when writing the logs to file.
|
||||||
|
gin.DisableConsoleColor()
|
||||||
|
|
||||||
|
// Logging to a file.
|
||||||
|
f, _ := os.Create("gin.log")
|
||||||
|
gin.DefaultWriter = io.MultiWriter(f)
|
||||||
|
|
||||||
|
// Use the following code if you need to write the logs to file and console at the same time.
|
||||||
|
// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
|
||||||
|
|
||||||
|
router := gin.Default()
|
||||||
|
router.GET("/ping", func(c *gin.Context) {
|
||||||
|
c.String(200, "pong")
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Run(":8080")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Model binding and validation
|
### Model binding and validation
|
||||||
|
|
||||||
To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz).
|
To bind a request body into a type, use model binding. We currently support binding of JSON, XML and standard form values (foo=bar&boo=baz).
|
||||||
@ -505,10 +532,12 @@ package main
|
|||||||
|
|
||||||
import "log"
|
import "log"
|
||||||
import "github.com/gin-gonic/gin"
|
import "github.com/gin-gonic/gin"
|
||||||
|
import "time"
|
||||||
|
|
||||||
type Person struct {
|
type Person struct {
|
||||||
Name string `form:"name"`
|
Name string `form:"name"`
|
||||||
Address string `form:"address"`
|
Address string `form:"address"`
|
||||||
|
Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -525,12 +554,18 @@ func startPage(c *gin.Context) {
|
|||||||
if c.Bind(&person) == nil {
|
if c.Bind(&person) == nil {
|
||||||
log.Println(person.Name)
|
log.Println(person.Name)
|
||||||
log.Println(person.Address)
|
log.Println(person.Address)
|
||||||
|
log.Println(person.Birthday)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.String(200, "Success")
|
c.String(200, "Success")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Test it with:
|
||||||
|
```sh
|
||||||
|
$ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15"
|
||||||
|
```
|
||||||
|
|
||||||
### Bind HTML checkboxes
|
### Bind HTML checkboxes
|
||||||
|
|
||||||
See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092)
|
See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092)
|
||||||
|
8
auth.go
8
auth.go
@ -10,10 +10,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthUserKey is the cookie name for user credential in basic auth
|
// AuthUserKey is the cookie name for user credential in basic auth.
|
||||||
const AuthUserKey = "user"
|
const AuthUserKey = "user"
|
||||||
|
|
||||||
// Accounts defines a key/value for user/pass list of authorized logins
|
// Accounts defines a key/value for user/pass list of authorized logins.
|
||||||
type Accounts map[string]string
|
type Accounts map[string]string
|
||||||
|
|
||||||
type authPair struct {
|
type authPair struct {
|
||||||
@ -56,7 +56,7 @@ func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using
|
// The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using
|
||||||
// c.MustGet(gin.AuthUserKey)
|
// c.MustGet(gin.AuthUserKey).
|
||||||
c.Set(AuthUserKey, user)
|
c.Set(AuthUserKey, user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,6 +90,6 @@ func secureCompare(given, actual string) bool {
|
|||||||
if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
|
if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
|
||||||
return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
|
return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
|
||||||
}
|
}
|
||||||
// Securely compare actual to itself to keep constant time, but always return false
|
// Securely compare actual to itself to keep constant time, but always return false.
|
||||||
return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
|
return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
|
||||||
}
|
}
|
||||||
|
34
context.go
34
context.go
@ -22,7 +22,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin/render"
|
"github.com/gin-gonic/gin/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Content-Type MIME of the most common data formats
|
// Content-Type MIME of the most common data formats.
|
||||||
const (
|
const (
|
||||||
MIMEJSON = binding.MIMEJSON
|
MIMEJSON = binding.MIMEJSON
|
||||||
MIMEHTML = binding.MIMEHTML
|
MIMEHTML = binding.MIMEHTML
|
||||||
@ -51,13 +51,13 @@ type Context struct {
|
|||||||
|
|
||||||
engine *Engine
|
engine *Engine
|
||||||
|
|
||||||
// Keys is a key/value pair exclusively for the context of each request
|
// Keys is a key/value pair exclusively for the context of each request.
|
||||||
Keys map[string]interface{}
|
Keys map[string]interface{}
|
||||||
|
|
||||||
// Errors is a list of errors attached to all the handlers/middlewares who used this context
|
// Errors is a list of errors attached to all the handlers/middlewares who used this context.
|
||||||
Errors errorMsgs
|
Errors errorMsgs
|
||||||
|
|
||||||
// Accepted defines a list of manually accepted formats for content negotiation
|
// Accepted defines a list of manually accepted formats for content negotiation.
|
||||||
Accepted []string
|
Accepted []string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ func (c *Context) Copy() *Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()",
|
// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()",
|
||||||
// this function will return "main.handleGetUsers"
|
// this function will return "main.handleGetUsers".
|
||||||
func (c *Context) HandlerName() string {
|
func (c *Context) HandlerName() string {
|
||||||
return nameOfFunction(c.handlers.Last())
|
return nameOfFunction(c.handlers.Last())
|
||||||
}
|
}
|
||||||
@ -462,18 +462,18 @@ func (c *Context) Bind(obj interface{}) error {
|
|||||||
return c.MustBindWith(obj, b)
|
return c.MustBindWith(obj, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON)
|
// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON).
|
||||||
func (c *Context) BindJSON(obj interface{}) error {
|
func (c *Context) BindJSON(obj interface{}) error {
|
||||||
return c.MustBindWith(obj, binding.JSON)
|
return c.MustBindWith(obj, binding.JSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query)
|
// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query).
|
||||||
func (c *Context) BindQuery(obj interface{}) error {
|
func (c *Context) BindQuery(obj interface{}) error {
|
||||||
return c.MustBindWith(obj, binding.Query)
|
return c.MustBindWith(obj, binding.Query)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustBindWith binds the passed struct pointer using the specified binding
|
// MustBindWith binds the passed struct pointer using the specified binding engine.
|
||||||
// engine. It will abort the request with HTTP 400 if any error ocurrs.
|
// It will abort the request with HTTP 400 if any error ocurrs.
|
||||||
// See the binding package.
|
// See the binding package.
|
||||||
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
|
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
|
||||||
if err = c.ShouldBindWith(obj, b); err != nil {
|
if err = c.ShouldBindWith(obj, b); err != nil {
|
||||||
@ -483,8 +483,7 @@ func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBindWith binds the passed struct pointer using the specified binding
|
// ShouldBindWith binds the passed struct pointer using the specified binding engine.
|
||||||
// engine.
|
|
||||||
// See the binding package.
|
// See the binding package.
|
||||||
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
|
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
|
||||||
return b.Bind(c.Request, obj)
|
return b.Bind(c.Request, obj)
|
||||||
@ -538,17 +537,14 @@ func (c *Context) IsWebsocket() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) requestHeader(key string) string {
|
func (c *Context) requestHeader(key string) string {
|
||||||
if values, _ := c.Request.Header[key]; len(values) > 0 {
|
return c.Request.Header.Get(key)
|
||||||
return values[0]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************/
|
/************************************/
|
||||||
/******** RESPONSE RENDERING ********/
|
/******** RESPONSE RENDERING ********/
|
||||||
/************************************/
|
/************************************/
|
||||||
|
|
||||||
// bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function
|
// bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function.
|
||||||
func bodyAllowedForStatus(status int) bool {
|
func bodyAllowedForStatus(status int) bool {
|
||||||
switch {
|
switch {
|
||||||
case status >= 100 && status <= 199:
|
case status >= 100 && status <= 199:
|
||||||
@ -566,7 +562,7 @@ func (c *Context) Status(code int) {
|
|||||||
c.writermem.WriteHeader(code)
|
c.writermem.WriteHeader(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header is a intelligent shortcut for c.Writer.Header().Set(key, value)
|
// Header is a intelligent shortcut for c.Writer.Header().Set(key, value).
|
||||||
// It writes a header in the response.
|
// It writes a header in the response.
|
||||||
// If value == "", this method removes the header `c.Writer.Header().Del(key)`
|
// If value == "", this method removes the header `c.Writer.Header().Del(key)`
|
||||||
func (c *Context) Header(key, value string) {
|
func (c *Context) Header(key, value string) {
|
||||||
@ -577,12 +573,12 @@ func (c *Context) Header(key, value string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHeader returns value from request headers
|
// GetHeader returns value from request headers.
|
||||||
func (c *Context) GetHeader(key string) string {
|
func (c *Context) GetHeader(key string) string {
|
||||||
return c.requestHeader(key)
|
return c.requestHeader(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRawData return stream data
|
// GetRawData return stream data.
|
||||||
func (c *Context) GetRawData() ([]byte, error) {
|
func (c *Context) GetRawData() ([]byte, error) {
|
||||||
return ioutil.ReadAll(c.Request.Body)
|
return ioutil.ReadAll(c.Request.Body)
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ func (msg *Error) JSON() interface{} {
|
|||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaller interface
|
// MarshalJSON implements the json.Marshaller interface.
|
||||||
func (msg *Error) MarshalJSON() ([]byte, error) {
|
func (msg *Error) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(msg.JSON())
|
return json.Marshal(msg.JSON())
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ func (msg *Error) IsType(flags ErrorType) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ByType returns a readonly copy filtered the byte.
|
// ByType returns a readonly copy filtered the byte.
|
||||||
// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic
|
// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic.
|
||||||
func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
|
func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
|
||||||
if len(a) == 0 {
|
if len(a) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@ -98,7 +98,7 @@ func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Last returns the last error in the slice. It returns nil if the array is empty.
|
// Last returns the last error in the slice. It returns nil if the array is empty.
|
||||||
// Shortcut for errors[len(errors)-1]
|
// Shortcut for errors[len(errors)-1].
|
||||||
func (a errorMsgs) Last() *Error {
|
func (a errorMsgs) Last() *Error {
|
||||||
if length := len(a); length > 0 {
|
if length := len(a); length > 0 {
|
||||||
return a[length-1]
|
return a[length-1]
|
||||||
|
@ -13,10 +13,10 @@ func init() {
|
|||||||
|
|
||||||
// Define your handlers
|
// Define your handlers
|
||||||
r.GET("/", func(c *gin.Context) {
|
r.GET("/", func(c *gin.Context) {
|
||||||
c.String(200, "Hello World!")
|
c.String(http.StatusOK, "Hello World!")
|
||||||
})
|
})
|
||||||
r.GET("/ping", func(c *gin.Context) {
|
r.GET("/ping", func(c *gin.Context) {
|
||||||
c.String(200, "pong")
|
c.String(http.StatusOK, "pong")
|
||||||
})
|
})
|
||||||
|
|
||||||
// Handle all requests using net/http
|
// Handle all requests using net/http
|
||||||
|
4
fs.go
4
fs.go
@ -29,7 +29,7 @@ func Dir(root string, listDirectory bool) http.FileSystem {
|
|||||||
return &onlyfilesFS{fs}
|
return &onlyfilesFS{fs}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open conforms to http.Filesystem
|
// Open conforms to http.Filesystem.
|
||||||
func (fs onlyfilesFS) Open(name string) (http.File, error) {
|
func (fs onlyfilesFS) Open(name string) (http.File, error) {
|
||||||
f, err := fs.fs.Open(name)
|
f, err := fs.fs.Open(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -38,7 +38,7 @@ func (fs onlyfilesFS) Open(name string) (http.File, error) {
|
|||||||
return neuteredReaddirFile{f}, nil
|
return neuteredReaddirFile{f}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Readdir overrides the http.File default implementation
|
// Readdir overrides the http.File default implementation.
|
||||||
func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
|
func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
|
||||||
// this disables directory listing
|
// this disables directory listing
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
13
gin.go
13
gin.go
@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin/render"
|
"github.com/gin-gonic/gin/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is Framework's version
|
// Version is Framework's version.
|
||||||
const Version = "v1.2"
|
const Version = "v1.2"
|
||||||
|
|
||||||
var default404Body = []byte("404 page not found")
|
var default404Body = []byte("404 page not found")
|
||||||
@ -191,7 +191,7 @@ func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
|
|||||||
engine.rebuild404Handlers()
|
engine.rebuild404Handlers()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoMethod sets the handlers called when... TODO
|
// NoMethod sets the handlers called when... TODO.
|
||||||
func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
|
func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
|
||||||
engine.noMethod = handlers
|
engine.noMethod = handlers
|
||||||
engine.rebuild405Handlers()
|
engine.rebuild405Handlers()
|
||||||
@ -268,7 +268,7 @@ func (engine *Engine) Run(addr ...string) (err error) {
|
|||||||
// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
|
// RunTLS attaches the router to a http.Server and starts listening and serving HTTPS (secure) requests.
|
||||||
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
|
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
|
||||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||||
func (engine *Engine) RunTLS(addr string, certFile string, keyFile string) (err error) {
|
func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {
|
||||||
debugPrint("Listening and serving HTTPS on %s\n", addr)
|
debugPrint("Listening and serving HTTPS on %s\n", addr)
|
||||||
defer func() { debugPrintError(err) }()
|
defer func() { debugPrintError(err) }()
|
||||||
|
|
||||||
@ -316,14 +316,11 @@ func (engine *Engine) HandleContext(c *Context) {
|
|||||||
|
|
||||||
func (engine *Engine) handleHTTPRequest(context *Context) {
|
func (engine *Engine) handleHTTPRequest(context *Context) {
|
||||||
httpMethod := context.Request.Method
|
httpMethod := context.Request.Method
|
||||||
var path string
|
path := context.Request.URL.Path
|
||||||
var unescape bool
|
unescape := false
|
||||||
if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 {
|
if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 {
|
||||||
path = context.Request.URL.RawPath
|
path = context.Request.URL.RawPath
|
||||||
unescape = engine.UnescapePathValues
|
unescape = engine.UnescapePathValues
|
||||||
} else {
|
|
||||||
path = context.Request.URL.Path
|
|
||||||
unescape = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find root of the tree for the given HTTP method
|
// Find root of the tree for the given HTTP method
|
||||||
|
@ -9,15 +9,15 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
. "github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
var internalEngine *Engine
|
var internalEngine *gin.Engine
|
||||||
|
|
||||||
func engine() *Engine {
|
func engine() *gin.Engine {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
internalEngine = Default()
|
internalEngine = gin.Default()
|
||||||
})
|
})
|
||||||
return internalEngine
|
return internalEngine
|
||||||
}
|
}
|
||||||
|
19
logger.go
19
logger.go
@ -25,17 +25,17 @@ var (
|
|||||||
disableColor = false
|
disableColor = false
|
||||||
)
|
)
|
||||||
|
|
||||||
// DisableConsoleColor disables color output in the console
|
// DisableConsoleColor disables color output in the console.
|
||||||
func DisableConsoleColor() {
|
func DisableConsoleColor() {
|
||||||
disableColor = true
|
disableColor = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorLogger returns a handlerfunc for any error type
|
// ErrorLogger returns a handlerfunc for any error type.
|
||||||
func ErrorLogger() HandlerFunc {
|
func ErrorLogger() HandlerFunc {
|
||||||
return ErrorLoggerT(ErrorTypeAny)
|
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 {
|
func ErrorLoggerT(typ ErrorType) HandlerFunc {
|
||||||
return func(c *Context) {
|
return func(c *Context) {
|
||||||
c.Next()
|
c.Next()
|
||||||
@ -46,8 +46,8 @@ func ErrorLoggerT(typ ErrorType) HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logger instances a Logger middleware that will write the logs to gin.DefaultWriter
|
// Logger instances a Logger middleware that will write the logs to gin.DefaultWriter.
|
||||||
// By default gin.DefaultWriter = os.Stdout
|
// By default gin.DefaultWriter = os.Stdout.
|
||||||
func Logger() HandlerFunc {
|
func Logger() HandlerFunc {
|
||||||
return LoggerWithWriter(DefaultWriter)
|
return LoggerWithWriter(DefaultWriter)
|
||||||
}
|
}
|
||||||
@ -91,10 +91,11 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
|||||||
clientIP := c.ClientIP()
|
clientIP := c.ClientIP()
|
||||||
method := c.Request.Method
|
method := c.Request.Method
|
||||||
statusCode := c.Writer.Status()
|
statusCode := c.Writer.Status()
|
||||||
var statusColor, methodColor string
|
var statusColor, methodColor, resetColor string
|
||||||
if isTerm {
|
if isTerm {
|
||||||
statusColor = colorForStatus(statusCode)
|
statusColor = colorForStatus(statusCode)
|
||||||
methodColor = colorForMethod(method)
|
methodColor = colorForMethod(method)
|
||||||
|
resetColor = reset
|
||||||
}
|
}
|
||||||
comment := c.Errors.ByType(ErrorTypePrivate).String()
|
comment := c.Errors.ByType(ErrorTypePrivate).String()
|
||||||
|
|
||||||
@ -102,12 +103,12 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
|||||||
path = path + "?" + raw
|
path = path + "?" + raw
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %s %-7s %s\n%s",
|
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s",
|
||||||
end.Format("2006/01/02 - 15:04:05"),
|
end.Format("2006/01/02 - 15:04:05"),
|
||||||
statusColor, statusCode, reset,
|
statusColor, statusCode, resetColor,
|
||||||
latency,
|
latency,
|
||||||
clientIP,
|
clientIP,
|
||||||
methodColor, method, reset,
|
methodColor, method, resetColor,
|
||||||
path,
|
path,
|
||||||
comment,
|
comment,
|
||||||
)
|
)
|
||||||
|
4
path.go
4
path.go
@ -17,7 +17,7 @@ package gin
|
|||||||
// 4. Eliminate .. elements that begin a rooted path:
|
// 4. Eliminate .. elements that begin a rooted path:
|
||||||
// that is, replace "/.." by "/" at the beginning of a path.
|
// that is, replace "/.." by "/" at the beginning of a path.
|
||||||
//
|
//
|
||||||
// If the result of this process is an empty string, "/" is returned
|
// If the result of this process is an empty string, "/" is returned.
|
||||||
func cleanPath(p string) string {
|
func cleanPath(p string) string {
|
||||||
// Turn empty string into "/"
|
// Turn empty string into "/"
|
||||||
if p == "" {
|
if p == "" {
|
||||||
@ -109,7 +109,7 @@ func cleanPath(p string) string {
|
|||||||
return string(buf[:w])
|
return string(buf[:w])
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal helper to lazily create a buffer if necessary
|
// internal helper to lazily create a buffer if necessary.
|
||||||
func bufApp(buf *[]byte, s string, w int, c byte) {
|
func bufApp(buf *[]byte, s string, w int, c byte) {
|
||||||
if *buf == nil {
|
if *buf == nil {
|
||||||
if s[w] == c {
|
if s[w] == c {
|
||||||
|
@ -46,7 +46,7 @@ func RecoveryWithWriter(out io.Writer) HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stack returns a nicely formated stack frame, skipping skip frames
|
// stack returns a nicely formated stack frame, skipping skip frames.
|
||||||
func stack(skip int) []byte {
|
func stack(skip int) []byte {
|
||||||
buf := new(bytes.Buffer) // the returned data
|
buf := new(bytes.Buffer) // the returned data
|
||||||
// As we loop, we open files and read them. These variables record the currently
|
// As we loop, we open files and read them. These variables record the currently
|
||||||
|
@ -11,7 +11,7 @@ type Data struct {
|
|||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render (Data) writes data with custom ContentType
|
// Render (Data) writes data with custom ContentType.
|
||||||
func (r Data) Render(w http.ResponseWriter) (err error) {
|
func (r Data) Render(w http.ResponseWriter) (err error) {
|
||||||
r.WriteContentType(w)
|
r.WriteContentType(w)
|
||||||
_, err = w.Write(r.Data)
|
_, err = w.Write(r.Data)
|
||||||
|
@ -95,7 +95,7 @@ func (w *responseWriter) Written() bool {
|
|||||||
return w.size != noWritten
|
return w.size != noWritten
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hijack implements the http.Hijacker interface
|
// Hijack implements the http.Hijacker interface.
|
||||||
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
if w.size < 0 {
|
if w.size < 0 {
|
||||||
w.size = 0
|
w.size = 0
|
||||||
@ -103,12 +103,12 @@ func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
|||||||
return w.ResponseWriter.(http.Hijacker).Hijack()
|
return w.ResponseWriter.(http.Hijacker).Hijack()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseNotify implements the http.CloseNotify interface
|
// CloseNotify implements the http.CloseNotify interface.
|
||||||
func (w *responseWriter) CloseNotify() <-chan bool {
|
func (w *responseWriter) CloseNotify() <-chan bool {
|
||||||
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush implements the http.Flush interface
|
// Flush implements the http.Flush interface.
|
||||||
func (w *responseWriter) Flush() {
|
func (w *responseWriter) Flush() {
|
||||||
w.ResponseWriter.(http.Flusher).Flush()
|
w.ResponseWriter.(http.Flusher).Flush()
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ type IRoutes interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix
|
// RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix
|
||||||
// and an array of handlers (middleware)
|
// and an array of handlers (middleware).
|
||||||
type RouterGroup struct {
|
type RouterGroup struct {
|
||||||
Handlers HandlersChain
|
Handlers HandlersChain
|
||||||
basePath string
|
basePath string
|
||||||
@ -89,43 +89,43 @@ func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...Ha
|
|||||||
return group.handle(httpMethod, relativePath, handlers)
|
return group.handle(httpMethod, relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST is a shortcut for router.Handle("POST", path, handle)
|
// POST is a shortcut for router.Handle("POST", path, handle).
|
||||||
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
|
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("POST", relativePath, handlers)
|
return group.handle("POST", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET is a shortcut for router.Handle("GET", path, handle)
|
// GET is a shortcut for router.Handle("GET", path, handle).
|
||||||
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
|
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("GET", relativePath, handlers)
|
return group.handle("GET", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
|
// DELETE is a shortcut for router.Handle("DELETE", path, handle).
|
||||||
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
|
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("DELETE", relativePath, handlers)
|
return group.handle("DELETE", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PATCH is a shortcut for router.Handle("PATCH", path, handle)
|
// PATCH is a shortcut for router.Handle("PATCH", path, handle).
|
||||||
func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes {
|
func (group *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("PATCH", relativePath, handlers)
|
return group.handle("PATCH", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PUT is a shortcut for router.Handle("PUT", path, handle)
|
// PUT is a shortcut for router.Handle("PUT", path, handle).
|
||||||
func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes {
|
func (group *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("PUT", relativePath, handlers)
|
return group.handle("PUT", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
|
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle).
|
||||||
func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes {
|
func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("OPTIONS", relativePath, handlers)
|
return group.handle("OPTIONS", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
|
// HEAD is a shortcut for router.Handle("HEAD", path, handle).
|
||||||
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
|
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle("HEAD", relativePath, handlers)
|
return group.handle("HEAD", relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any registers a route that matches all the HTTP methods.
|
// Any registers a route that matches all the HTTP methods.
|
||||||
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE
|
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
|
||||||
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
|
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
group.handle("GET", relativePath, handlers)
|
group.handle("GET", relativePath, handlers)
|
||||||
group.handle("POST", relativePath, handlers)
|
group.handle("POST", relativePath, handlers)
|
||||||
|
@ -375,25 +375,25 @@ func TestRouterNotFound(t *testing.T) {
|
|||||||
router.GET("/", func(c *Context) {})
|
router.GET("/", func(c *Context) {})
|
||||||
|
|
||||||
testRoutes := []struct {
|
testRoutes := []struct {
|
||||||
route string
|
route string
|
||||||
code int
|
code int
|
||||||
header string
|
location string
|
||||||
}{
|
}{
|
||||||
{"/path/", 301, "map[Location:[/path]]"}, // TSR -/
|
{"/path/", 301, "/path"}, // TSR -/
|
||||||
{"/dir", 301, "map[Location:[/dir/]]"}, // TSR +/
|
{"/dir", 301, "/dir/"}, // TSR +/
|
||||||
{"", 301, "map[Location:[/]]"}, // TSR +/
|
{"", 301, "/"}, // TSR +/
|
||||||
{"/PATH", 301, "map[Location:[/path]]"}, // Fixed Case
|
{"/PATH", 301, "/path"}, // Fixed Case
|
||||||
{"/DIR/", 301, "map[Location:[/dir/]]"}, // Fixed Case
|
{"/DIR/", 301, "/dir/"}, // Fixed Case
|
||||||
{"/PATH/", 301, "map[Location:[/path]]"}, // Fixed Case -/
|
{"/PATH/", 301, "/path"}, // Fixed Case -/
|
||||||
{"/DIR", 301, "map[Location:[/dir/]]"}, // Fixed Case +/
|
{"/DIR", 301, "/dir/"}, // Fixed Case +/
|
||||||
{"/../path", 301, "map[Location:[/path]]"}, // CleanPath
|
{"/../path", 301, "/path"}, // CleanPath
|
||||||
{"/nope", 404, ""}, // NotFound
|
{"/nope", 404, ""}, // NotFound
|
||||||
}
|
}
|
||||||
for _, tr := range testRoutes {
|
for _, tr := range testRoutes {
|
||||||
w := performRequest(router, "GET", tr.route)
|
w := performRequest(router, "GET", tr.route)
|
||||||
assert.Equal(t, w.Code, tr.code)
|
assert.Equal(t, w.Code, tr.code)
|
||||||
if w.Code != 404 {
|
if w.Code != 404 {
|
||||||
assert.Equal(t, fmt.Sprint(w.Header()), tr.header)
|
assert.Equal(t, fmt.Sprint(w.Header().Get("Location")), tr.location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
tree.go
2
tree.go
@ -96,7 +96,7 @@ type node struct {
|
|||||||
priority uint32
|
priority uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// increments priority of the given child and reorders if necessary
|
// increments priority of the given child and reorders if necessary.
|
||||||
func (n *node) incrementChildPrio(pos int) int {
|
func (n *node) incrementChildPrio(pos int) int {
|
||||||
n.children[pos].priority++
|
n.children[pos].priority++
|
||||||
prio := n.children[pos].priority
|
prio := n.children[pos].priority
|
||||||
|
2
utils.go
2
utils.go
@ -47,7 +47,7 @@ func WrapH(h http.Handler) HandlerFunc {
|
|||||||
|
|
||||||
type H map[string]interface{}
|
type H map[string]interface{}
|
||||||
|
|
||||||
// MarshalXML allows type H to be used with xml.Marshal
|
// MarshalXML allows type H to be used with xml.Marshal.
|
||||||
func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
||||||
start.Name = xml.Name{
|
start.Name = xml.Name{
|
||||||
Space: "",
|
Space: "",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user