diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index de067ce0..9d49aa41 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -3,8 +3,8 @@
- Please provide source code and commit sha if you found a bug.
- Review existing issues and provide feedback or react to them.
+- go version:
- gin version (or commit ref):
-- git version:
- operating system:
## Description
diff --git a/.travis.yml b/.travis.yml
index a93458f9..398c1409 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,6 +7,11 @@ go:
- 1.9.x
- 1.10.x
- 1.11.x
+ - master
+
+matrix:
+ allow_failures:
+ - go: master
git:
depth: 10
diff --git a/README.md b/README.md
index d852f97d..6fd2dd34 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
- [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct)
- [Try to bind body into different structs](#try-to-bind-body-into-different-structs)
- [http2 server push](#http2-server-push)
+ - [Define format for the log of routes](#define-format-for-the-log-of-routes)
- [Testing](#testing)
- [Users](#users)
@@ -658,6 +659,7 @@ import (
"gopkg.in/go-playground/validator.v8"
)
+// Booking contains binded and validated data.
type Booking struct {
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
@@ -1836,6 +1838,49 @@ func main() {
}
```
+### Define format for the log of routes
+
+The default log of routes is:
+```
+[GIN-debug] POST /foo --> main.main.func1 (3 handlers)
+[GIN-debug] GET /bar --> main.main.func2 (3 handlers)
+[GIN-debug] GET /status --> main.main.func3 (3 handlers)
+```
+
+If you want to log this information in given format (e.g. JSON, key values or something else), then you can define this format with `gin.DebugPrintRouteFunc`.
+In the example below, we log all routes with standard log package but you can use another log tools that suits of your needs.
+```go
+import (
+ "log"
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+)
+
+func main() {
+ r := gin.Default()
+ gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
+ log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)
+ }
+
+ r.POST("/foo", func(c *gin.Context) {
+ c.JSON(http.StatusOK, "foo")
+ })
+
+ r.GET("/bar", func(c *gin.Context) {
+ c.JSON(http.StatusOK, "bar")
+ })
+
+ r.GET("/status", func(c *gin.Context) {
+ c.JSON(http.StatusOK, "ok")
+ })
+
+ // Listen and Server in http://0.0.0.0:8080
+ r.Run()
+}
+```
+
+
## Testing
The `net/http/httptest` package is preferable way for HTTP testing.
@@ -1886,5 +1931,6 @@ func TestPingRoute(t *testing.T) {
Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework.
-* [drone](https://github.com/drone/drone): Drone is a Continuous Delivery platform built on Docker, written in Go
+* [drone](https://github.com/drone/drone): Drone is a Continuous Delivery platform built on Docker, written in Go.
* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go.
+* [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform.
diff --git a/context.go b/context.go
index 063c72f0..809902a3 100644
--- a/context.go
+++ b/context.go
@@ -711,6 +711,7 @@ func (c *Context) Cookie(name string) (string, error) {
return val, nil
}
+// Render writes the response headers and calls render.Render to render data.
func (c *Context) Render(code int, r render.Render) {
c.Status(code)
@@ -833,6 +834,7 @@ func (c *Context) SSEvent(name string, message interface{}) {
})
}
+// Stream sends a streaming response.
func (c *Context) Stream(step func(w io.Writer) bool) {
w := c.Writer
clientGone := w.CloseNotify()
@@ -854,6 +856,7 @@ func (c *Context) Stream(step func(w io.Writer) bool) {
/******** CONTENT NEGOTIATION *******/
/************************************/
+// Negotiate contains all negotiations data.
type Negotiate struct {
Offered []string
HTMLName string
@@ -863,6 +866,7 @@ type Negotiate struct {
Data interface{}
}
+// Negotiate calls different Render according acceptable Accept format.
func (c *Context) Negotiate(code int, config Negotiate) {
switch c.NegotiateFormat(config.Offered...) {
case binding.MIMEJSON:
@@ -882,6 +886,7 @@ func (c *Context) Negotiate(code int, config Negotiate) {
}
}
+// NegotiateFormat returns an acceptable Accept format.
func (c *Context) NegotiateFormat(offered ...string) string {
assert1(len(offered) > 0, "you must provide at least one offer")
@@ -901,6 +906,7 @@ func (c *Context) NegotiateFormat(offered ...string) string {
return ""
}
+// SetAccepted sets Accept header data.
func (c *Context) SetAccepted(formats ...string) {
c.Accepted = formats
}
diff --git a/context_test.go b/context_test.go
index 782f7bed..5a5bb6e1 100644
--- a/context_test.go
+++ b/context_test.go
@@ -784,14 +784,14 @@ func TestContextRenderHTML2(t *testing.T) {
router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})
assert.Len(t, router.trees, 1)
- var b bytes.Buffer
- setup(&b)
- defer teardown()
-
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
- router.SetHTMLTemplate(templ)
+ re := captureOutput(func() {
+ SetMode(DebugMode)
+ router.SetHTMLTemplate(templ)
+ SetMode(TestMode)
+ })
- assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", b.String())
+ assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", re)
c.HTML(http.StatusCreated, "t", H{"name": "alexandernyquist"})
@@ -978,7 +978,7 @@ func TestContextRenderProtoBuf(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, http.StatusCreated, w.Code)
- assert.Equal(t, string(protoData[:]), w.Body.String())
+ assert.Equal(t, string(protoData), w.Body.String())
assert.Equal(t, "application/x-protobuf", w.HeaderMap.Get("Content-Type"))
}
diff --git a/debug.go b/debug.go
index f11156b3..ce908dc7 100644
--- a/debug.go
+++ b/debug.go
@@ -6,25 +6,28 @@ package gin
import (
"bytes"
+ "fmt"
"html/template"
- "log"
)
-func init() {
- log.SetFlags(0)
-}
-
// IsDebugging returns true if the framework is running in debug mode.
// Use SetMode(gin.ReleaseMode) to disable debug mode.
func IsDebugging() bool {
return ginMode == debugCode
}
+// DebugPrintRouteFunc indicates debug log output format.
+var DebugPrintRouteFunc func(httpMethod, absolutePath, handlerName string, nuHandlers int)
+
func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) {
if IsDebugging() {
nuHandlers := len(handlers)
handlerName := nameOfFunction(handlers.Last())
- debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
+ if DebugPrintRouteFunc == nil {
+ debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
+ } else {
+ DebugPrintRouteFunc(httpMethod, absolutePath, handlerName, nuHandlers)
+ }
}
}
@@ -42,7 +45,7 @@ func debugPrintLoadTemplate(tmpl *template.Template) {
func debugPrint(format string, values ...interface{}) {
if IsDebugging() {
- log.Printf("[GIN-debug] "+format, values...)
+ fmt.Printf("[GIN-debug] "+format, values...)
}
}
diff --git a/debug_test.go b/debug_test.go
index ed5a6a54..0b9d7910 100644
--- a/debug_test.go
+++ b/debug_test.go
@@ -11,6 +11,7 @@ import (
"io"
"log"
"os"
+ "sync"
"testing"
"github.com/stretchr/testify/assert"
@@ -30,86 +31,101 @@ func TestIsDebugging(t *testing.T) {
}
func TestDebugPrint(t *testing.T) {
- var w bytes.Buffer
- setup(&w)
- defer teardown()
-
- SetMode(ReleaseMode)
- debugPrint("DEBUG this!")
- SetMode(TestMode)
- debugPrint("DEBUG this!")
- assert.Empty(t, w.String())
-
- SetMode(DebugMode)
- debugPrint("these are %d %s\n", 2, "error messages")
- assert.Equal(t, "[GIN-debug] these are 2 error messages\n", w.String())
+ re := captureOutput(func() {
+ SetMode(DebugMode)
+ SetMode(ReleaseMode)
+ debugPrint("DEBUG this!")
+ SetMode(TestMode)
+ debugPrint("DEBUG this!")
+ SetMode(DebugMode)
+ debugPrint("these are %d %s\n", 2, "error messages")
+ SetMode(TestMode)
+ })
+ assert.Equal(t, "[GIN-debug] these are 2 error messages\n", re)
}
func TestDebugPrintError(t *testing.T) {
- var w bytes.Buffer
- setup(&w)
- defer teardown()
-
- SetMode(DebugMode)
- debugPrintError(nil)
- assert.Empty(t, w.String())
-
- debugPrintError(errors.New("this is an error"))
- assert.Equal(t, "[GIN-debug] [ERROR] this is an error\n", w.String())
+ re := captureOutput(func() {
+ SetMode(DebugMode)
+ debugPrintError(nil)
+ debugPrintError(errors.New("this is an error"))
+ SetMode(TestMode)
+ })
+ assert.Equal(t, "[GIN-debug] [ERROR] this is an error\n", re)
}
func TestDebugPrintRoutes(t *testing.T) {
- var w bytes.Buffer
- setup(&w)
- defer teardown()
-
- debugPrintRoute("GET", "/path/to/route/:param", HandlersChain{func(c *Context) {}, handlerNameTest})
- assert.Regexp(t, `^\[GIN-debug\] GET /path/to/route/:param --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, w.String())
+ re := captureOutput(func() {
+ SetMode(DebugMode)
+ debugPrintRoute("GET", "/path/to/route/:param", HandlersChain{func(c *Context) {}, handlerNameTest})
+ SetMode(TestMode)
+ })
+ assert.Regexp(t, `^\[GIN-debug\] GET /path/to/route/:param --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, re)
}
func TestDebugPrintLoadTemplate(t *testing.T) {
- var w bytes.Buffer
- setup(&w)
- defer teardown()
-
- templ := template.Must(template.New("").Delims("{[{", "}]}").ParseGlob("./testdata/template/hello.tmpl"))
- debugPrintLoadTemplate(templ)
- assert.Regexp(t, `^\[GIN-debug\] Loaded HTML Templates \(2\): \n(\t- \n|\t- hello\.tmpl\n){2}\n`, w.String())
+ re := captureOutput(func() {
+ SetMode(DebugMode)
+ templ := template.Must(template.New("").Delims("{[{", "}]}").ParseGlob("./testdata/template/hello.tmpl"))
+ debugPrintLoadTemplate(templ)
+ SetMode(TestMode)
+ })
+ assert.Regexp(t, `^\[GIN-debug\] Loaded HTML Templates \(2\): \n(\t- \n|\t- hello\.tmpl\n){2}\n`, re)
}
func TestDebugPrintWARNINGSetHTMLTemplate(t *testing.T) {
- var w bytes.Buffer
- setup(&w)
- defer teardown()
-
- debugPrintWARNINGSetHTMLTemplate()
- assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", w.String())
+ re := captureOutput(func() {
+ SetMode(DebugMode)
+ debugPrintWARNINGSetHTMLTemplate()
+ SetMode(TestMode)
+ })
+ assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", re)
}
func TestDebugPrintWARNINGDefault(t *testing.T) {
- var w bytes.Buffer
- setup(&w)
- defer teardown()
-
- debugPrintWARNINGDefault()
- assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", w.String())
+ re := captureOutput(func() {
+ SetMode(DebugMode)
+ debugPrintWARNINGDefault()
+ SetMode(TestMode)
+ })
+ assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
}
func TestDebugPrintWARNINGNew(t *testing.T) {
- var w bytes.Buffer
- setup(&w)
- defer teardown()
-
- debugPrintWARNINGNew()
- assert.Equal(t, "[GIN-debug] [WARNING] Running in \"debug\" mode. Switch to \"release\" mode in production.\n - using env:\texport GIN_MODE=release\n - using code:\tgin.SetMode(gin.ReleaseMode)\n\n", w.String())
+ re := captureOutput(func() {
+ SetMode(DebugMode)
+ debugPrintWARNINGNew()
+ SetMode(TestMode)
+ })
+ assert.Equal(t, "[GIN-debug] [WARNING] Running in \"debug\" mode. Switch to \"release\" mode in production.\n - using env:\texport GIN_MODE=release\n - using code:\tgin.SetMode(gin.ReleaseMode)\n\n", re)
}
-func setup(w io.Writer) {
- SetMode(DebugMode)
- log.SetOutput(w)
-}
-
-func teardown() {
- SetMode(TestMode)
- log.SetOutput(os.Stdout)
+func captureOutput(f func()) string {
+ reader, writer, err := os.Pipe()
+ if err != nil {
+ panic(err)
+ }
+ stdout := os.Stdout
+ stderr := os.Stderr
+ defer func() {
+ os.Stdout = stdout
+ os.Stderr = stderr
+ log.SetOutput(os.Stderr)
+ }()
+ os.Stdout = writer
+ os.Stderr = writer
+ log.SetOutput(writer)
+ out := make(chan string)
+ wg := new(sync.WaitGroup)
+ wg.Add(1)
+ go func() {
+ var buf bytes.Buffer
+ wg.Done()
+ io.Copy(&buf, reader)
+ out <- buf.String()
+ }()
+ wg.Wait()
+ f()
+ writer.Close()
+ return <-out
}
diff --git a/errors.go b/errors.go
index 6f90377e..477b9d58 100644
--- a/errors.go
+++ b/errors.go
@@ -12,18 +12,24 @@ import (
"github.com/gin-gonic/gin/internal/json"
)
+// ErrorType is an unsigned 64-bit error code as defined in the gin spec.
type ErrorType uint64
const (
- ErrorTypeBind ErrorType = 1 << 63 // used when c.Bind() fails
- ErrorTypeRender ErrorType = 1 << 62 // used when c.Render() fails
+ // ErrorTypeBind is used when Context.Bind() fails.
+ ErrorTypeBind ErrorType = 1 << 63
+ // ErrorTypeRender is used when Context.Render() fails.
+ ErrorTypeRender ErrorType = 1 << 62
+ // ErrorTypePrivate indicates a private error.
ErrorTypePrivate ErrorType = 1 << 0
- ErrorTypePublic ErrorType = 1 << 1
-
+ // ErrorTypePublic indicates a public error.
+ ErrorTypePublic ErrorType = 1 << 1
+ // ErrorTypeAny indicates other any error.
ErrorTypeAny ErrorType = 1<<64 - 1
ErrorTypeNu = 2
)
+// Error represents a error's specification.
type Error struct {
Err error
Type ErrorType
@@ -34,11 +40,13 @@ type errorMsgs []*Error
var _ error = &Error{}
+// SetType sets the error's type.
func (msg *Error) SetType(flags ErrorType) *Error {
msg.Type = flags
return msg
}
+// SetMeta sets the error's meta data.
func (msg *Error) SetMeta(data interface{}) *Error {
msg.Meta = data
return msg
@@ -70,11 +78,12 @@ func (msg *Error) MarshalJSON() ([]byte, error) {
return json.Marshal(msg.JSON())
}
-// Error implements the error interface
+// Error implements the error interface.
func (msg Error) Error() string {
return msg.Err.Error()
}
+// IsType judges one error.
func (msg *Error) IsType(flags ErrorType) bool {
return (msg.Type & flags) > 0
}
@@ -138,6 +147,7 @@ func (a errorMsgs) JSON() interface{} {
}
}
+// MarshalJSON implements the json.Marshaller interface.
func (a errorMsgs) MarshalJSON() ([]byte, error) {
return json.Marshal(a.JSON())
}
diff --git a/examples/basic/main.go b/examples/basic/main.go
index 48fa7bba..1c9e0ac4 100644
--- a/examples/basic/main.go
+++ b/examples/basic/main.go
@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
)
-var DB = make(map[string]string)
+var db = make(map[string]string)
func setupRouter() *gin.Engine {
// Disable Console Color
@@ -21,7 +21,7 @@ func setupRouter() *gin.Engine {
// Get user value
r.GET("/user/:name", func(c *gin.Context) {
user := c.Params.ByName("name")
- value, ok := DB[user]
+ value, ok := db[user]
if ok {
c.JSON(http.StatusOK, gin.H{"user": user, "value": value})
} else {
@@ -50,7 +50,7 @@ func setupRouter() *gin.Engine {
}
if c.Bind(&json) == nil {
- DB[user] = json.Value
+ db[user] = json.Value
c.JSON(http.StatusOK, gin.H{"status": "ok"})
}
})
diff --git a/examples/custom-validation/server.go b/examples/custom-validation/server.go
index dea0c302..9238200b 100644
--- a/examples/custom-validation/server.go
+++ b/examples/custom-validation/server.go
@@ -10,6 +10,7 @@ import (
"gopkg.in/go-playground/validator.v8"
)
+// Booking contains binded and validated data.
type Booking struct {
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
diff --git a/examples/realtime-advanced/main.go b/examples/realtime-advanced/main.go
index 1f3c8585..f3ead476 100644
--- a/examples/realtime-advanced/main.go
+++ b/examples/realtime-advanced/main.go
@@ -13,16 +13,19 @@ func main() {
StartGin()
}
+// ConfigRuntime sets the number of operating system threads.
func ConfigRuntime() {
nuCPU := runtime.NumCPU()
runtime.GOMAXPROCS(nuCPU)
fmt.Printf("Running with %d CPUs\n", nuCPU)
}
+// StartWorkers start starsWorker by goroutine.
func StartWorkers() {
go statsWorker()
}
+// StartGin starts gin web server with setting router.
func StartGin() {
gin.SetMode(gin.ReleaseMode)
diff --git a/examples/realtime-advanced/stats.go b/examples/realtime-advanced/stats.go
index 4afedcb5..a6488035 100644
--- a/examples/realtime-advanced/stats.go
+++ b/examples/realtime-advanced/stats.go
@@ -50,6 +50,7 @@ func connectedUsers() uint64 {
return uint64(connected)
}
+// Stats returns savedStats data.
func Stats() map[string]uint64 {
mutexStats.RLock()
defer mutexStats.RUnlock()
diff --git a/gin.go b/gin.go
index aa62e014..11f6d94a 100644
--- a/gin.go
+++ b/gin.go
@@ -14,11 +14,7 @@ import (
"github.com/gin-gonic/gin/render"
)
-const (
- // Version is Framework's version.
- Version = "v1.3.0"
- defaultMultipartMemory = 32 << 20 // 32 MB
-)
+const defaultMultipartMemory = 32 << 20 // 32 MB
var (
default404Body = []byte("404 page not found")
@@ -26,7 +22,10 @@ var (
defaultAppEngine bool
)
+// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
+
+// HandlersChain defines a HandlerFunc array.
type HandlersChain []HandlerFunc
// Last returns the last handler in the chain. ie. the last handler is the main own.
@@ -37,12 +36,14 @@ func (c HandlersChain) Last() HandlerFunc {
return nil
}
+// RouteInfo represents a request route's specification which contains method and path and its handler.
type RouteInfo struct {
Method string
Path string
Handler string
}
+// RoutesInfo defines a RouteInfo array.
type RoutesInfo []RouteInfo
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
@@ -155,6 +156,7 @@ func (engine *Engine) allocateContext() *Context {
return &Context{engine: engine}
}
+// Delims sets template left and right delims and returns a Engine instance.
func (engine *Engine) Delims(left, right string) *Engine {
engine.delims = render.Delims{Left: left, Right: right}
return engine
diff --git a/ginS/gins.go b/ginS/gins.go
index a7686f23..04cf131c 100644
--- a/ginS/gins.go
+++ b/ginS/gins.go
@@ -22,14 +22,17 @@ func engine() *gin.Engine {
return internalEngine
}
+// LoadHTMLGlob is a wrapper for Engine.LoadHTMLGlob.
func LoadHTMLGlob(pattern string) {
engine().LoadHTMLGlob(pattern)
}
+// LoadHTMLFiles is a wrapper for Engine.LoadHTMLFiles.
func LoadHTMLFiles(files ...string) {
engine().LoadHTMLFiles(files...)
}
+// SetHTMLTemplate is a wrapper for Engine.SetHTMLTemplate.
func SetHTMLTemplate(templ *template.Template) {
engine().SetHTMLTemplate(templ)
}
@@ -39,7 +42,7 @@ func NoRoute(handlers ...gin.HandlerFunc) {
engine().NoRoute(handlers...)
}
-// NoMethod sets the handlers called when... TODO
+// NoMethod is a wrapper for Engine.NoMethod.
func NoMethod(handlers ...gin.HandlerFunc) {
engine().NoMethod(handlers...)
}
@@ -50,6 +53,7 @@ func Group(relativePath string, handlers ...gin.HandlerFunc) *gin.RouterGroup {
return engine().Group(relativePath, handlers...)
}
+// Handle is a wrapper for Engine.Handle.
func Handle(httpMethod, relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().Handle(httpMethod, relativePath, handlers...)
}
@@ -89,10 +93,12 @@ func HEAD(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().HEAD(relativePath, handlers...)
}
+// Any is a wrapper for Engine.Any.
func Any(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().Any(relativePath, handlers...)
}
+// StaticFile is a wrapper for Engine.StaticFile.
func StaticFile(relativePath, filepath string) gin.IRoutes {
return engine().StaticFile(relativePath, filepath)
}
@@ -107,6 +113,7 @@ func Static(relativePath, root string) gin.IRoutes {
return engine().Static(relativePath, root)
}
+// StaticFS is a wrapper for Engine.StaticFS.
func StaticFS(relativePath string, fs http.FileSystem) gin.IRoutes {
return engine().StaticFS(relativePath, fs)
}
diff --git a/gin_test.go b/gin_test.go
index b3cab715..95b9cc16 100644
--- a/gin_test.go
+++ b/gin_test.go
@@ -88,7 +88,7 @@ func TestLoadHTMLGlob(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
- assert.Equal(t, "
Hello world
", string(resp[:]))
+ assert.Equal(t, "Hello world
", string(resp))
td()
}
@@ -101,7 +101,7 @@ func TestLoadHTMLGlob2(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
- assert.Equal(t, "Hello world
", string(resp[:]))
+ assert.Equal(t, "Hello world
", string(resp))
td()
}
@@ -114,7 +114,7 @@ func TestLoadHTMLGlob3(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
- assert.Equal(t, "Hello world
", string(resp[:]))
+ assert.Equal(t, "Hello world
", string(resp))
td()
}
@@ -134,7 +134,7 @@ func TestLoadHTMLGlobUsingTLS(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
- assert.Equal(t, "Hello world
", string(resp[:]))
+ assert.Equal(t, "Hello world
", string(resp))
td()
}
@@ -148,7 +148,7 @@ func TestLoadHTMLGlobFromFuncMap(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
- assert.Equal(t, "Date: 2017/07/01\n", string(resp[:]))
+ assert.Equal(t, "Date: 2017/07/01\n", string(resp))
td()
}
@@ -187,7 +187,7 @@ func TestLoadHTMLFiles(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
- assert.Equal(t, "Hello world
", string(resp[:]))
+ assert.Equal(t, "Hello world
", string(resp))
td()
}
@@ -199,7 +199,7 @@ func TestLoadHTMLFiles2(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
- assert.Equal(t, "Hello world
", string(resp[:]))
+ assert.Equal(t, "Hello world
", string(resp))
td()
}
@@ -211,7 +211,7 @@ func TestLoadHTMLFiles3(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
- assert.Equal(t, "Hello world
", string(resp[:]))
+ assert.Equal(t, "Hello world
", string(resp))
td()
}
@@ -230,7 +230,7 @@ func TestLoadHTMLFilesUsingTLS(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
- assert.Equal(t, "Hello world
", string(resp[:]))
+ assert.Equal(t, "Hello world
", string(resp))
td()
}
@@ -243,7 +243,7 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
- assert.Equal(t, "Date: 2017/07/01\n", string(resp[:]))
+ assert.Equal(t, "Date: 2017/07/01\n", string(resp))
td()
}
diff --git a/go.mod b/go.mod
new file mode 100644
index 00000000..bd4ad975
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,27 @@
+module github.com/gin-gonic/gin
+
+require (
+ github.com/davecgh/go-spew v0.0.0-20180221232628-8991bc29aa16 // indirect
+ github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66
+ github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7
+ github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b
+ github.com/golang/protobuf v1.2.0
+ github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15
+ github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b
+ github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227
+ github.com/mattn/go-isatty v0.0.3
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/stretchr/testify v1.2.2
+ github.com/thinkerou/favicon v0.0.0-20170710140520-94a442a49da6
+ github.com/ugorji/go v1.1.1
+ golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b
+ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d
+ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
+ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e // indirect
+ google.golang.org/grpc v1.15.0
+ gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
+ gopkg.in/go-playground/validator.v8 v8.18.2
+ gopkg.in/yaml.v2 v2.2.1
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 00000000..3382beed
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,65 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/davecgh/go-spew v0.0.0-20180221232628-8991bc29aa16 h1:HqkufMBR7waVfFFRABWqHa1WgTvjtVDJTLJe3CR576A=
+github.com/davecgh/go-spew v0.0.0-20180221232628-8991bc29aa16/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66 h1:QnnoVdChKs+GeTvN4rPYTW6b5U6M3HMEvQ/+x4IGtfY=
+github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66/go.mod h1:kTEh6M2J/mh7nsskr28alwLCXm/DSG5OSA/o31yy2XU=
+github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY=
+github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
+github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b h1:dm/NYytoj7p8Jc6zMvyRz3PCQrTTCXnVRvEzyBcM890=
+github.com/gin-gonic/autotls v0.0.0-20180426091246-be87bd5ef97b/go.mod h1:vwfeXwKgEIWq63oVfwaBjoByS4dZzYbHHROHjV4IjNY=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15 h1:cW/amwGEJK5MSKntPXRjX4dxs/nGxGT8gXKIsKFmHGc=
+github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15/go.mod h1:Fdm/oWRW+CH8PRbLntksCNtmcCBximKPkVQYvmMl80k=
+github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b h1:X61dhFTE1Au92SvyF8HyAwdjWqiSdfBgFR7wTxC0+uU=
+github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227 h1:KIaAZ/V+/0/6BOULrmBQ9T1ed8BkKqGIjIKW923nJuo=
+github.com/manucorporat/stats v0.0.0-20180402194714-3ba42d56d227/go.mod h1:ruMr5t05gVho4tuDv0PbI0Bb8nOxc/5Y6JzRHe/yfA0=
+github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/thinkerou/favicon v0.0.0-20170710140520-94a442a49da6 h1:d/LEgOfWe+AlOCz/kzmkvlO+gq9LRGhjSHqt2nx8Muc=
+github.com/thinkerou/favicon v0.0.0-20170710140520-94a442a49da6/go.mod h1:HL7Pap5kOluZv1ku34pZo/AJ44GaxMEPFZ3pmuexV2s=
+github.com/ugorji/go v1.1.1 h1:gmervu+jDMvXTbcHQ0pd2wee85nEoE0BsVyEuzkfK8w=
+github.com/ugorji/go v1.1.1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
+golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b h1:2b9XGzhjiYsYPnKXoEfL7klWZQIt8IfyRCz62gCqqlQ=
+golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/grpc v1.15.0 h1:Az/KuahOM4NAidTEuJCv/RonAA7rYsTPkqXVjr+8OOw=
+google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
+gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
+gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
+gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
+gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/internal/json/json.go b/internal/json/json.go
index 4f643c56..419d35f2 100644
--- a/internal/json/json.go
+++ b/internal/json/json.go
@@ -9,8 +9,12 @@ package json
import "encoding/json"
var (
- Marshal = json.Marshal
+ // Marshal is exported by gin/json package.
+ Marshal = json.Marshal
+ // MarshalIndent is exported by gin/json package.
MarshalIndent = json.MarshalIndent
- NewDecoder = json.NewDecoder
- NewEncoder = json.NewEncoder
+ // NewDecoder is exported by gin/json package.
+ NewDecoder = json.NewDecoder
+ // NewEncoder is exported by gin/json package.
+ NewEncoder = json.NewEncoder
)
diff --git a/internal/json/jsoniter.go b/internal/json/jsoniter.go
index f1ed5bea..2021c53c 100644
--- a/internal/json/jsoniter.go
+++ b/internal/json/jsoniter.go
@@ -9,9 +9,13 @@ package json
import "github.com/json-iterator/go"
var (
- json = jsoniter.ConfigCompatibleWithStandardLibrary
- Marshal = json.Marshal
+ json = jsoniter.ConfigCompatibleWithStandardLibrary
+ // Marshal is exported by gin/json package.
+ Marshal = json.Marshal
+ // MarshalIndent is exported by gin/json package.
MarshalIndent = json.MarshalIndent
- NewDecoder = json.NewDecoder
- NewEncoder = json.NewEncoder
+ // NewDecoder is exported by gin/json package.
+ NewDecoder = json.NewDecoder
+ // NewEncoder is exported by gin/json package.
+ NewEncoder = json.NewEncoder
)
diff --git a/mode.go b/mode.go
index 9df4e45f..7cb0143a 100644
--- a/mode.go
+++ b/mode.go
@@ -11,12 +11,16 @@ import (
"github.com/gin-gonic/gin/binding"
)
+// ENV_GIN_MODE indicates environment name for gin mode.
const ENV_GIN_MODE = "GIN_MODE"
const (
- DebugMode = "debug"
+ // DebugMode indicates gin mode is debug.
+ DebugMode = "debug"
+ // ReleaseMode indicates gin mode is relase.
ReleaseMode = "release"
- TestMode = "test"
+ // TestMode indicates gin mode is test.
+ TestMode = "test"
)
const (
debugCode = iota
@@ -42,6 +46,7 @@ func init() {
SetMode(mode)
}
+// SetMode sets gin mode according to input string.
func SetMode(value string) {
switch value {
case DebugMode, "":
@@ -59,14 +64,18 @@ func SetMode(value string) {
modeName = value
}
+// DisableBindValidation closes the default validator.
func DisableBindValidation() {
binding.Validator = nil
}
+// EnableJsonDecoderUseNumber sets true for binding.EnableDecoderUseNumberto to
+// call the UseNumber method on the JSON Decoder instance.
func EnableJsonDecoderUseNumber() {
binding.EnableDecoderUseNumber = true
}
+// Mode returns currently gin mode.
func Mode() string {
return modeName
}
diff --git a/render/data.go b/render/data.go
index 33194913..6ba657ba 100644
--- a/render/data.go
+++ b/render/data.go
@@ -6,6 +6,7 @@ package render
import "net/http"
+// Data contains ContentType and bytes data.
type Data struct {
ContentType string
Data []byte
@@ -18,6 +19,7 @@ func (r Data) Render(w http.ResponseWriter) (err error) {
return
}
+// WriteContentType (Data) writes custom ContentType.
func (r Data) WriteContentType(w http.ResponseWriter) {
writeContentType(w, []string{r.ContentType})
}
diff --git a/render/html.go b/render/html.go
index 1e3be65b..6696ece9 100644
--- a/render/html.go
+++ b/render/html.go
@@ -9,20 +9,27 @@ import (
"net/http"
)
+// Delims represents a set of Left and Right delimiters for HTML template rendering.
type Delims struct {
- Left string
+ // Left delimiter, defaults to {{.
+ Left string
+ // Right delimiter, defaults to }}.
Right string
}
+// HTMLRender interface is to be implemented by HTMLProduction and HTMLDebug.
type HTMLRender interface {
+ // Instance returns an HTML instance.
Instance(string, interface{}) Render
}
+// HTMLProduction contains template reference and its delims.
type HTMLProduction struct {
Template *template.Template
Delims Delims
}
+// HTMLDebug contains template delims and pattern and function with file list.
type HTMLDebug struct {
Files []string
Glob string
@@ -30,6 +37,7 @@ type HTMLDebug struct {
FuncMap template.FuncMap
}
+// HTML contains template reference and its name with given interface object.
type HTML struct {
Template *template.Template
Name string
@@ -38,6 +46,7 @@ type HTML struct {
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 {
return HTML{
Template: r.Template,
@@ -46,6 +55,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 {
return HTML{
Template: r.loadTemplate(),
@@ -66,6 +76,7 @@ func (r HTMLDebug) loadTemplate() *template.Template {
panic("the HTML debug render was created without files or glob pattern")
}
+// Render (HTML) executes template and writes its result with custom ContentType for response.
func (r HTML) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
@@ -75,6 +86,7 @@ func (r HTML) Render(w http.ResponseWriter) error {
return r.Template.ExecuteTemplate(w, r.Name, r.Data)
}
+// WriteContentType (HTML) writes HTML ContentType.
func (r HTML) WriteContentType(w http.ResponseWriter) {
writeContentType(w, htmlContentType)
}
diff --git a/render/json.go b/render/json.go
index f6b7878e..32d0fc42 100644
--- a/render/json.go
+++ b/render/json.go
@@ -13,34 +13,41 @@ import (
"github.com/gin-gonic/gin/internal/json"
)
+// JSON contains the given interface object.
type JSON struct {
Data interface{}
}
+// IndentedJSON contains the given interface object.
type IndentedJSON struct {
Data interface{}
}
+// SecureJSON contains the given interface object and its prefix.
type SecureJSON struct {
Prefix string
Data interface{}
}
+// JsonpJSON contains the given interface object its callback.
type JsonpJSON struct {
Callback string
Data interface{}
}
+// AsciiJSON contains the given interface object.
type AsciiJSON struct {
Data interface{}
}
+// SecureJSONPrefix is a string which represents SecureJSON prefix.
type SecureJSONPrefix string
var jsonContentType = []string{"application/json; charset=utf-8"}
var jsonpContentType = []string{"application/javascript; charset=utf-8"}
var jsonAsciiContentType = []string{"application/json"}
+// Render (JSON) writes data with custom ContentType.
func (r JSON) Render(w http.ResponseWriter) (err error) {
if err = WriteJSON(w, r.Data); err != nil {
panic(err)
@@ -48,10 +55,12 @@ func (r JSON) Render(w http.ResponseWriter) (err error) {
return
}
+// WriteContentType (JSON) writes JSON ContentType.
func (r JSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
+// WriteJSON marshals the given interface object and writes it with custom ContentType.
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
writeContentType(w, jsonContentType)
jsonBytes, err := json.Marshal(obj)
@@ -62,6 +71,7 @@ func WriteJSON(w http.ResponseWriter, obj interface{}) error {
return nil
}
+// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType.
func (r IndentedJSON) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
@@ -72,10 +82,12 @@ func (r IndentedJSON) Render(w http.ResponseWriter) error {
return nil
}
+// WriteContentType (IndentedJSON) writes JSON ContentType.
func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
+// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType.
func (r SecureJSON) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
jsonBytes, err := json.Marshal(r.Data)
@@ -90,10 +102,12 @@ func (r SecureJSON) Render(w http.ResponseWriter) error {
return nil
}
+// WriteContentType (SecureJSON) writes JSON ContentType.
func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
+// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType.
func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
ret, err := json.Marshal(r.Data)
@@ -115,10 +129,12 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
return nil
}
+// WriteContentType (JsonpJSON) writes Javascript ContentType.
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonpContentType)
}
+// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType.
func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
ret, err := json.Marshal(r.Data)
@@ -139,6 +155,7 @@ func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
return nil
}
+// WriteContentType (AsciiJSON) writes JSON ContentType.
func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonAsciiContentType)
}
diff --git a/render/json_17.go b/render/json_17.go
index 75f7f374..208193c7 100644
--- a/render/json_17.go
+++ b/render/json_17.go
@@ -12,10 +12,12 @@ import (
"github.com/gin-gonic/gin/internal/json"
)
+// PureJSON contains the given interface object.
type PureJSON struct {
Data interface{}
}
+// Render (PureJSON) writes custom ContentType and encodes the given interface object.
func (r PureJSON) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
encoder := json.NewEncoder(w)
@@ -23,6 +25,7 @@ func (r PureJSON) Render(w http.ResponseWriter) error {
return encoder.Encode(r.Data)
}
+// WriteContentType (PureJSON) writes custom ContentType.
func (r PureJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
diff --git a/render/msgpack.go b/render/msgpack.go
index b6b8aa0f..dc681fcf 100644
--- a/render/msgpack.go
+++ b/render/msgpack.go
@@ -10,20 +10,24 @@ import (
"github.com/ugorji/go/codec"
)
+// MsgPack contains the given interface object.
type MsgPack struct {
Data interface{}
}
var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
+// WriteContentType (MsgPack) writes MsgPack ContentType.
func (r MsgPack) WriteContentType(w http.ResponseWriter) {
writeContentType(w, msgpackContentType)
}
+// Render (MsgPack) encodes the given interface object and writes data with custom ContentType.
func (r MsgPack) Render(w http.ResponseWriter) error {
return WriteMsgPack(w, r.Data)
}
+// WriteMsgPack writes MsgPack ContentType and encodes the given interface object.
func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
writeContentType(w, msgpackContentType)
var mh codec.MsgpackHandle
diff --git a/render/protobuf.go b/render/protobuf.go
index 34f1e9b5..47895072 100644
--- a/render/protobuf.go
+++ b/render/protobuf.go
@@ -10,12 +10,14 @@ import (
"github.com/golang/protobuf/proto"
)
+// ProtoBuf contains the given interface object.
type ProtoBuf struct {
Data interface{}
}
var protobufContentType = []string{"application/x-protobuf"}
+// Render (ProtoBuf) marshals the given interface object and writes data with custom ContentType.
func (r ProtoBuf) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
@@ -28,6 +30,7 @@ func (r ProtoBuf) Render(w http.ResponseWriter) error {
return nil
}
+// WriteContentType (ProtoBuf) writes ProtoBuf ContentType.
func (r ProtoBuf) WriteContentType(w http.ResponseWriter) {
writeContentType(w, protobufContentType)
}
diff --git a/render/reader.go b/render/reader.go
index 7a06cce4..ab60e53a 100644
--- a/render/reader.go
+++ b/render/reader.go
@@ -10,6 +10,7 @@ import (
"strconv"
)
+// Reader contains the IO reader and its length, and custom ContentType and other headers.
type Reader struct {
ContentType string
ContentLength int64
@@ -26,10 +27,12 @@ func (r Reader) Render(w http.ResponseWriter) (err error) {
return
}
+// WriteContentType (Reader) writes custom ContentType.
func (r Reader) WriteContentType(w http.ResponseWriter) {
writeContentType(w, []string{r.ContentType})
}
+// writeHeaders writes custom Header.
func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) {
header := w.Header()
for k, v := range headers {
diff --git a/render/redirect.go b/render/redirect.go
index a0634f5a..9c145fe2 100644
--- a/render/redirect.go
+++ b/render/redirect.go
@@ -9,12 +9,14 @@ import (
"net/http"
)
+// Redirect contains the http request reference and redirects status code and location.
type Redirect struct {
Code int
Request *http.Request
Location string
}
+// Render (Redirect) redirects the http request to new location and writes redirect response.
func (r Redirect) Render(w http.ResponseWriter) error {
// todo(thinkerou): go1.6 not support StatusPermanentRedirect(308)
// when we upgrade go version we can use http.StatusPermanentRedirect
@@ -25,4 +27,5 @@ func (r Redirect) Render(w http.ResponseWriter) error {
return nil
}
+// WriteContentType (Redirect) don't write any ContentType.
func (r Redirect) WriteContentType(http.ResponseWriter) {}
diff --git a/render/render.go b/render/render.go
index df0d1d7c..abfc79fc 100644
--- a/render/render.go
+++ b/render/render.go
@@ -6,8 +6,11 @@ package render
import "net/http"
+// Render interface is to be implemented by JSON, XML, HTML, YAML and so on.
type Render interface {
+ // Render writes data with custom ContentType.
Render(http.ResponseWriter) error
+ // WriteContentType writes custom ContentType.
WriteContentType(w http.ResponseWriter)
}
diff --git a/render/render_test.go b/render/render_test.go
index fe9228e9..09ccc658 100644
--- a/render/render_test.go
+++ b/render/render_test.go
@@ -287,7 +287,7 @@ func TestRenderProtoBuf(t *testing.T) {
err = (ProtoBuf{data}).Render(w)
assert.NoError(t, err)
- assert.Equal(t, string(protoData[:]), w.Body.String())
+ assert.Equal(t, string(protoData), w.Body.String())
assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
}
diff --git a/render/text.go b/render/text.go
index 7a6acc44..2ea7343c 100644
--- a/render/text.go
+++ b/render/text.go
@@ -10,6 +10,7 @@ import (
"net/http"
)
+// String contains the given interface object slice and its format.
type String struct {
Format string
Data []interface{}
@@ -17,15 +18,18 @@ type String struct {
var plainContentType = []string{"text/plain; charset=utf-8"}
+// Render (String) writes data with custom ContentType.
func (r String) Render(w http.ResponseWriter) error {
WriteString(w, r.Format, r.Data)
return nil
}
+// WriteContentType (String) writes Plain ContentType.
func (r String) WriteContentType(w http.ResponseWriter) {
writeContentType(w, plainContentType)
}
+// WriteString writes data according to its format and write custom ContentType.
func WriteString(w http.ResponseWriter, format string, data []interface{}) {
writeContentType(w, plainContentType)
if len(data) > 0 {
diff --git a/render/xml.go b/render/xml.go
index cff1ac3e..cc5390a2 100644
--- a/render/xml.go
+++ b/render/xml.go
@@ -9,17 +9,20 @@ import (
"net/http"
)
+// XML contains the given interface object.
type XML struct {
Data interface{}
}
var xmlContentType = []string{"application/xml; charset=utf-8"}
+// Render (XML) encodes the given interface object and writes data with custom ContentType.
func (r XML) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
return xml.NewEncoder(w).Encode(r.Data)
}
+// WriteContentType (XML) writes XML ContentType for response.
func (r XML) WriteContentType(w http.ResponseWriter) {
writeContentType(w, xmlContentType)
}
diff --git a/render/yaml.go b/render/yaml.go
index 25d0ebd4..33bc3254 100644
--- a/render/yaml.go
+++ b/render/yaml.go
@@ -10,12 +10,14 @@ import (
"gopkg.in/yaml.v2"
)
+// YAML contains the given interface object.
type YAML struct {
Data interface{}
}
var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
+// Render (YAML) marshals the given interface object and writes data with custom ContentType.
func (r YAML) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
@@ -28,6 +30,7 @@ func (r YAML) Render(w http.ResponseWriter) error {
return nil
}
+// WriteContentType (YAML) writes YAML ContentType for response.
func (r YAML) WriteContentType(w http.ResponseWriter) {
writeContentType(w, yamlContentType)
}
diff --git a/routergroup.go b/routergroup.go
index 876a61b8..579aa7dc 100644
--- a/routergroup.go
+++ b/routergroup.go
@@ -11,11 +11,13 @@ import (
"strings"
)
+// IRouter defines all router handle interface includes single and group router.
type IRouter interface {
IRoutes
Group(string, ...HandlerFunc) *RouterGroup
}
+// IRoutes defines all router handle interface.
type IRoutes interface {
Use(...HandlerFunc) IRoutes
@@ -34,8 +36,8 @@ type IRoutes interface {
StaticFS(string, http.FileSystem) IRoutes
}
-// RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix
-// and an array of handlers (middleware).
+// RouterGroup is used internally to configure router, a RouterGroup is associated with
+// a prefix and an array of handlers (middleware).
type RouterGroup struct {
Handlers HandlersChain
basePath string
@@ -61,6 +63,8 @@ func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *R
}
}
+// BasePath returns the base path of router group.
+// For example, if v := router.Group("/rest/n/v1/api"), v.BasePath() is "/rest/n/v1/api".
func (group *RouterGroup) BasePath() string {
return group.basePath
}
diff --git a/tree.go b/tree.go
index b6530665..ada62ceb 100644
--- a/tree.go
+++ b/tree.go
@@ -193,9 +193,16 @@ func (n *node) addRoute(path string, handlers HandlersChain) {
}
}
- panic("path segment '" + path +
+ pathSeg := path
+ if n.nType != catchAll {
+ pathSeg = strings.SplitN(path, "/", 2)[0]
+ }
+ prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
+ panic("'" + pathSeg +
+ "' in new path '" + fullPath +
"' conflicts with existing wildcard '" + n.path +
- "' in path '" + fullPath + "'")
+ "' in existing prefix '" + prefix +
+ "'")
}
c := path[0]
diff --git a/tree_test.go b/tree_test.go
index 5bc27171..a1b3bbe7 100644
--- a/tree_test.go
+++ b/tree_test.go
@@ -5,7 +5,9 @@
package gin
import (
+ "fmt"
"reflect"
+ "regexp"
"strings"
"testing"
)
@@ -125,8 +127,6 @@ func TestTreeAddAndGet(t *testing.T) {
tree.addRoute(route, fakeHandler(route))
}
- //printChildren(tree, "")
-
checkRequests(t, tree, testRequests{
{"/a", false, "/a", nil},
{"/", true, "", nil},
@@ -168,8 +168,6 @@ func TestTreeWildcard(t *testing.T) {
tree.addRoute(route, fakeHandler(route))
}
- //printChildren(tree, "")
-
checkRequests(t, tree, testRequests{
{"/", false, "/", nil},
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}},
@@ -208,7 +206,6 @@ func TestUnescapeParameters(t *testing.T) {
tree.addRoute(route, fakeHandler(route))
}
- //printChildren(tree, "")
unescape := true
checkRequests(t, tree, testRequests{
{"/", false, "/", nil},
@@ -260,8 +257,6 @@ func testRoutes(t *testing.T, routes []testRoute) {
t.Errorf("unexpected panic for route '%s': %v", route.path, recv)
}
}
-
- //printChildren(tree, "")
}
func TestTreeWildcardConflict(t *testing.T) {
@@ -328,8 +323,6 @@ func TestTreeDupliatePath(t *testing.T) {
}
}
- //printChildren(tree, "")
-
checkRequests(t, tree, testRequests{
{"/", false, "/", nil},
{"/doc/", false, "/doc/", nil},
@@ -444,8 +437,6 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
}
}
- //printChildren(tree, "")
-
tsrRoutes := [...]string{
"/hi/",
"/b",
@@ -664,3 +655,43 @@ func TestTreeInvalidNodeType(t *testing.T) {
t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv)
}
}
+
+func TestTreeWildcardConflictEx(t *testing.T) {
+ conflicts := [...]struct {
+ route string
+ segPath string
+ existPath string
+ existSegPath string
+ }{
+ {"/who/are/foo", "/foo", `/who/are/\*you`, `/\*you`},
+ {"/who/are/foo/", "/foo/", `/who/are/\*you`, `/\*you`},
+ {"/who/are/foo/bar", "/foo/bar", `/who/are/\*you`, `/\*you`},
+ {"/conxxx", "xxx", `/con:tact`, `:tact`},
+ {"/conooo/xxx", "ooo", `/con:tact`, `:tact`},
+ }
+
+ for _, conflict := range conflicts {
+ // I have to re-create a 'tree', because the 'tree' will be
+ // in an inconsistent state when the loop recovers from the
+ // panic which threw by 'addRoute' function.
+ tree := &node{}
+ routes := [...]string{
+ "/con:tact",
+ "/who/are/*you",
+ "/who/foo/hello",
+ }
+
+ for _, route := range routes {
+ tree.addRoute(route, fakeHandler(route))
+ }
+
+ recv := catchPanic(func() {
+ tree.addRoute(conflict.route, fakeHandler(conflict.route))
+ })
+
+ if !regexp.MustCompile(fmt.Sprintf("'%s' in new path .* conflicts with existing wildcard '%s' in existing prefix '%s'",
+ conflict.segPath, conflict.existSegPath, conflict.existPath)).MatchString(fmt.Sprint(recv)) {
+ t.Fatalf("invalid wildcard conflict error (%v)", recv)
+ }
+ }
+}
diff --git a/utils.go b/utils.go
index bf32c775..f4532d56 100644
--- a/utils.go
+++ b/utils.go
@@ -14,8 +14,10 @@ import (
"strings"
)
+// 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 {
value := reflect.ValueOf(val)
if value.Kind() == reflect.Ptr {
@@ -33,16 +35,14 @@ func Bind(val interface{}) HandlerFunc {
}
}
-// WrapF is a helper function for wrapping http.HandlerFunc
-// Returns a Gin middleware
+// WrapF is a helper function for wrapping http.HandlerFunc and returns a Gin middleware.
func WrapF(f http.HandlerFunc) HandlerFunc {
return func(c *Context) {
f(c.Writer, c.Request)
}
}
-// WrapH is a helper function for wrapping http.Handler
-// Returns a Gin middleware
+// WrapH is a helper function for wrapping http.Handler and returns a Gin middleware.
func WrapH(h http.Handler) HandlerFunc {
return func(c *Context) {
h.ServeHTTP(c.Writer, c.Request)
diff --git a/vendor/vendor.json b/vendor/vendor.json
index e6d038a4..86df11be 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -27,8 +27,8 @@
"path": "github.com/json-iterator/go",
"revision": "1624edc4454b8682399def8740d46db5e4362ba4",
"revisionTime": "2018-08-06T06:07:27Z",
- "version": "1.1.5",
- "versionExact": "1.1.5"
+ "version": "v1.1",
+ "versionExact": "v1.1.5"
},
{
"checksumSHA1": "y/A5iuvwjytQE2CqVuphQRXR2nI=",
@@ -38,24 +38,6 @@
"version": "v0.0.3",
"versionExact": "v0.0.3"
},
- {
- "checksumSHA1": "ZTcgWKWHsrX0RXYVXn5Xeb8Q0go=",
- "path": "github.com/modern-go/concurrent",
- "revision": "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94",
- "revisionTime": "2018-03-06T01:26:44Z"
- },
- {
- "checksumSHA1": "qvH48wzTIV3QKSDqI0dLFtVjaDI=",
- "path": "github.com/modern-go/reflect2",
- "revision": "94122c33edd36123c84d5368cfb2b69df93a0ec8",
- "revisionTime": "2018-07-18T01:23:57Z"
- },
- {
- "checksumSHA1": "LuFv4/jlrmFNnDb/5SCSEPAM9vU=",
- "path": "github.com/pmezard/go-difflib/difflib",
- "revision": "792786c7400a136282c1664665ae0a8db921c6c2",
- "revisionTime": "2016-01-10T10:55:54Z"
- },
{
"checksumSHA1": "c6pbpF7eowwO59phRTpF8cQ80Z0=",
"path": "github.com/stretchr/testify/assert",
@@ -79,12 +61,6 @@
"revision": "d4c55e66d8c3a2f3382d264b08e3e3454a66355a",
"revisionTime": "2016-10-18T08:54:36Z"
},
- {
- "checksumSHA1": "7Gocawl8bm27cpAILtuf21xvVD8=",
- "path": "golang.org/x/sys/unix",
- "revision": "1c9583448a9c3aa0f9a6a5241bf73c0bd8aafded",
- "revisionTime": "2018-08-15T07:37:39Z"
- },
{
"checksumSHA1": "P/k5ZGf0lEBgpKgkwy++F7K1PSg=",
"path": "gopkg.in/go-playground/validator.v8",
diff --git a/version.go b/version.go
new file mode 100644
index 00000000..028caebe
--- /dev/null
+++ b/version.go
@@ -0,0 +1,8 @@
+// Copyright 2018 Gin Core Team. All rights reserved.
+// Use of this source code is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package gin
+
+// Version is the current gin framework's version.
+const Version = "v1.4.0-dev"