mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-23 01:57:55 +08:00
Merge branch 'master' into master
This commit is contained in:
commit
c236352b54
44
README.md
44
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)
|
||||||
|
|
||||||
@ -1836,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.
|
||||||
|
@ -713,6 +713,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)
|
||||||
|
|
||||||
@ -835,6 +836,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()
|
||||||
@ -856,6 +858,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
|
||||||
@ -865,6 +868,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:
|
||||||
@ -884,6 +888,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")
|
||||||
|
|
||||||
@ -903,6 +908,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}}`))
|
||||||
|
re := captureOutput(func() {
|
||||||
|
SetMode(DebugMode)
|
||||||
router.SetHTMLTemplate(templ)
|
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"})
|
||||||
|
|
||||||
|
14
debug.go
14
debug.go
@ -6,25 +6,27 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
if DebugPrintRouteFunc == nil {
|
||||||
debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
|
debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
|
||||||
|
} else {
|
||||||
|
DebugPrintRouteFunc(httpMethod, absolutePath, handlerName, nuHandlers)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +44,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...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
110
debug_test.go
110
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)
|
SetMode(ReleaseMode)
|
||||||
debugPrint("DEBUG this!")
|
debugPrint("DEBUG this!")
|
||||||
SetMode(TestMode)
|
SetMode(TestMode)
|
||||||
debugPrint("DEBUG this!")
|
debugPrint("DEBUG this!")
|
||||||
assert.Empty(t, w.String())
|
|
||||||
|
|
||||||
SetMode(DebugMode)
|
SetMode(DebugMode)
|
||||||
debugPrint("these are %d %s\n", 2, "error messages")
|
debugPrint("these are %d %s\n", 2, "error messages")
|
||||||
assert.Equal(t, "[GIN-debug] these are 2 error messages\n", w.String())
|
SetMode(TestMode)
|
||||||
|
})
|
||||||
|
assert.Equal(t, "[GIN-debug] these are 2 error messages\n", re)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDebugPrintError(t *testing.T) {
|
func TestDebugPrintError(t *testing.T) {
|
||||||
var w bytes.Buffer
|
re := captureOutput(func() {
|
||||||
setup(&w)
|
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
SetMode(DebugMode)
|
SetMode(DebugMode)
|
||||||
debugPrintError(nil)
|
debugPrintError(nil)
|
||||||
assert.Empty(t, w.String())
|
|
||||||
|
|
||||||
debugPrintError(errors.New("this is an error"))
|
debugPrintError(errors.New("this is an error"))
|
||||||
assert.Equal(t, "[GIN-debug] [ERROR] this is an error\n", w.String())
|
SetMode(TestMode)
|
||||||
|
})
|
||||||
|
assert.Equal(t, "[GIN-debug] [ERROR] this is an error\n", re)
|
||||||
}
|
}
|
||||||
|
|
||||||
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})
|
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())
|
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) {
|
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"))
|
templ := template.Must(template.New("").Delims("{[{", "}]}").ParseGlob("./testdata/template/hello.tmpl"))
|
||||||
debugPrintLoadTemplate(templ)
|
debugPrintLoadTemplate(templ)
|
||||||
assert.Regexp(t, `^\[GIN-debug\] Loaded HTML Templates \(2\): \n(\t- \n|\t- hello\.tmpl\n){2}\n`, w.String())
|
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) {
|
func TestDebugPrintWARNINGSetHTMLTemplate(t *testing.T) {
|
||||||
var w bytes.Buffer
|
re := captureOutput(func() {
|
||||||
setup(&w)
|
SetMode(DebugMode)
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
debugPrintWARNINGSetHTMLTemplate()
|
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())
|
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) {
|
func TestDebugPrintWARNINGDefault(t *testing.T) {
|
||||||
var w bytes.Buffer
|
re := captureOutput(func() {
|
||||||
setup(&w)
|
SetMode(DebugMode)
|
||||||
defer teardown()
|
|
||||||
|
|
||||||
debugPrintWARNINGDefault()
|
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())
|
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) {
|
func TestDebugPrintWARNINGNew(t *testing.T) {
|
||||||
var w bytes.Buffer
|
re := captureOutput(func() {
|
||||||
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())
|
|
||||||
}
|
|
||||||
|
|
||||||
func setup(w io.Writer) {
|
|
||||||
SetMode(DebugMode)
|
SetMode(DebugMode)
|
||||||
log.SetOutput(w)
|
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 teardown() {
|
func captureOutput(f func()) string {
|
||||||
SetMode(TestMode)
|
reader, writer, err := os.Pipe()
|
||||||
log.SetOutput(os.Stdout)
|
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
|
||||||
}
|
}
|
||||||
|
18
errors.go
18
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 indicates a public error.
|
||||||
ErrorTypePublic ErrorType = 1 << 1
|
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())
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ func ConfigRuntime() {
|
|||||||
fmt.Printf("Running with %d CPUs\n", nuCPU)
|
fmt.Printf("Running with %d CPUs\n", nuCPU)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartWrokers start starsWorker by goroutine.
|
// StartWorkers start starsWorker by goroutine.
|
||||||
func StartWorkers() {
|
func StartWorkers() {
|
||||||
go statsWorker()
|
go statsWorker()
|
||||||
}
|
}
|
||||||
|
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
|
||||||
|
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
mode.go
9
mode.go
@ -11,11 +11,15 @@ 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 indicates gin mode is debug.
|
||||||
DebugMode = "debug"
|
DebugMode = "debug"
|
||||||
|
// ReleaseMode indicates gin mode is relase.
|
||||||
ReleaseMode = "release"
|
ReleaseMode = "release"
|
||||||
|
// TestMode indicates gin mode is test.
|
||||||
TestMode = "test"
|
TestMode = "test"
|
||||||
)
|
)
|
||||||
const (
|
const (
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
|
@ -46,7 +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..
|
// 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,
|
||||||
@ -55,7 +55,7 @@ func (r HTMLProduction) Instance(name string, data interface{}) Render {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instance (HTMLDebug) returns an HTML instance which it realizes Render interface..
|
// 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(),
|
||||||
|
@ -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 defins 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
|
||||||
}
|
}
|
||||||
|
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