mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-22 17:42:14 +08:00
Merge branch 'master' into patch-1
This commit is contained in:
commit
1416a04404
@ -7,6 +7,11 @@ go:
|
|||||||
- 1.9.x
|
- 1.9.x
|
||||||
- 1.10.x
|
- 1.10.x
|
||||||
- 1.11.x
|
- 1.11.x
|
||||||
|
- master
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: master
|
||||||
|
|
||||||
git:
|
git:
|
||||||
depth: 10
|
depth: 10
|
||||||
|
45
README.md
45
README.md
@ -58,6 +58,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)
|
- [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)
|
- [Try to bind body into different structs](#try-to-bind-body-into-different-structs)
|
||||||
- [http2 server push](#http2-server-push)
|
- [http2 server push](#http2-server-push)
|
||||||
|
- [Define format for the log of routes](#define-format-for-the-log-of-routes)
|
||||||
- [Testing](#testing)
|
- [Testing](#testing)
|
||||||
- [Users](#users)
|
- [Users](#users)
|
||||||
|
|
||||||
@ -657,6 +658,7 @@ import (
|
|||||||
"gopkg.in/go-playground/validator.v8"
|
"gopkg.in/go-playground/validator.v8"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Booking contains binded and validated data.
|
||||||
type Booking struct {
|
type Booking struct {
|
||||||
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
|
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"`
|
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
|
||||||
@ -1835,6 +1837,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
|
## Testing
|
||||||
|
|
||||||
The `net/http/httptest` package is preferable way for HTTP testing.
|
The `net/http/httptest` package is preferable way for HTTP testing.
|
||||||
|
@ -711,6 +711,7 @@ func (c *Context) Cookie(name string) (string, error) {
|
|||||||
return val, nil
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render writes the response headers and calls render.Render to render data.
|
||||||
func (c *Context) Render(code int, r render.Render) {
|
func (c *Context) Render(code int, r render.Render) {
|
||||||
c.Status(code)
|
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) {
|
func (c *Context) Stream(step func(w io.Writer) bool) {
|
||||||
w := c.Writer
|
w := c.Writer
|
||||||
clientGone := w.CloseNotify()
|
clientGone := w.CloseNotify()
|
||||||
@ -854,6 +856,7 @@ func (c *Context) Stream(step func(w io.Writer) bool) {
|
|||||||
/******** CONTENT NEGOTIATION *******/
|
/******** CONTENT NEGOTIATION *******/
|
||||||
/************************************/
|
/************************************/
|
||||||
|
|
||||||
|
// Negotiate contains all negotiations data.
|
||||||
type Negotiate struct {
|
type Negotiate struct {
|
||||||
Offered []string
|
Offered []string
|
||||||
HTMLName string
|
HTMLName string
|
||||||
@ -863,6 +866,7 @@ type Negotiate struct {
|
|||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Negotiate calls different Render according acceptable Accept format.
|
||||||
func (c *Context) Negotiate(code int, config Negotiate) {
|
func (c *Context) Negotiate(code int, config Negotiate) {
|
||||||
switch c.NegotiateFormat(config.Offered...) {
|
switch c.NegotiateFormat(config.Offered...) {
|
||||||
case binding.MIMEJSON:
|
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 {
|
func (c *Context) NegotiateFormat(offered ...string) string {
|
||||||
assert1(len(offered) > 0, "you must provide at least one offer")
|
assert1(len(offered) > 0, "you must provide at least one offer")
|
||||||
|
|
||||||
@ -901,6 +906,7 @@ func (c *Context) NegotiateFormat(offered ...string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAccepted sets Accept header data.
|
||||||
func (c *Context) SetAccepted(formats ...string) {
|
func (c *Context) SetAccepted(formats ...string) {
|
||||||
c.Accepted = formats
|
c.Accepted = formats
|
||||||
}
|
}
|
||||||
|
@ -784,14 +784,14 @@ func TestContextRenderHTML2(t *testing.T) {
|
|||||||
router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})
|
router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})
|
||||||
assert.Len(t, router.trees, 1)
|
assert.Len(t, router.trees, 1)
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
setup(&b)
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
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"})
|
c.HTML(http.StatusCreated, "t", H{"name": "alexandernyquist"})
|
||||||
|
|
||||||
@ -978,7 +978,7 @@ func TestContextRenderProtoBuf(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, http.StatusCreated, w.Code)
|
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"))
|
assert.Equal(t, "application/x-protobuf", w.HeaderMap.Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
debug.go
17
debug.go
@ -6,25 +6,28 @@ package gin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
log.SetFlags(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDebugging returns true if the framework is running in debug mode.
|
// IsDebugging returns true if the framework is running in debug mode.
|
||||||
// Use SetMode(gin.ReleaseMode) to disable debug mode.
|
// Use SetMode(gin.ReleaseMode) to disable debug mode.
|
||||||
func IsDebugging() bool {
|
func IsDebugging() bool {
|
||||||
return ginMode == debugCode
|
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) {
|
func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) {
|
||||||
if IsDebugging() {
|
if IsDebugging() {
|
||||||
nuHandlers := len(handlers)
|
nuHandlers := len(handlers)
|
||||||
handlerName := nameOfFunction(handlers.Last())
|
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{}) {
|
func debugPrint(format string, values ...interface{}) {
|
||||||
if IsDebugging() {
|
if IsDebugging() {
|
||||||
log.Printf("[GIN-debug] "+format, values...)
|
fmt.Printf("[GIN-debug] "+format, values...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
140
debug_test.go
140
debug_test.go
@ -11,6 +11,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -30,86 +31,101 @@ func TestIsDebugging(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDebugPrint(t *testing.T) {
|
func TestDebugPrint(t *testing.T) {
|
||||||
var w bytes.Buffer
|
re := captureOutput(func() {
|
||||||
setup(&w)
|
SetMode(DebugMode)
|
||||||
defer teardown()
|
SetMode(ReleaseMode)
|
||||||
|
debugPrint("DEBUG this!")
|
||||||
SetMode(ReleaseMode)
|
SetMode(TestMode)
|
||||||
debugPrint("DEBUG this!")
|
debugPrint("DEBUG this!")
|
||||||
SetMode(TestMode)
|
SetMode(DebugMode)
|
||||||
debugPrint("DEBUG this!")
|
debugPrint("these are %d %s\n", 2, "error messages")
|
||||||
assert.Empty(t, w.String())
|
SetMode(TestMode)
|
||||||
|
})
|
||||||
SetMode(DebugMode)
|
assert.Equal(t, "[GIN-debug] these are 2 error messages\n", re)
|
||||||
debugPrint("these are %d %s\n", 2, "error messages")
|
|
||||||
assert.Equal(t, "[GIN-debug] these are 2 error messages\n", w.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDebugPrintError(t *testing.T) {
|
func TestDebugPrintError(t *testing.T) {
|
||||||
var w bytes.Buffer
|
re := captureOutput(func() {
|
||||||
setup(&w)
|
SetMode(DebugMode)
|
||||||
defer teardown()
|
debugPrintError(nil)
|
||||||
|
debugPrintError(errors.New("this is an error"))
|
||||||
SetMode(DebugMode)
|
SetMode(TestMode)
|
||||||
debugPrintError(nil)
|
})
|
||||||
assert.Empty(t, w.String())
|
assert.Equal(t, "[GIN-debug] [ERROR] this is an error\n", re)
|
||||||
|
|
||||||
debugPrintError(errors.New("this is an error"))
|
|
||||||
assert.Equal(t, "[GIN-debug] [ERROR] this is an error\n", w.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDebugPrintRoutes(t *testing.T) {
|
func TestDebugPrintRoutes(t *testing.T) {
|
||||||
var w bytes.Buffer
|
re := captureOutput(func() {
|
||||||
setup(&w)
|
SetMode(DebugMode)
|
||||||
defer teardown()
|
debugPrintRoute("GET", "/path/to/route/:param", HandlersChain{func(c *Context) {}, handlerNameTest})
|
||||||
|
SetMode(TestMode)
|
||||||
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())
|
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) {
|
func TestDebugPrintLoadTemplate(t *testing.T) {
|
||||||
var w bytes.Buffer
|
re := captureOutput(func() {
|
||||||
setup(&w)
|
SetMode(DebugMode)
|
||||||
defer teardown()
|
templ := template.Must(template.New("").Delims("{[{", "}]}").ParseGlob("./testdata/template/hello.tmpl"))
|
||||||
|
debugPrintLoadTemplate(templ)
|
||||||
templ := template.Must(template.New("").Delims("{[{", "}]}").ParseGlob("./testdata/template/hello.tmpl"))
|
SetMode(TestMode)
|
||||||
debugPrintLoadTemplate(templ)
|
})
|
||||||
assert.Regexp(t, `^\[GIN-debug\] Loaded HTML Templates \(2\): \n(\t- \n|\t- hello\.tmpl\n){2}\n`, w.String())
|
assert.Regexp(t, `^\[GIN-debug\] Loaded HTML Templates \(2\): \n(\t- \n|\t- hello\.tmpl\n){2}\n`, re)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDebugPrintWARNINGSetHTMLTemplate(t *testing.T) {
|
func TestDebugPrintWARNINGSetHTMLTemplate(t *testing.T) {
|
||||||
var w bytes.Buffer
|
re := captureOutput(func() {
|
||||||
setup(&w)
|
SetMode(DebugMode)
|
||||||
defer teardown()
|
debugPrintWARNINGSetHTMLTemplate()
|
||||||
|
SetMode(TestMode)
|
||||||
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())
|
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) {
|
func TestDebugPrintWARNINGDefault(t *testing.T) {
|
||||||
var w bytes.Buffer
|
re := captureOutput(func() {
|
||||||
setup(&w)
|
SetMode(DebugMode)
|
||||||
defer teardown()
|
debugPrintWARNINGDefault()
|
||||||
|
SetMode(TestMode)
|
||||||
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())
|
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) {
|
func TestDebugPrintWARNINGNew(t *testing.T) {
|
||||||
var w bytes.Buffer
|
re := captureOutput(func() {
|
||||||
setup(&w)
|
SetMode(DebugMode)
|
||||||
defer teardown()
|
debugPrintWARNINGNew()
|
||||||
|
SetMode(TestMode)
|
||||||
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())
|
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) {
|
func captureOutput(f func()) string {
|
||||||
SetMode(DebugMode)
|
reader, writer, err := os.Pipe()
|
||||||
log.SetOutput(w)
|
if err != nil {
|
||||||
}
|
panic(err)
|
||||||
|
}
|
||||||
func teardown() {
|
stdout := os.Stdout
|
||||||
SetMode(TestMode)
|
stderr := os.Stderr
|
||||||
log.SetOutput(os.Stdout)
|
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
|
||||||
}
|
}
|
||||||
|
20
errors.go
20
errors.go
@ -12,18 +12,24 @@ import (
|
|||||||
"github.com/gin-gonic/gin/internal/json"
|
"github.com/gin-gonic/gin/internal/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrorType is an unsigned 64-bit error code as defined in the gin spec.
|
||||||
type ErrorType uint64
|
type ErrorType uint64
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ErrorTypeBind ErrorType = 1 << 63 // used when c.Bind() fails
|
// ErrorTypeBind is used when Context.Bind() fails.
|
||||||
ErrorTypeRender ErrorType = 1 << 62 // used when c.Render() 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
|
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
|
ErrorTypeAny ErrorType = 1<<64 - 1
|
||||||
ErrorTypeNu = 2
|
ErrorTypeNu = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Error represents a error's specification.
|
||||||
type Error struct {
|
type Error struct {
|
||||||
Err error
|
Err error
|
||||||
Type ErrorType
|
Type ErrorType
|
||||||
@ -34,11 +40,13 @@ type errorMsgs []*Error
|
|||||||
|
|
||||||
var _ error = &Error{}
|
var _ error = &Error{}
|
||||||
|
|
||||||
|
// SetType sets the error's type.
|
||||||
func (msg *Error) SetType(flags ErrorType) *Error {
|
func (msg *Error) SetType(flags ErrorType) *Error {
|
||||||
msg.Type = flags
|
msg.Type = flags
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMeta sets the error's meta data.
|
||||||
func (msg *Error) SetMeta(data interface{}) *Error {
|
func (msg *Error) SetMeta(data interface{}) *Error {
|
||||||
msg.Meta = data
|
msg.Meta = data
|
||||||
return msg
|
return msg
|
||||||
@ -70,11 +78,12 @@ func (msg *Error) MarshalJSON() ([]byte, error) {
|
|||||||
return json.Marshal(msg.JSON())
|
return json.Marshal(msg.JSON())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error implements the error interface
|
// Error implements the error interface.
|
||||||
func (msg Error) Error() string {
|
func (msg Error) Error() string {
|
||||||
return msg.Err.Error()
|
return msg.Err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsType judges one error.
|
||||||
func (msg *Error) IsType(flags ErrorType) bool {
|
func (msg *Error) IsType(flags ErrorType) bool {
|
||||||
return (msg.Type & flags) > 0
|
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) {
|
func (a errorMsgs) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(a.JSON())
|
return json.Marshal(a.JSON())
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DB = make(map[string]string)
|
var db = make(map[string]string)
|
||||||
|
|
||||||
func setupRouter() *gin.Engine {
|
func setupRouter() *gin.Engine {
|
||||||
// Disable Console Color
|
// Disable Console Color
|
||||||
@ -21,7 +21,7 @@ func setupRouter() *gin.Engine {
|
|||||||
// Get user value
|
// Get user value
|
||||||
r.GET("/user/:name", func(c *gin.Context) {
|
r.GET("/user/:name", func(c *gin.Context) {
|
||||||
user := c.Params.ByName("name")
|
user := c.Params.ByName("name")
|
||||||
value, ok := DB[user]
|
value, ok := db[user]
|
||||||
if ok {
|
if ok {
|
||||||
c.JSON(http.StatusOK, gin.H{"user": user, "value": value})
|
c.JSON(http.StatusOK, gin.H{"user": user, "value": value})
|
||||||
} else {
|
} else {
|
||||||
@ -50,7 +50,7 @@ func setupRouter() *gin.Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.Bind(&json) == nil {
|
if c.Bind(&json) == nil {
|
||||||
DB[user] = json.Value
|
db[user] = json.Value
|
||||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"gopkg.in/go-playground/validator.v8"
|
"gopkg.in/go-playground/validator.v8"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Booking contains binded and validated data.
|
||||||
type Booking struct {
|
type Booking struct {
|
||||||
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
|
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"`
|
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
|
||||||
|
@ -13,16 +13,19 @@ func main() {
|
|||||||
StartGin()
|
StartGin()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConfigRuntime sets the number of operating system threads.
|
||||||
func ConfigRuntime() {
|
func ConfigRuntime() {
|
||||||
nuCPU := runtime.NumCPU()
|
nuCPU := runtime.NumCPU()
|
||||||
runtime.GOMAXPROCS(nuCPU)
|
runtime.GOMAXPROCS(nuCPU)
|
||||||
fmt.Printf("Running with %d CPUs\n", nuCPU)
|
fmt.Printf("Running with %d CPUs\n", nuCPU)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartWorkers start starsWorker by goroutine.
|
||||||
func StartWorkers() {
|
func StartWorkers() {
|
||||||
go statsWorker()
|
go statsWorker()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartGin starts gin web server with setting router.
|
||||||
func StartGin() {
|
func StartGin() {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ func connectedUsers() uint64 {
|
|||||||
return uint64(connected)
|
return uint64(connected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stats returns savedStats data.
|
||||||
func Stats() map[string]uint64 {
|
func Stats() map[string]uint64 {
|
||||||
mutexStats.RLock()
|
mutexStats.RLock()
|
||||||
defer mutexStats.RUnlock()
|
defer mutexStats.RUnlock()
|
||||||
|
12
gin.go
12
gin.go
@ -14,11 +14,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin/render"
|
"github.com/gin-gonic/gin/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const defaultMultipartMemory = 32 << 20 // 32 MB
|
||||||
// Version is Framework's version.
|
|
||||||
Version = "v1.3.0"
|
|
||||||
defaultMultipartMemory = 32 << 20 // 32 MB
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
default404Body = []byte("404 page not found")
|
default404Body = []byte("404 page not found")
|
||||||
@ -26,7 +22,10 @@ var (
|
|||||||
defaultAppEngine bool
|
defaultAppEngine bool
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// HandlerFunc defines the handler used by gin middleware as return value.
|
||||||
type HandlerFunc func(*Context)
|
type HandlerFunc func(*Context)
|
||||||
|
|
||||||
|
// HandlersChain defines a HandlerFunc array.
|
||||||
type HandlersChain []HandlerFunc
|
type HandlersChain []HandlerFunc
|
||||||
|
|
||||||
// Last returns the last handler in the chain. ie. the last handler is the main own.
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RouteInfo represents a request route's specification which contains method and path and its handler.
|
||||||
type RouteInfo struct {
|
type RouteInfo struct {
|
||||||
Method string
|
Method string
|
||||||
Path string
|
Path string
|
||||||
Handler string
|
Handler string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RoutesInfo defines a RouteInfo array.
|
||||||
type RoutesInfo []RouteInfo
|
type RoutesInfo []RouteInfo
|
||||||
|
|
||||||
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
|
// 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}
|
return &Context{engine: engine}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delims sets template left and right delims and returns a Engine instance.
|
||||||
func (engine *Engine) Delims(left, right string) *Engine {
|
func (engine *Engine) Delims(left, right string) *Engine {
|
||||||
engine.delims = render.Delims{Left: left, Right: right}
|
engine.delims = render.Delims{Left: left, Right: right}
|
||||||
return engine
|
return engine
|
||||||
|
@ -22,14 +22,17 @@ func engine() *gin.Engine {
|
|||||||
return internalEngine
|
return internalEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadHTMLGlob is a wrapper for Engine.LoadHTMLGlob.
|
||||||
func LoadHTMLGlob(pattern string) {
|
func LoadHTMLGlob(pattern string) {
|
||||||
engine().LoadHTMLGlob(pattern)
|
engine().LoadHTMLGlob(pattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadHTMLFiles is a wrapper for Engine.LoadHTMLFiles.
|
||||||
func LoadHTMLFiles(files ...string) {
|
func LoadHTMLFiles(files ...string) {
|
||||||
engine().LoadHTMLFiles(files...)
|
engine().LoadHTMLFiles(files...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetHTMLTemplate is a wrapper for Engine.SetHTMLTemplate.
|
||||||
func SetHTMLTemplate(templ *template.Template) {
|
func SetHTMLTemplate(templ *template.Template) {
|
||||||
engine().SetHTMLTemplate(templ)
|
engine().SetHTMLTemplate(templ)
|
||||||
}
|
}
|
||||||
@ -39,7 +42,7 @@ func NoRoute(handlers ...gin.HandlerFunc) {
|
|||||||
engine().NoRoute(handlers...)
|
engine().NoRoute(handlers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoMethod sets the handlers called when... TODO
|
// NoMethod is a wrapper for Engine.NoMethod.
|
||||||
func NoMethod(handlers ...gin.HandlerFunc) {
|
func NoMethod(handlers ...gin.HandlerFunc) {
|
||||||
engine().NoMethod(handlers...)
|
engine().NoMethod(handlers...)
|
||||||
}
|
}
|
||||||
@ -50,6 +53,7 @@ func Group(relativePath string, handlers ...gin.HandlerFunc) *gin.RouterGroup {
|
|||||||
return engine().Group(relativePath, handlers...)
|
return engine().Group(relativePath, handlers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle is a wrapper for Engine.Handle.
|
||||||
func Handle(httpMethod, relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
func Handle(httpMethod, relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||||
return engine().Handle(httpMethod, relativePath, handlers...)
|
return engine().Handle(httpMethod, relativePath, handlers...)
|
||||||
}
|
}
|
||||||
@ -89,10 +93,12 @@ func HEAD(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
|||||||
return engine().HEAD(relativePath, handlers...)
|
return engine().HEAD(relativePath, handlers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Any is a wrapper for Engine.Any.
|
||||||
func Any(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
func Any(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||||
return engine().Any(relativePath, handlers...)
|
return engine().Any(relativePath, handlers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StaticFile is a wrapper for Engine.StaticFile.
|
||||||
func StaticFile(relativePath, filepath string) gin.IRoutes {
|
func StaticFile(relativePath, filepath string) gin.IRoutes {
|
||||||
return engine().StaticFile(relativePath, filepath)
|
return engine().StaticFile(relativePath, filepath)
|
||||||
}
|
}
|
||||||
@ -107,6 +113,7 @@ func Static(relativePath, root string) gin.IRoutes {
|
|||||||
return engine().Static(relativePath, root)
|
return engine().Static(relativePath, root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StaticFS is a wrapper for Engine.StaticFS.
|
||||||
func StaticFS(relativePath string, fs http.FileSystem) gin.IRoutes {
|
func StaticFS(relativePath string, fs http.FileSystem) gin.IRoutes {
|
||||||
return engine().StaticFS(relativePath, fs)
|
return engine().StaticFS(relativePath, fs)
|
||||||
}
|
}
|
||||||
|
20
gin_test.go
20
gin_test.go
@ -88,7 +88,7 @@ func TestLoadHTMLGlob(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, _ := ioutil.ReadAll(res.Body)
|
resp, _ := ioutil.ReadAll(res.Body)
|
||||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
|
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
|
||||||
|
|
||||||
td()
|
td()
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@ func TestLoadHTMLGlob2(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, _ := ioutil.ReadAll(res.Body)
|
resp, _ := ioutil.ReadAll(res.Body)
|
||||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
|
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
|
||||||
|
|
||||||
td()
|
td()
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ func TestLoadHTMLGlob3(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, _ := ioutil.ReadAll(res.Body)
|
resp, _ := ioutil.ReadAll(res.Body)
|
||||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
|
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
|
||||||
|
|
||||||
td()
|
td()
|
||||||
}
|
}
|
||||||
@ -134,7 +134,7 @@ func TestLoadHTMLGlobUsingTLS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, _ := ioutil.ReadAll(res.Body)
|
resp, _ := ioutil.ReadAll(res.Body)
|
||||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
|
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
|
||||||
|
|
||||||
td()
|
td()
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ func TestLoadHTMLGlobFromFuncMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, _ := ioutil.ReadAll(res.Body)
|
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()
|
td()
|
||||||
}
|
}
|
||||||
@ -187,7 +187,7 @@ func TestLoadHTMLFiles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, _ := ioutil.ReadAll(res.Body)
|
resp, _ := ioutil.ReadAll(res.Body)
|
||||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
|
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
|
||||||
td()
|
td()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +199,7 @@ func TestLoadHTMLFiles2(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, _ := ioutil.ReadAll(res.Body)
|
resp, _ := ioutil.ReadAll(res.Body)
|
||||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
|
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
|
||||||
td()
|
td()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ func TestLoadHTMLFiles3(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, _ := ioutil.ReadAll(res.Body)
|
resp, _ := ioutil.ReadAll(res.Body)
|
||||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
|
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
|
||||||
td()
|
td()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +230,7 @@ func TestLoadHTMLFilesUsingTLS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, _ := ioutil.ReadAll(res.Body)
|
resp, _ := ioutil.ReadAll(res.Body)
|
||||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
|
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
|
||||||
td()
|
td()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +243,7 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, _ := ioutil.ReadAll(res.Body)
|
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()
|
td()
|
||||||
}
|
}
|
||||||
|
27
go.mod
Normal file
27
go.mod
Normal file
@ -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
|
||||||
|
)
|
65
go.sum
Normal file
65
go.sum
Normal file
@ -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=
|
@ -9,8 +9,12 @@ package json
|
|||||||
import "encoding/json"
|
import "encoding/json"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Marshal = json.Marshal
|
// Marshal is exported by gin/json package.
|
||||||
|
Marshal = json.Marshal
|
||||||
|
// MarshalIndent is exported by gin/json package.
|
||||||
MarshalIndent = json.MarshalIndent
|
MarshalIndent = json.MarshalIndent
|
||||||
NewDecoder = json.NewDecoder
|
// NewDecoder is exported by gin/json package.
|
||||||
NewEncoder = json.NewEncoder
|
NewDecoder = json.NewDecoder
|
||||||
|
// NewEncoder is exported by gin/json package.
|
||||||
|
NewEncoder = json.NewEncoder
|
||||||
)
|
)
|
||||||
|
@ -9,9 +9,13 @@ package json
|
|||||||
import "github.com/json-iterator/go"
|
import "github.com/json-iterator/go"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
json = jsoniter.ConfigCompatibleWithStandardLibrary
|
json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
Marshal = json.Marshal
|
// Marshal is exported by gin/json package.
|
||||||
|
Marshal = json.Marshal
|
||||||
|
// MarshalIndent is exported by gin/json package.
|
||||||
MarshalIndent = json.MarshalIndent
|
MarshalIndent = json.MarshalIndent
|
||||||
NewDecoder = json.NewDecoder
|
// NewDecoder is exported by gin/json package.
|
||||||
NewEncoder = json.NewEncoder
|
NewDecoder = json.NewDecoder
|
||||||
|
// NewEncoder is exported by gin/json package.
|
||||||
|
NewEncoder = json.NewEncoder
|
||||||
)
|
)
|
||||||
|
13
mode.go
13
mode.go
@ -11,12 +11,16 @@ import (
|
|||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ENV_GIN_MODE indicates environment name for gin mode.
|
||||||
const ENV_GIN_MODE = "GIN_MODE"
|
const ENV_GIN_MODE = "GIN_MODE"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DebugMode = "debug"
|
// DebugMode indicates gin mode is debug.
|
||||||
|
DebugMode = "debug"
|
||||||
|
// ReleaseMode indicates gin mode is relase.
|
||||||
ReleaseMode = "release"
|
ReleaseMode = "release"
|
||||||
TestMode = "test"
|
// TestMode indicates gin mode is test.
|
||||||
|
TestMode = "test"
|
||||||
)
|
)
|
||||||
const (
|
const (
|
||||||
debugCode = iota
|
debugCode = iota
|
||||||
@ -42,6 +46,7 @@ func init() {
|
|||||||
SetMode(mode)
|
SetMode(mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMode sets gin mode according to input string.
|
||||||
func SetMode(value string) {
|
func SetMode(value string) {
|
||||||
switch value {
|
switch value {
|
||||||
case DebugMode, "":
|
case DebugMode, "":
|
||||||
@ -59,14 +64,18 @@ func SetMode(value string) {
|
|||||||
modeName = value
|
modeName = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DisableBindValidation closes the default validator.
|
||||||
func DisableBindValidation() {
|
func DisableBindValidation() {
|
||||||
binding.Validator = nil
|
binding.Validator = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableJsonDecoderUseNumber sets true for binding.EnableDecoderUseNumberto to
|
||||||
|
// call the UseNumber method on the JSON Decoder instance.
|
||||||
func EnableJsonDecoderUseNumber() {
|
func EnableJsonDecoderUseNumber() {
|
||||||
binding.EnableDecoderUseNumber = true
|
binding.EnableDecoderUseNumber = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mode returns currently gin mode.
|
||||||
func Mode() string {
|
func Mode() string {
|
||||||
return modeName
|
return modeName
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ package render
|
|||||||
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
|
// Data contains ContentType and bytes data.
|
||||||
type Data struct {
|
type Data struct {
|
||||||
ContentType string
|
ContentType string
|
||||||
Data []byte
|
Data []byte
|
||||||
@ -18,6 +19,7 @@ func (r Data) Render(w http.ResponseWriter) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (Data) writes custom ContentType.
|
||||||
func (r Data) WriteContentType(w http.ResponseWriter) {
|
func (r Data) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, []string{r.ContentType})
|
writeContentType(w, []string{r.ContentType})
|
||||||
}
|
}
|
||||||
|
@ -9,20 +9,27 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Delims represents a set of Left and Right delimiters for HTML template rendering.
|
||||||
type Delims struct {
|
type Delims struct {
|
||||||
Left string
|
// Left delimiter, defaults to {{.
|
||||||
|
Left string
|
||||||
|
// Right delimiter, defaults to }}.
|
||||||
Right string
|
Right string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTMLRender interface is to be implemented by HTMLProduction and HTMLDebug.
|
||||||
type HTMLRender interface {
|
type HTMLRender interface {
|
||||||
|
// Instance returns an HTML instance.
|
||||||
Instance(string, interface{}) Render
|
Instance(string, interface{}) Render
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTMLProduction contains template reference and its delims.
|
||||||
type HTMLProduction struct {
|
type HTMLProduction struct {
|
||||||
Template *template.Template
|
Template *template.Template
|
||||||
Delims Delims
|
Delims Delims
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTMLDebug contains template delims and pattern and function with file list.
|
||||||
type HTMLDebug struct {
|
type HTMLDebug struct {
|
||||||
Files []string
|
Files []string
|
||||||
Glob string
|
Glob string
|
||||||
@ -30,6 +37,7 @@ type HTMLDebug struct {
|
|||||||
FuncMap template.FuncMap
|
FuncMap template.FuncMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTML contains template reference and its name with given interface object.
|
||||||
type HTML struct {
|
type HTML struct {
|
||||||
Template *template.Template
|
Template *template.Template
|
||||||
Name string
|
Name string
|
||||||
@ -38,6 +46,7 @@ type HTML struct {
|
|||||||
|
|
||||||
var htmlContentType = []string{"text/html; charset=utf-8"}
|
var htmlContentType = []string{"text/html; charset=utf-8"}
|
||||||
|
|
||||||
|
// Instance (HTMLProduction) returns an HTML instance which it realizes Render interface.
|
||||||
func (r HTMLProduction) Instance(name string, data interface{}) Render {
|
func (r HTMLProduction) Instance(name string, data interface{}) Render {
|
||||||
return HTML{
|
return HTML{
|
||||||
Template: r.Template,
|
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 {
|
func (r HTMLDebug) Instance(name string, data interface{}) Render {
|
||||||
return HTML{
|
return HTML{
|
||||||
Template: r.loadTemplate(),
|
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")
|
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 {
|
func (r HTML) Render(w http.ResponseWriter) error {
|
||||||
r.WriteContentType(w)
|
r.WriteContentType(w)
|
||||||
|
|
||||||
@ -75,6 +86,7 @@ func (r HTML) Render(w http.ResponseWriter) error {
|
|||||||
return r.Template.ExecuteTemplate(w, r.Name, r.Data)
|
return r.Template.ExecuteTemplate(w, r.Name, r.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (HTML) writes HTML ContentType.
|
||||||
func (r HTML) WriteContentType(w http.ResponseWriter) {
|
func (r HTML) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, htmlContentType)
|
writeContentType(w, htmlContentType)
|
||||||
}
|
}
|
||||||
|
@ -13,34 +13,41 @@ import (
|
|||||||
"github.com/gin-gonic/gin/internal/json"
|
"github.com/gin-gonic/gin/internal/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// JSON contains the given interface object.
|
||||||
type JSON struct {
|
type JSON struct {
|
||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IndentedJSON contains the given interface object.
|
||||||
type IndentedJSON struct {
|
type IndentedJSON struct {
|
||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SecureJSON contains the given interface object and its prefix.
|
||||||
type SecureJSON struct {
|
type SecureJSON struct {
|
||||||
Prefix string
|
Prefix string
|
||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JsonpJSON contains the given interface object its callback.
|
||||||
type JsonpJSON struct {
|
type JsonpJSON struct {
|
||||||
Callback string
|
Callback string
|
||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AsciiJSON contains the given interface object.
|
||||||
type AsciiJSON struct {
|
type AsciiJSON struct {
|
||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SecureJSONPrefix is a string which represents SecureJSON prefix.
|
||||||
type SecureJSONPrefix string
|
type SecureJSONPrefix string
|
||||||
|
|
||||||
var jsonContentType = []string{"application/json; charset=utf-8"}
|
var jsonContentType = []string{"application/json; charset=utf-8"}
|
||||||
var jsonpContentType = []string{"application/javascript; charset=utf-8"}
|
var jsonpContentType = []string{"application/javascript; charset=utf-8"}
|
||||||
var jsonAsciiContentType = []string{"application/json"}
|
var jsonAsciiContentType = []string{"application/json"}
|
||||||
|
|
||||||
|
// Render (JSON) writes data with custom ContentType.
|
||||||
func (r JSON) Render(w http.ResponseWriter) (err error) {
|
func (r JSON) Render(w http.ResponseWriter) (err error) {
|
||||||
if err = WriteJSON(w, r.Data); err != nil {
|
if err = WriteJSON(w, r.Data); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -48,10 +55,12 @@ func (r JSON) Render(w http.ResponseWriter) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (JSON) writes JSON ContentType.
|
||||||
func (r JSON) WriteContentType(w http.ResponseWriter) {
|
func (r JSON) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, jsonContentType)
|
writeContentType(w, jsonContentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteJSON marshals the given interface object and writes it with custom ContentType.
|
||||||
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
|
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
|
||||||
writeContentType(w, jsonContentType)
|
writeContentType(w, jsonContentType)
|
||||||
jsonBytes, err := json.Marshal(obj)
|
jsonBytes, err := json.Marshal(obj)
|
||||||
@ -62,6 +71,7 @@ func WriteJSON(w http.ResponseWriter, obj interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType.
|
||||||
func (r IndentedJSON) Render(w http.ResponseWriter) error {
|
func (r IndentedJSON) Render(w http.ResponseWriter) error {
|
||||||
r.WriteContentType(w)
|
r.WriteContentType(w)
|
||||||
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
|
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
|
||||||
@ -72,10 +82,12 @@ func (r IndentedJSON) Render(w http.ResponseWriter) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (IndentedJSON) writes JSON ContentType.
|
||||||
func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
|
func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, jsonContentType)
|
writeContentType(w, jsonContentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType.
|
||||||
func (r SecureJSON) Render(w http.ResponseWriter) error {
|
func (r SecureJSON) Render(w http.ResponseWriter) error {
|
||||||
r.WriteContentType(w)
|
r.WriteContentType(w)
|
||||||
jsonBytes, err := json.Marshal(r.Data)
|
jsonBytes, err := json.Marshal(r.Data)
|
||||||
@ -90,10 +102,12 @@ func (r SecureJSON) Render(w http.ResponseWriter) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (SecureJSON) writes JSON ContentType.
|
||||||
func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
|
func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, jsonContentType)
|
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) {
|
func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
|
||||||
r.WriteContentType(w)
|
r.WriteContentType(w)
|
||||||
ret, err := json.Marshal(r.Data)
|
ret, err := json.Marshal(r.Data)
|
||||||
@ -115,10 +129,12 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (JsonpJSON) writes Javascript ContentType.
|
||||||
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
|
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, jsonpContentType)
|
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) {
|
func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
|
||||||
r.WriteContentType(w)
|
r.WriteContentType(w)
|
||||||
ret, err := json.Marshal(r.Data)
|
ret, err := json.Marshal(r.Data)
|
||||||
@ -139,6 +155,7 @@ func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (AsciiJSON) writes JSON ContentType.
|
||||||
func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
|
func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, jsonAsciiContentType)
|
writeContentType(w, jsonAsciiContentType)
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,12 @@ import (
|
|||||||
"github.com/gin-gonic/gin/internal/json"
|
"github.com/gin-gonic/gin/internal/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PureJSON contains the given interface object.
|
||||||
type PureJSON struct {
|
type PureJSON struct {
|
||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render (PureJSON) writes custom ContentType and encodes the given interface object.
|
||||||
func (r PureJSON) Render(w http.ResponseWriter) error {
|
func (r PureJSON) Render(w http.ResponseWriter) error {
|
||||||
r.WriteContentType(w)
|
r.WriteContentType(w)
|
||||||
encoder := json.NewEncoder(w)
|
encoder := json.NewEncoder(w)
|
||||||
@ -23,6 +25,7 @@ func (r PureJSON) Render(w http.ResponseWriter) error {
|
|||||||
return encoder.Encode(r.Data)
|
return encoder.Encode(r.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (PureJSON) writes custom ContentType.
|
||||||
func (r PureJSON) WriteContentType(w http.ResponseWriter) {
|
func (r PureJSON) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, jsonContentType)
|
writeContentType(w, jsonContentType)
|
||||||
}
|
}
|
||||||
|
@ -10,20 +10,24 @@ import (
|
|||||||
"github.com/ugorji/go/codec"
|
"github.com/ugorji/go/codec"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MsgPack contains the given interface object.
|
||||||
type MsgPack struct {
|
type MsgPack struct {
|
||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
|
var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
|
||||||
|
|
||||||
|
// WriteContentType (MsgPack) writes MsgPack ContentType.
|
||||||
func (r MsgPack) WriteContentType(w http.ResponseWriter) {
|
func (r MsgPack) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, msgpackContentType)
|
writeContentType(w, msgpackContentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render (MsgPack) encodes the given interface object and writes data with custom ContentType.
|
||||||
func (r MsgPack) Render(w http.ResponseWriter) error {
|
func (r MsgPack) Render(w http.ResponseWriter) error {
|
||||||
return WriteMsgPack(w, r.Data)
|
return WriteMsgPack(w, r.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteMsgPack writes MsgPack ContentType and encodes the given interface object.
|
||||||
func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
|
func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
|
||||||
writeContentType(w, msgpackContentType)
|
writeContentType(w, msgpackContentType)
|
||||||
var mh codec.MsgpackHandle
|
var mh codec.MsgpackHandle
|
||||||
|
@ -10,12 +10,14 @@ import (
|
|||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ProtoBuf contains the given interface object.
|
||||||
type ProtoBuf struct {
|
type ProtoBuf struct {
|
||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var protobufContentType = []string{"application/x-protobuf"}
|
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 {
|
func (r ProtoBuf) Render(w http.ResponseWriter) error {
|
||||||
r.WriteContentType(w)
|
r.WriteContentType(w)
|
||||||
|
|
||||||
@ -28,6 +30,7 @@ func (r ProtoBuf) Render(w http.ResponseWriter) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (ProtoBuf) writes ProtoBuf ContentType.
|
||||||
func (r ProtoBuf) WriteContentType(w http.ResponseWriter) {
|
func (r ProtoBuf) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, protobufContentType)
|
writeContentType(w, protobufContentType)
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Reader contains the IO reader and its length, and custom ContentType and other headers.
|
||||||
type Reader struct {
|
type Reader struct {
|
||||||
ContentType string
|
ContentType string
|
||||||
ContentLength int64
|
ContentLength int64
|
||||||
@ -26,10 +27,12 @@ func (r Reader) Render(w http.ResponseWriter) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (Reader) writes custom ContentType.
|
||||||
func (r Reader) WriteContentType(w http.ResponseWriter) {
|
func (r Reader) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, []string{r.ContentType})
|
writeContentType(w, []string{r.ContentType})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeHeaders writes custom Header.
|
||||||
func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) {
|
func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) {
|
||||||
header := w.Header()
|
header := w.Header()
|
||||||
for k, v := range headers {
|
for k, v := range headers {
|
||||||
|
@ -9,12 +9,14 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Redirect contains the http request reference and redirects status code and location.
|
||||||
type Redirect struct {
|
type Redirect struct {
|
||||||
Code int
|
Code int
|
||||||
Request *http.Request
|
Request *http.Request
|
||||||
Location string
|
Location string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render (Redirect) redirects the http request to new location and writes redirect response.
|
||||||
func (r Redirect) Render(w http.ResponseWriter) error {
|
func (r Redirect) Render(w http.ResponseWriter) error {
|
||||||
// todo(thinkerou): go1.6 not support StatusPermanentRedirect(308)
|
// todo(thinkerou): go1.6 not support StatusPermanentRedirect(308)
|
||||||
// when we upgrade go version we can use http.StatusPermanentRedirect
|
// when we upgrade go version we can use http.StatusPermanentRedirect
|
||||||
@ -25,4 +27,5 @@ func (r Redirect) Render(w http.ResponseWriter) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (Redirect) don't write any ContentType.
|
||||||
func (r Redirect) WriteContentType(http.ResponseWriter) {}
|
func (r Redirect) WriteContentType(http.ResponseWriter) {}
|
||||||
|
@ -6,8 +6,11 @@ package render
|
|||||||
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
|
// Render interface is to be implemented by JSON, XML, HTML, YAML and so on.
|
||||||
type Render interface {
|
type Render interface {
|
||||||
|
// Render writes data with custom ContentType.
|
||||||
Render(http.ResponseWriter) error
|
Render(http.ResponseWriter) error
|
||||||
|
// WriteContentType writes custom ContentType.
|
||||||
WriteContentType(w http.ResponseWriter)
|
WriteContentType(w http.ResponseWriter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +287,7 @@ func TestRenderProtoBuf(t *testing.T) {
|
|||||||
err = (ProtoBuf{data}).Render(w)
|
err = (ProtoBuf{data}).Render(w)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
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"))
|
assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// String contains the given interface object slice and its format.
|
||||||
type String struct {
|
type String struct {
|
||||||
Format string
|
Format string
|
||||||
Data []interface{}
|
Data []interface{}
|
||||||
@ -17,15 +18,18 @@ type String struct {
|
|||||||
|
|
||||||
var plainContentType = []string{"text/plain; charset=utf-8"}
|
var plainContentType = []string{"text/plain; charset=utf-8"}
|
||||||
|
|
||||||
|
// Render (String) writes data with custom ContentType.
|
||||||
func (r String) Render(w http.ResponseWriter) error {
|
func (r String) Render(w http.ResponseWriter) error {
|
||||||
WriteString(w, r.Format, r.Data)
|
WriteString(w, r.Format, r.Data)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (String) writes Plain ContentType.
|
||||||
func (r String) WriteContentType(w http.ResponseWriter) {
|
func (r String) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, plainContentType)
|
writeContentType(w, plainContentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteString writes data according to its format and write custom ContentType.
|
||||||
func WriteString(w http.ResponseWriter, format string, data []interface{}) {
|
func WriteString(w http.ResponseWriter, format string, data []interface{}) {
|
||||||
writeContentType(w, plainContentType)
|
writeContentType(w, plainContentType)
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
|
@ -9,17 +9,20 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// XML contains the given interface object.
|
||||||
type XML struct {
|
type XML struct {
|
||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var xmlContentType = []string{"application/xml; charset=utf-8"}
|
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 {
|
func (r XML) Render(w http.ResponseWriter) error {
|
||||||
r.WriteContentType(w)
|
r.WriteContentType(w)
|
||||||
return xml.NewEncoder(w).Encode(r.Data)
|
return xml.NewEncoder(w).Encode(r.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (XML) writes XML ContentType for response.
|
||||||
func (r XML) WriteContentType(w http.ResponseWriter) {
|
func (r XML) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, xmlContentType)
|
writeContentType(w, xmlContentType)
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,14 @@ import (
|
|||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// YAML contains the given interface object.
|
||||||
type YAML struct {
|
type YAML struct {
|
||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
|
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 {
|
func (r YAML) Render(w http.ResponseWriter) error {
|
||||||
r.WriteContentType(w)
|
r.WriteContentType(w)
|
||||||
|
|
||||||
@ -28,6 +30,7 @@ func (r YAML) Render(w http.ResponseWriter) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteContentType (YAML) writes YAML ContentType for response.
|
||||||
func (r YAML) WriteContentType(w http.ResponseWriter) {
|
func (r YAML) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, yamlContentType)
|
writeContentType(w, yamlContentType)
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IRouter defines all router handle interface includes single and group router.
|
||||||
type IRouter interface {
|
type IRouter interface {
|
||||||
IRoutes
|
IRoutes
|
||||||
Group(string, ...HandlerFunc) *RouterGroup
|
Group(string, ...HandlerFunc) *RouterGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IRoutes defines all router handle interface.
|
||||||
type IRoutes interface {
|
type IRoutes interface {
|
||||||
Use(...HandlerFunc) IRoutes
|
Use(...HandlerFunc) IRoutes
|
||||||
|
|
||||||
@ -34,8 +36,8 @@ type IRoutes interface {
|
|||||||
StaticFS(string, http.FileSystem) IRoutes
|
StaticFS(string, http.FileSystem) IRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
// and an array of handlers (middleware).
|
// a prefix and an array of handlers (middleware).
|
||||||
type RouterGroup struct {
|
type RouterGroup struct {
|
||||||
Handlers HandlersChain
|
Handlers HandlersChain
|
||||||
basePath string
|
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 {
|
func (group *RouterGroup) BasePath() string {
|
||||||
return group.basePath
|
return group.basePath
|
||||||
}
|
}
|
||||||
|
11
tree.go
11
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 +
|
"' conflicts with existing wildcard '" + n.path +
|
||||||
"' in path '" + fullPath + "'")
|
"' in existing prefix '" + prefix +
|
||||||
|
"'")
|
||||||
}
|
}
|
||||||
|
|
||||||
c := path[0]
|
c := path[0]
|
||||||
|
53
tree_test.go
53
tree_test.go
@ -5,7 +5,9 @@
|
|||||||
package gin
|
package gin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -125,8 +127,6 @@ func TestTreeAddAndGet(t *testing.T) {
|
|||||||
tree.addRoute(route, fakeHandler(route))
|
tree.addRoute(route, fakeHandler(route))
|
||||||
}
|
}
|
||||||
|
|
||||||
//printChildren(tree, "")
|
|
||||||
|
|
||||||
checkRequests(t, tree, testRequests{
|
checkRequests(t, tree, testRequests{
|
||||||
{"/a", false, "/a", nil},
|
{"/a", false, "/a", nil},
|
||||||
{"/", true, "", nil},
|
{"/", true, "", nil},
|
||||||
@ -168,8 +168,6 @@ func TestTreeWildcard(t *testing.T) {
|
|||||||
tree.addRoute(route, fakeHandler(route))
|
tree.addRoute(route, fakeHandler(route))
|
||||||
}
|
}
|
||||||
|
|
||||||
//printChildren(tree, "")
|
|
||||||
|
|
||||||
checkRequests(t, tree, testRequests{
|
checkRequests(t, tree, testRequests{
|
||||||
{"/", false, "/", nil},
|
{"/", false, "/", nil},
|
||||||
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}},
|
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}},
|
||||||
@ -208,7 +206,6 @@ func TestUnescapeParameters(t *testing.T) {
|
|||||||
tree.addRoute(route, fakeHandler(route))
|
tree.addRoute(route, fakeHandler(route))
|
||||||
}
|
}
|
||||||
|
|
||||||
//printChildren(tree, "")
|
|
||||||
unescape := true
|
unescape := true
|
||||||
checkRequests(t, tree, testRequests{
|
checkRequests(t, tree, testRequests{
|
||||||
{"/", false, "/", nil},
|
{"/", false, "/", nil},
|
||||||
@ -260,8 +257,6 @@ func testRoutes(t *testing.T, routes []testRoute) {
|
|||||||
t.Errorf("unexpected panic for route '%s': %v", route.path, recv)
|
t.Errorf("unexpected panic for route '%s': %v", route.path, recv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//printChildren(tree, "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTreeWildcardConflict(t *testing.T) {
|
func TestTreeWildcardConflict(t *testing.T) {
|
||||||
@ -328,8 +323,6 @@ func TestTreeDupliatePath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//printChildren(tree, "")
|
|
||||||
|
|
||||||
checkRequests(t, tree, testRequests{
|
checkRequests(t, tree, testRequests{
|
||||||
{"/", false, "/", nil},
|
{"/", false, "/", nil},
|
||||||
{"/doc/", false, "/doc/", nil},
|
{"/doc/", false, "/doc/", nil},
|
||||||
@ -444,8 +437,6 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//printChildren(tree, "")
|
|
||||||
|
|
||||||
tsrRoutes := [...]string{
|
tsrRoutes := [...]string{
|
||||||
"/hi/",
|
"/hi/",
|
||||||
"/b",
|
"/b",
|
||||||
@ -664,3 +655,43 @@ func TestTreeInvalidNodeType(t *testing.T) {
|
|||||||
t.Fatalf("Expected panic '"+panicMsg+"', got '%v'", recv)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
8
utils.go
8
utils.go
@ -14,8 +14,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BindKey indicates a default bind key.
|
||||||
const BindKey = "_gin-gonic/gin/bindkey"
|
const BindKey = "_gin-gonic/gin/bindkey"
|
||||||
|
|
||||||
|
// Bind is a helper function for given interface object and returns a Gin middleware.
|
||||||
func Bind(val interface{}) HandlerFunc {
|
func Bind(val interface{}) HandlerFunc {
|
||||||
value := reflect.ValueOf(val)
|
value := reflect.ValueOf(val)
|
||||||
if value.Kind() == reflect.Ptr {
|
if value.Kind() == reflect.Ptr {
|
||||||
@ -33,16 +35,14 @@ func Bind(val interface{}) HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WrapF is a helper function for wrapping http.HandlerFunc
|
// WrapF is a helper function for wrapping http.HandlerFunc and returns a Gin middleware.
|
||||||
// Returns a Gin middleware
|
|
||||||
func WrapF(f http.HandlerFunc) HandlerFunc {
|
func WrapF(f http.HandlerFunc) HandlerFunc {
|
||||||
return func(c *Context) {
|
return func(c *Context) {
|
||||||
f(c.Writer, c.Request)
|
f(c.Writer, c.Request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WrapH is a helper function for wrapping http.Handler
|
// WrapH is a helper function for wrapping http.Handler and returns a Gin middleware.
|
||||||
// Returns a Gin middleware
|
|
||||||
func WrapH(h http.Handler) HandlerFunc {
|
func WrapH(h http.Handler) HandlerFunc {
|
||||||
return func(c *Context) {
|
return func(c *Context) {
|
||||||
h.ServeHTTP(c.Writer, c.Request)
|
h.ServeHTTP(c.Writer, c.Request)
|
||||||
|
8
version.go
Normal file
8
version.go
Normal file
@ -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"
|
Loading…
x
Reference in New Issue
Block a user