Merge branch 'master' into hr-tree

This commit is contained in:
Bo-Yi Wu 2018-09-21 10:03:05 +08:00 committed by GitHub
commit f4819c9e3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 402 additions and 156 deletions

View File

@ -3,8 +3,8 @@
- Please provide source code and commit sha if you found a bug.
- Review existing issues and provide feedback or react to them.
- go version:
- gin version (or commit ref):
- git version:
- operating system:
## Description

View File

@ -7,6 +7,11 @@ go:
- 1.9.x
- 1.10.x
- 1.11.x
- master
matrix:
allow_failures:
- go: master
git:
depth: 10

View File

@ -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)
- [Try to bind body into different structs](#try-to-bind-body-into-different-structs)
- [http2 server push](#http2-server-push)
- [Define format for the log of routes](#define-format-for-the-log-of-routes)
- [Testing](#testing)
- [Users](#users)
@ -657,6 +658,7 @@ import (
"gopkg.in/go-playground/validator.v8"
)
// Booking contains binded and validated data.
type Booking struct {
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
@ -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
The `net/http/httptest` package is preferable way for HTTP testing.
@ -1885,5 +1930,6 @@ func TestPingRoute(t *testing.T) {
Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework.
* [drone](https://github.com/drone/drone): Drone is a Continuous Delivery platform built on Docker, written in Go
* [drone](https://github.com/drone/drone): Drone is a Continuous Delivery platform built on Docker, written in Go.
* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go.
* [fnproject](https://github.com/fnproject/fn): The container native, cloud agnostic serverless platform.

View File

@ -711,6 +711,7 @@ func (c *Context) Cookie(name string) (string, error) {
return val, nil
}
// Render writes the response headers and calls render.Render to render data.
func (c *Context) Render(code int, r render.Render) {
c.Status(code)
@ -833,6 +834,7 @@ func (c *Context) SSEvent(name string, message interface{}) {
})
}
// Stream sends a streaming response.
func (c *Context) Stream(step func(w io.Writer) bool) {
w := c.Writer
clientGone := w.CloseNotify()
@ -854,6 +856,7 @@ func (c *Context) Stream(step func(w io.Writer) bool) {
/******** CONTENT NEGOTIATION *******/
/************************************/
// Negotiate contains all negotiations data.
type Negotiate struct {
Offered []string
HTMLName string
@ -863,6 +866,7 @@ type Negotiate struct {
Data interface{}
}
// Negotiate calls different Render according acceptable Accept format.
func (c *Context) Negotiate(code int, config Negotiate) {
switch c.NegotiateFormat(config.Offered...) {
case binding.MIMEJSON:
@ -882,6 +886,7 @@ func (c *Context) Negotiate(code int, config Negotiate) {
}
}
// NegotiateFormat returns an acceptable Accept format.
func (c *Context) NegotiateFormat(offered ...string) string {
assert1(len(offered) > 0, "you must provide at least one offer")
@ -901,6 +906,7 @@ func (c *Context) NegotiateFormat(offered ...string) string {
return ""
}
// SetAccepted sets Accept header data.
func (c *Context) SetAccepted(formats ...string) {
c.Accepted = formats
}

View File

@ -784,14 +784,14 @@ func TestContextRenderHTML2(t *testing.T) {
router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})
assert.Len(t, router.trees, 1)
var b bytes.Buffer
setup(&b)
defer teardown()
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
router.SetHTMLTemplate(templ)
re := captureOutput(func() {
SetMode(DebugMode)
router.SetHTMLTemplate(templ)
SetMode(TestMode)
})
assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", b.String())
assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", re)
c.HTML(http.StatusCreated, "t", H{"name": "alexandernyquist"})
@ -978,7 +978,7 @@ func TestContextRenderProtoBuf(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, string(protoData[:]), w.Body.String())
assert.Equal(t, string(protoData), w.Body.String())
assert.Equal(t, "application/x-protobuf", w.HeaderMap.Get("Content-Type"))
}

View File

@ -6,25 +6,28 @@ package gin
import (
"bytes"
"fmt"
"html/template"
"log"
)
func init() {
log.SetFlags(0)
}
// IsDebugging returns true if the framework is running in debug mode.
// Use SetMode(gin.ReleaseMode) to disable debug mode.
func IsDebugging() bool {
return ginMode == debugCode
}
// DebugPrintRouteFunc indicates debug log output format.
var DebugPrintRouteFunc func(httpMethod, absolutePath, handlerName string, nuHandlers int)
func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) {
if IsDebugging() {
nuHandlers := len(handlers)
handlerName := nameOfFunction(handlers.Last())
debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
if DebugPrintRouteFunc == nil {
debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers)
} else {
DebugPrintRouteFunc(httpMethod, absolutePath, handlerName, nuHandlers)
}
}
}
@ -42,7 +45,7 @@ func debugPrintLoadTemplate(tmpl *template.Template) {
func debugPrint(format string, values ...interface{}) {
if IsDebugging() {
log.Printf("[GIN-debug] "+format, values...)
fmt.Printf("[GIN-debug] "+format, values...)
}
}

View File

@ -11,6 +11,7 @@ import (
"io"
"log"
"os"
"sync"
"testing"
"github.com/stretchr/testify/assert"
@ -30,86 +31,101 @@ func TestIsDebugging(t *testing.T) {
}
func TestDebugPrint(t *testing.T) {
var w bytes.Buffer
setup(&w)
defer teardown()
SetMode(ReleaseMode)
debugPrint("DEBUG this!")
SetMode(TestMode)
debugPrint("DEBUG this!")
assert.Empty(t, w.String())
SetMode(DebugMode)
debugPrint("these are %d %s\n", 2, "error messages")
assert.Equal(t, "[GIN-debug] these are 2 error messages\n", w.String())
re := captureOutput(func() {
SetMode(DebugMode)
SetMode(ReleaseMode)
debugPrint("DEBUG this!")
SetMode(TestMode)
debugPrint("DEBUG this!")
SetMode(DebugMode)
debugPrint("these are %d %s\n", 2, "error messages")
SetMode(TestMode)
})
assert.Equal(t, "[GIN-debug] these are 2 error messages\n", re)
}
func TestDebugPrintError(t *testing.T) {
var w bytes.Buffer
setup(&w)
defer teardown()
SetMode(DebugMode)
debugPrintError(nil)
assert.Empty(t, w.String())
debugPrintError(errors.New("this is an error"))
assert.Equal(t, "[GIN-debug] [ERROR] this is an error\n", w.String())
re := captureOutput(func() {
SetMode(DebugMode)
debugPrintError(nil)
debugPrintError(errors.New("this is an error"))
SetMode(TestMode)
})
assert.Equal(t, "[GIN-debug] [ERROR] this is an error\n", re)
}
func TestDebugPrintRoutes(t *testing.T) {
var w bytes.Buffer
setup(&w)
defer teardown()
debugPrintRoute("GET", "/path/to/route/:param", HandlersChain{func(c *Context) {}, handlerNameTest})
assert.Regexp(t, `^\[GIN-debug\] GET /path/to/route/:param --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, w.String())
re := captureOutput(func() {
SetMode(DebugMode)
debugPrintRoute("GET", "/path/to/route/:param", HandlersChain{func(c *Context) {}, handlerNameTest})
SetMode(TestMode)
})
assert.Regexp(t, `^\[GIN-debug\] GET /path/to/route/:param --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, re)
}
func TestDebugPrintLoadTemplate(t *testing.T) {
var w bytes.Buffer
setup(&w)
defer teardown()
templ := template.Must(template.New("").Delims("{[{", "}]}").ParseGlob("./testdata/template/hello.tmpl"))
debugPrintLoadTemplate(templ)
assert.Regexp(t, `^\[GIN-debug\] Loaded HTML Templates \(2\): \n(\t- \n|\t- hello\.tmpl\n){2}\n`, w.String())
re := captureOutput(func() {
SetMode(DebugMode)
templ := template.Must(template.New("").Delims("{[{", "}]}").ParseGlob("./testdata/template/hello.tmpl"))
debugPrintLoadTemplate(templ)
SetMode(TestMode)
})
assert.Regexp(t, `^\[GIN-debug\] Loaded HTML Templates \(2\): \n(\t- \n|\t- hello\.tmpl\n){2}\n`, re)
}
func TestDebugPrintWARNINGSetHTMLTemplate(t *testing.T) {
var w bytes.Buffer
setup(&w)
defer teardown()
debugPrintWARNINGSetHTMLTemplate()
assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", w.String())
re := captureOutput(func() {
SetMode(DebugMode)
debugPrintWARNINGSetHTMLTemplate()
SetMode(TestMode)
})
assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", re)
}
func TestDebugPrintWARNINGDefault(t *testing.T) {
var w bytes.Buffer
setup(&w)
defer teardown()
debugPrintWARNINGDefault()
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", w.String())
re := captureOutput(func() {
SetMode(DebugMode)
debugPrintWARNINGDefault()
SetMode(TestMode)
})
assert.Equal(t, "[GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon.\n\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n", re)
}
func TestDebugPrintWARNINGNew(t *testing.T) {
var w bytes.Buffer
setup(&w)
defer teardown()
debugPrintWARNINGNew()
assert.Equal(t, "[GIN-debug] [WARNING] Running in \"debug\" mode. Switch to \"release\" mode in production.\n - using env:\texport GIN_MODE=release\n - using code:\tgin.SetMode(gin.ReleaseMode)\n\n", w.String())
re := captureOutput(func() {
SetMode(DebugMode)
debugPrintWARNINGNew()
SetMode(TestMode)
})
assert.Equal(t, "[GIN-debug] [WARNING] Running in \"debug\" mode. Switch to \"release\" mode in production.\n - using env:\texport GIN_MODE=release\n - using code:\tgin.SetMode(gin.ReleaseMode)\n\n", re)
}
func setup(w io.Writer) {
SetMode(DebugMode)
log.SetOutput(w)
}
func teardown() {
SetMode(TestMode)
log.SetOutput(os.Stdout)
func captureOutput(f func()) string {
reader, writer, err := os.Pipe()
if err != nil {
panic(err)
}
stdout := os.Stdout
stderr := os.Stderr
defer func() {
os.Stdout = stdout
os.Stderr = stderr
log.SetOutput(os.Stderr)
}()
os.Stdout = writer
os.Stderr = writer
log.SetOutput(writer)
out := make(chan string)
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
var buf bytes.Buffer
wg.Done()
io.Copy(&buf, reader)
out <- buf.String()
}()
wg.Wait()
f()
writer.Close()
return <-out
}

View File

@ -12,18 +12,24 @@ import (
"github.com/gin-gonic/gin/internal/json"
)
// ErrorType is an unsigned 64-bit error code as defined in the gin spec.
type ErrorType uint64
const (
ErrorTypeBind ErrorType = 1 << 63 // used when c.Bind() fails
ErrorTypeRender ErrorType = 1 << 62 // used when c.Render() fails
// ErrorTypeBind is used when Context.Bind() fails.
ErrorTypeBind ErrorType = 1 << 63
// ErrorTypeRender is used when Context.Render() fails.
ErrorTypeRender ErrorType = 1 << 62
// ErrorTypePrivate indicates a private error.
ErrorTypePrivate ErrorType = 1 << 0
ErrorTypePublic ErrorType = 1 << 1
// ErrorTypePublic indicates a public error.
ErrorTypePublic ErrorType = 1 << 1
// ErrorTypeAny indicates other any error.
ErrorTypeAny ErrorType = 1<<64 - 1
ErrorTypeNu = 2
)
// Error represents a error's specification.
type Error struct {
Err error
Type ErrorType
@ -34,11 +40,13 @@ type errorMsgs []*Error
var _ error = &Error{}
// SetType sets the error's type.
func (msg *Error) SetType(flags ErrorType) *Error {
msg.Type = flags
return msg
}
// SetMeta sets the error's meta data.
func (msg *Error) SetMeta(data interface{}) *Error {
msg.Meta = data
return msg
@ -70,11 +78,12 @@ func (msg *Error) MarshalJSON() ([]byte, error) {
return json.Marshal(msg.JSON())
}
// Error implements the error interface
// Error implements the error interface.
func (msg Error) Error() string {
return msg.Err.Error()
}
// IsType judges one error.
func (msg *Error) IsType(flags ErrorType) bool {
return (msg.Type & flags) > 0
}
@ -138,6 +147,7 @@ func (a errorMsgs) JSON() interface{} {
}
}
// MarshalJSON implements the json.Marshaller interface.
func (a errorMsgs) MarshalJSON() ([]byte, error) {
return json.Marshal(a.JSON())
}

View File

@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
)
var DB = make(map[string]string)
var db = make(map[string]string)
func setupRouter() *gin.Engine {
// Disable Console Color
@ -21,7 +21,7 @@ func setupRouter() *gin.Engine {
// Get user value
r.GET("/user/:name", func(c *gin.Context) {
user := c.Params.ByName("name")
value, ok := DB[user]
value, ok := db[user]
if ok {
c.JSON(http.StatusOK, gin.H{"user": user, "value": value})
} else {
@ -50,7 +50,7 @@ func setupRouter() *gin.Engine {
}
if c.Bind(&json) == nil {
DB[user] = json.Value
db[user] = json.Value
c.JSON(http.StatusOK, gin.H{"status": "ok"})
}
})

View File

@ -10,6 +10,7 @@ import (
"gopkg.in/go-playground/validator.v8"
)
// Booking contains binded and validated data.
type Booking struct {
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`

View File

@ -13,16 +13,19 @@ func main() {
StartGin()
}
// ConfigRuntime sets the number of operating system threads.
func ConfigRuntime() {
nuCPU := runtime.NumCPU()
runtime.GOMAXPROCS(nuCPU)
fmt.Printf("Running with %d CPUs\n", nuCPU)
}
// StartWorkers start starsWorker by goroutine.
func StartWorkers() {
go statsWorker()
}
// StartGin starts gin web server with setting router.
func StartGin() {
gin.SetMode(gin.ReleaseMode)

View File

@ -50,6 +50,7 @@ func connectedUsers() uint64 {
return uint64(connected)
}
// Stats returns savedStats data.
func Stats() map[string]uint64 {
mutexStats.RLock()
defer mutexStats.RUnlock()

12
gin.go
View File

@ -14,11 +14,7 @@ import (
"github.com/gin-gonic/gin/render"
)
const (
// Version is Framework's version.
Version = "v1.3.0"
defaultMultipartMemory = 32 << 20 // 32 MB
)
const defaultMultipartMemory = 32 << 20 // 32 MB
var (
default404Body = []byte("404 page not found")
@ -26,7 +22,10 @@ var (
defaultAppEngine bool
)
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
// HandlersChain defines a HandlerFunc array.
type HandlersChain []HandlerFunc
// Last returns the last handler in the chain. ie. the last handler is the main own.
@ -37,12 +36,14 @@ func (c HandlersChain) Last() HandlerFunc {
return nil
}
// RouteInfo represents a request route's specification which contains method and path and its handler.
type RouteInfo struct {
Method string
Path string
Handler string
}
// RoutesInfo defines a RouteInfo array.
type RoutesInfo []RouteInfo
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
@ -155,6 +156,7 @@ func (engine *Engine) allocateContext() *Context {
return &Context{engine: engine}
}
// Delims sets template left and right delims and returns a Engine instance.
func (engine *Engine) Delims(left, right string) *Engine {
engine.delims = render.Delims{Left: left, Right: right}
return engine

View File

@ -22,14 +22,17 @@ func engine() *gin.Engine {
return internalEngine
}
// LoadHTMLGlob is a wrapper for Engine.LoadHTMLGlob.
func LoadHTMLGlob(pattern string) {
engine().LoadHTMLGlob(pattern)
}
// LoadHTMLFiles is a wrapper for Engine.LoadHTMLFiles.
func LoadHTMLFiles(files ...string) {
engine().LoadHTMLFiles(files...)
}
// SetHTMLTemplate is a wrapper for Engine.SetHTMLTemplate.
func SetHTMLTemplate(templ *template.Template) {
engine().SetHTMLTemplate(templ)
}
@ -39,7 +42,7 @@ func NoRoute(handlers ...gin.HandlerFunc) {
engine().NoRoute(handlers...)
}
// NoMethod sets the handlers called when... TODO
// NoMethod is a wrapper for Engine.NoMethod.
func NoMethod(handlers ...gin.HandlerFunc) {
engine().NoMethod(handlers...)
}
@ -50,6 +53,7 @@ func Group(relativePath string, handlers ...gin.HandlerFunc) *gin.RouterGroup {
return engine().Group(relativePath, handlers...)
}
// Handle is a wrapper for Engine.Handle.
func Handle(httpMethod, relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().Handle(httpMethod, relativePath, handlers...)
}
@ -89,10 +93,12 @@ func HEAD(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().HEAD(relativePath, handlers...)
}
// Any is a wrapper for Engine.Any.
func Any(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
return engine().Any(relativePath, handlers...)
}
// StaticFile is a wrapper for Engine.StaticFile.
func StaticFile(relativePath, filepath string) gin.IRoutes {
return engine().StaticFile(relativePath, filepath)
}
@ -107,6 +113,7 @@ func Static(relativePath, root string) gin.IRoutes {
return engine().Static(relativePath, root)
}
// StaticFS is a wrapper for Engine.StaticFS.
func StaticFS(relativePath string, fs http.FileSystem) gin.IRoutes {
return engine().StaticFS(relativePath, fs)
}

View File

@ -88,7 +88,7 @@ func TestLoadHTMLGlob(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
td()
}
@ -101,7 +101,7 @@ func TestLoadHTMLGlob2(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
td()
}
@ -114,7 +114,7 @@ func TestLoadHTMLGlob3(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
td()
}
@ -134,7 +134,7 @@ func TestLoadHTMLGlobUsingTLS(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
td()
}
@ -148,7 +148,7 @@ func TestLoadHTMLGlobFromFuncMap(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "Date: 2017/07/01\n", string(resp[:]))
assert.Equal(t, "Date: 2017/07/01\n", string(resp))
td()
}
@ -187,7 +187,7 @@ func TestLoadHTMLFiles(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
td()
}
@ -199,7 +199,7 @@ func TestLoadHTMLFiles2(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
td()
}
@ -211,7 +211,7 @@ func TestLoadHTMLFiles3(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
td()
}
@ -230,7 +230,7 @@ func TestLoadHTMLFilesUsingTLS(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
assert.Equal(t, "<h1>Hello world</h1>", string(resp))
td()
}
@ -243,7 +243,7 @@ func TestLoadHTMLFilesFuncMap(t *testing.T) {
}
resp, _ := ioutil.ReadAll(res.Body)
assert.Equal(t, "Date: 2017/07/01\n", string(resp[:]))
assert.Equal(t, "Date: 2017/07/01\n", string(resp))
td()
}

27
go.mod Normal file
View 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
View 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=

View File

@ -9,8 +9,12 @@ package json
import "encoding/json"
var (
Marshal = json.Marshal
// Marshal is exported by gin/json package.
Marshal = json.Marshal
// MarshalIndent is exported by gin/json package.
MarshalIndent = json.MarshalIndent
NewDecoder = json.NewDecoder
NewEncoder = json.NewEncoder
// NewDecoder is exported by gin/json package.
NewDecoder = json.NewDecoder
// NewEncoder is exported by gin/json package.
NewEncoder = json.NewEncoder
)

View File

@ -9,9 +9,13 @@ package json
import "github.com/json-iterator/go"
var (
json = jsoniter.ConfigCompatibleWithStandardLibrary
Marshal = json.Marshal
json = jsoniter.ConfigCompatibleWithStandardLibrary
// Marshal is exported by gin/json package.
Marshal = json.Marshal
// MarshalIndent is exported by gin/json package.
MarshalIndent = json.MarshalIndent
NewDecoder = json.NewDecoder
NewEncoder = json.NewEncoder
// NewDecoder is exported by gin/json package.
NewDecoder = json.NewDecoder
// NewEncoder is exported by gin/json package.
NewEncoder = json.NewEncoder
)

13
mode.go
View File

@ -11,12 +11,16 @@ import (
"github.com/gin-gonic/gin/binding"
)
// ENV_GIN_MODE indicates environment name for gin mode.
const ENV_GIN_MODE = "GIN_MODE"
const (
DebugMode = "debug"
// DebugMode indicates gin mode is debug.
DebugMode = "debug"
// ReleaseMode indicates gin mode is relase.
ReleaseMode = "release"
TestMode = "test"
// TestMode indicates gin mode is test.
TestMode = "test"
)
const (
debugCode = iota
@ -42,6 +46,7 @@ func init() {
SetMode(mode)
}
// SetMode sets gin mode according to input string.
func SetMode(value string) {
switch value {
case DebugMode, "":
@ -59,14 +64,18 @@ func SetMode(value string) {
modeName = value
}
// DisableBindValidation closes the default validator.
func DisableBindValidation() {
binding.Validator = nil
}
// EnableJsonDecoderUseNumber sets true for binding.EnableDecoderUseNumberto to
// call the UseNumber method on the JSON Decoder instance.
func EnableJsonDecoderUseNumber() {
binding.EnableDecoderUseNumber = true
}
// Mode returns currently gin mode.
func Mode() string {
return modeName
}

View File

@ -6,6 +6,7 @@ package render
import "net/http"
// Data contains ContentType and bytes data.
type Data struct {
ContentType string
Data []byte
@ -18,6 +19,7 @@ func (r Data) Render(w http.ResponseWriter) (err error) {
return
}
// WriteContentType (Data) writes custom ContentType.
func (r Data) WriteContentType(w http.ResponseWriter) {
writeContentType(w, []string{r.ContentType})
}

View File

@ -9,20 +9,27 @@ import (
"net/http"
)
// Delims represents a set of Left and Right delimiters for HTML template rendering.
type Delims struct {
Left string
// Left delimiter, defaults to {{.
Left string
// Right delimiter, defaults to }}.
Right string
}
// HTMLRender interface is to be implemented by HTMLProduction and HTMLDebug.
type HTMLRender interface {
// Instance returns an HTML instance.
Instance(string, interface{}) Render
}
// HTMLProduction contains template reference and its delims.
type HTMLProduction struct {
Template *template.Template
Delims Delims
}
// HTMLDebug contains template delims and pattern and function with file list.
type HTMLDebug struct {
Files []string
Glob string
@ -30,6 +37,7 @@ type HTMLDebug struct {
FuncMap template.FuncMap
}
// HTML contains template reference and its name with given interface object.
type HTML struct {
Template *template.Template
Name string
@ -38,6 +46,7 @@ type HTML struct {
var htmlContentType = []string{"text/html; charset=utf-8"}
// Instance (HTMLProduction) returns an HTML instance which it realizes Render interface.
func (r HTMLProduction) Instance(name string, data interface{}) Render {
return HTML{
Template: r.Template,
@ -46,6 +55,7 @@ func (r HTMLProduction) Instance(name string, data interface{}) Render {
}
}
// Instance (HTMLDebug) returns an HTML instance which it realizes Render interface.
func (r HTMLDebug) Instance(name string, data interface{}) Render {
return HTML{
Template: r.loadTemplate(),
@ -66,6 +76,7 @@ func (r HTMLDebug) loadTemplate() *template.Template {
panic("the HTML debug render was created without files or glob pattern")
}
// Render (HTML) executes template and writes its result with custom ContentType for response.
func (r HTML) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
@ -75,6 +86,7 @@ func (r HTML) Render(w http.ResponseWriter) error {
return r.Template.ExecuteTemplate(w, r.Name, r.Data)
}
// WriteContentType (HTML) writes HTML ContentType.
func (r HTML) WriteContentType(w http.ResponseWriter) {
writeContentType(w, htmlContentType)
}

View File

@ -13,34 +13,41 @@ import (
"github.com/gin-gonic/gin/internal/json"
)
// JSON contains the given interface object.
type JSON struct {
Data interface{}
}
// IndentedJSON contains the given interface object.
type IndentedJSON struct {
Data interface{}
}
// SecureJSON contains the given interface object and its prefix.
type SecureJSON struct {
Prefix string
Data interface{}
}
// JsonpJSON contains the given interface object its callback.
type JsonpJSON struct {
Callback string
Data interface{}
}
// AsciiJSON contains the given interface object.
type AsciiJSON struct {
Data interface{}
}
// SecureJSONPrefix is a string which represents SecureJSON prefix.
type SecureJSONPrefix string
var jsonContentType = []string{"application/json; charset=utf-8"}
var jsonpContentType = []string{"application/javascript; charset=utf-8"}
var jsonAsciiContentType = []string{"application/json"}
// Render (JSON) writes data with custom ContentType.
func (r JSON) Render(w http.ResponseWriter) (err error) {
if err = WriteJSON(w, r.Data); err != nil {
panic(err)
@ -48,10 +55,12 @@ func (r JSON) Render(w http.ResponseWriter) (err error) {
return
}
// WriteContentType (JSON) writes JSON ContentType.
func (r JSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
// WriteJSON marshals the given interface object and writes it with custom ContentType.
func WriteJSON(w http.ResponseWriter, obj interface{}) error {
writeContentType(w, jsonContentType)
jsonBytes, err := json.Marshal(obj)
@ -62,6 +71,7 @@ func WriteJSON(w http.ResponseWriter, obj interface{}) error {
return nil
}
// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType.
func (r IndentedJSON) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
@ -72,10 +82,12 @@ func (r IndentedJSON) Render(w http.ResponseWriter) error {
return nil
}
// WriteContentType (IndentedJSON) writes JSON ContentType.
func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType.
func (r SecureJSON) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
jsonBytes, err := json.Marshal(r.Data)
@ -90,10 +102,12 @@ func (r SecureJSON) Render(w http.ResponseWriter) error {
return nil
}
// WriteContentType (SecureJSON) writes JSON ContentType.
func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType.
func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
ret, err := json.Marshal(r.Data)
@ -115,10 +129,12 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
return nil
}
// WriteContentType (JsonpJSON) writes Javascript ContentType.
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonpContentType)
}
// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType.
func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
ret, err := json.Marshal(r.Data)
@ -139,6 +155,7 @@ func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
return nil
}
// WriteContentType (AsciiJSON) writes JSON ContentType.
func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonAsciiContentType)
}

View File

@ -12,10 +12,12 @@ import (
"github.com/gin-gonic/gin/internal/json"
)
// PureJSON contains the given interface object.
type PureJSON struct {
Data interface{}
}
// Render (PureJSON) writes custom ContentType and encodes the given interface object.
func (r PureJSON) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
encoder := json.NewEncoder(w)
@ -23,6 +25,7 @@ func (r PureJSON) Render(w http.ResponseWriter) error {
return encoder.Encode(r.Data)
}
// WriteContentType (PureJSON) writes custom ContentType.
func (r PureJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}

View File

@ -10,20 +10,24 @@ import (
"github.com/ugorji/go/codec"
)
// MsgPack contains the given interface object.
type MsgPack struct {
Data interface{}
}
var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
// WriteContentType (MsgPack) writes MsgPack ContentType.
func (r MsgPack) WriteContentType(w http.ResponseWriter) {
writeContentType(w, msgpackContentType)
}
// Render (MsgPack) encodes the given interface object and writes data with custom ContentType.
func (r MsgPack) Render(w http.ResponseWriter) error {
return WriteMsgPack(w, r.Data)
}
// WriteMsgPack writes MsgPack ContentType and encodes the given interface object.
func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
writeContentType(w, msgpackContentType)
var mh codec.MsgpackHandle

View File

@ -10,12 +10,14 @@ import (
"github.com/golang/protobuf/proto"
)
// ProtoBuf contains the given interface object.
type ProtoBuf struct {
Data interface{}
}
var protobufContentType = []string{"application/x-protobuf"}
// Render (ProtoBuf) marshals the given interface object and writes data with custom ContentType.
func (r ProtoBuf) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
@ -28,6 +30,7 @@ func (r ProtoBuf) Render(w http.ResponseWriter) error {
return nil
}
// WriteContentType (ProtoBuf) writes ProtoBuf ContentType.
func (r ProtoBuf) WriteContentType(w http.ResponseWriter) {
writeContentType(w, protobufContentType)
}

View File

@ -10,6 +10,7 @@ import (
"strconv"
)
// Reader contains the IO reader and its length, and custom ContentType and other headers.
type Reader struct {
ContentType string
ContentLength int64
@ -26,10 +27,12 @@ func (r Reader) Render(w http.ResponseWriter) (err error) {
return
}
// WriteContentType (Reader) writes custom ContentType.
func (r Reader) WriteContentType(w http.ResponseWriter) {
writeContentType(w, []string{r.ContentType})
}
// writeHeaders writes custom Header.
func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) {
header := w.Header()
for k, v := range headers {

View File

@ -9,12 +9,14 @@ import (
"net/http"
)
// Redirect contains the http request reference and redirects status code and location.
type Redirect struct {
Code int
Request *http.Request
Location string
}
// Render (Redirect) redirects the http request to new location and writes redirect response.
func (r Redirect) Render(w http.ResponseWriter) error {
// todo(thinkerou): go1.6 not support StatusPermanentRedirect(308)
// when we upgrade go version we can use http.StatusPermanentRedirect
@ -25,4 +27,5 @@ func (r Redirect) Render(w http.ResponseWriter) error {
return nil
}
// WriteContentType (Redirect) don't write any ContentType.
func (r Redirect) WriteContentType(http.ResponseWriter) {}

View File

@ -6,8 +6,11 @@ package render
import "net/http"
// Render interface is to be implemented by JSON, XML, HTML, YAML and so on.
type Render interface {
// Render writes data with custom ContentType.
Render(http.ResponseWriter) error
// WriteContentType writes custom ContentType.
WriteContentType(w http.ResponseWriter)
}

View File

@ -287,7 +287,7 @@ func TestRenderProtoBuf(t *testing.T) {
err = (ProtoBuf{data}).Render(w)
assert.NoError(t, err)
assert.Equal(t, string(protoData[:]), w.Body.String())
assert.Equal(t, string(protoData), w.Body.String())
assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
}

View File

@ -10,6 +10,7 @@ import (
"net/http"
)
// String contains the given interface object slice and its format.
type String struct {
Format string
Data []interface{}
@ -17,15 +18,18 @@ type String struct {
var plainContentType = []string{"text/plain; charset=utf-8"}
// Render (String) writes data with custom ContentType.
func (r String) Render(w http.ResponseWriter) error {
WriteString(w, r.Format, r.Data)
return nil
}
// WriteContentType (String) writes Plain ContentType.
func (r String) WriteContentType(w http.ResponseWriter) {
writeContentType(w, plainContentType)
}
// WriteString writes data according to its format and write custom ContentType.
func WriteString(w http.ResponseWriter, format string, data []interface{}) {
writeContentType(w, plainContentType)
if len(data) > 0 {

View File

@ -9,17 +9,20 @@ import (
"net/http"
)
// XML contains the given interface object.
type XML struct {
Data interface{}
}
var xmlContentType = []string{"application/xml; charset=utf-8"}
// Render (XML) encodes the given interface object and writes data with custom ContentType.
func (r XML) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
return xml.NewEncoder(w).Encode(r.Data)
}
// WriteContentType (XML) writes XML ContentType for response.
func (r XML) WriteContentType(w http.ResponseWriter) {
writeContentType(w, xmlContentType)
}

View File

@ -10,12 +10,14 @@ import (
"gopkg.in/yaml.v2"
)
// YAML contains the given interface object.
type YAML struct {
Data interface{}
}
var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
// Render (YAML) marshals the given interface object and writes data with custom ContentType.
func (r YAML) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
@ -28,6 +30,7 @@ func (r YAML) Render(w http.ResponseWriter) error {
return nil
}
// WriteContentType (YAML) writes YAML ContentType for response.
func (r YAML) WriteContentType(w http.ResponseWriter) {
writeContentType(w, yamlContentType)
}

View File

@ -11,11 +11,13 @@ import (
"strings"
)
// IRouter defines all router handle interface includes single and group router.
type IRouter interface {
IRoutes
Group(string, ...HandlerFunc) *RouterGroup
}
// IRoutes defines all router handle interface.
type IRoutes interface {
Use(...HandlerFunc) IRoutes
@ -34,8 +36,8 @@ type IRoutes interface {
StaticFS(string, http.FileSystem) IRoutes
}
// RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix
// and an array of handlers (middleware).
// RouterGroup is used internally to configure router, a RouterGroup is associated with
// a prefix and an array of handlers (middleware).
type RouterGroup struct {
Handlers HandlersChain
basePath string
@ -61,6 +63,8 @@ func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *R
}
}
// BasePath returns the base path of router group.
// For example, if v := router.Group("/rest/n/v1/api"), v.BasePath() is "/rest/n/v1/api".
func (group *RouterGroup) BasePath() string {
return group.basePath
}

View File

@ -127,8 +127,6 @@ func TestTreeAddAndGet(t *testing.T) {
tree.addRoute(route, fakeHandler(route))
}
//printChildren(tree, "")
checkRequests(t, tree, testRequests{
{"/a", false, "/a", nil},
{"/", true, "", nil},
@ -170,8 +168,6 @@ func TestTreeWildcard(t *testing.T) {
tree.addRoute(route, fakeHandler(route))
}
//printChildren(tree, "")
checkRequests(t, tree, testRequests{
{"/", false, "/", nil},
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}},
@ -210,7 +206,6 @@ func TestUnescapeParameters(t *testing.T) {
tree.addRoute(route, fakeHandler(route))
}
//printChildren(tree, "")
unescape := true
checkRequests(t, tree, testRequests{
{"/", false, "/", nil},
@ -262,8 +257,6 @@ func testRoutes(t *testing.T, routes []testRoute) {
t.Errorf("unexpected panic for route '%s': %v", route.path, recv)
}
}
//printChildren(tree, "")
}
func TestTreeWildcardConflict(t *testing.T) {
@ -330,8 +323,6 @@ func TestTreeDupliatePath(t *testing.T) {
}
}
//printChildren(tree, "")
checkRequests(t, tree, testRequests{
{"/", false, "/", nil},
{"/doc/", false, "/doc/", nil},
@ -446,8 +437,6 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
}
}
//printChildren(tree, "")
tsrRoutes := [...]string{
"/hi/",
"/b",

View File

@ -14,8 +14,10 @@ import (
"strings"
)
// BindKey indicates a default bind key.
const BindKey = "_gin-gonic/gin/bindkey"
// Bind is a helper function for given interface object and returns a Gin middleware.
func Bind(val interface{}) HandlerFunc {
value := reflect.ValueOf(val)
if value.Kind() == reflect.Ptr {
@ -33,16 +35,14 @@ func Bind(val interface{}) HandlerFunc {
}
}
// WrapF is a helper function for wrapping http.HandlerFunc
// Returns a Gin middleware
// WrapF is a helper function for wrapping http.HandlerFunc and returns a Gin middleware.
func WrapF(f http.HandlerFunc) HandlerFunc {
return func(c *Context) {
f(c.Writer, c.Request)
}
}
// WrapH is a helper function for wrapping http.Handler
// Returns a Gin middleware
// WrapH is a helper function for wrapping http.Handler and returns a Gin middleware.
func WrapH(h http.Handler) HandlerFunc {
return func(c *Context) {
h.ServeHTTP(c.Writer, c.Request)

28
vendor/vendor.json vendored
View File

@ -27,8 +27,8 @@
"path": "github.com/json-iterator/go",
"revision": "1624edc4454b8682399def8740d46db5e4362ba4",
"revisionTime": "2018-08-06T06:07:27Z",
"version": "1.1.5",
"versionExact": "1.1.5"
"version": "v1.1",
"versionExact": "v1.1.5"
},
{
"checksumSHA1": "y/A5iuvwjytQE2CqVuphQRXR2nI=",
@ -38,24 +38,6 @@
"version": "v0.0.3",
"versionExact": "v0.0.3"
},
{
"checksumSHA1": "ZTcgWKWHsrX0RXYVXn5Xeb8Q0go=",
"path": "github.com/modern-go/concurrent",
"revision": "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94",
"revisionTime": "2018-03-06T01:26:44Z"
},
{
"checksumSHA1": "qvH48wzTIV3QKSDqI0dLFtVjaDI=",
"path": "github.com/modern-go/reflect2",
"revision": "94122c33edd36123c84d5368cfb2b69df93a0ec8",
"revisionTime": "2018-07-18T01:23:57Z"
},
{
"checksumSHA1": "LuFv4/jlrmFNnDb/5SCSEPAM9vU=",
"path": "github.com/pmezard/go-difflib/difflib",
"revision": "792786c7400a136282c1664665ae0a8db921c6c2",
"revisionTime": "2016-01-10T10:55:54Z"
},
{
"checksumSHA1": "c6pbpF7eowwO59phRTpF8cQ80Z0=",
"path": "github.com/stretchr/testify/assert",
@ -79,12 +61,6 @@
"revision": "d4c55e66d8c3a2f3382d264b08e3e3454a66355a",
"revisionTime": "2016-10-18T08:54:36Z"
},
{
"checksumSHA1": "7Gocawl8bm27cpAILtuf21xvVD8=",
"path": "golang.org/x/sys/unix",
"revision": "1c9583448a9c3aa0f9a6a5241bf73c0bd8aafded",
"revisionTime": "2018-08-15T07:37:39Z"
},
{
"checksumSHA1": "P/k5ZGf0lEBgpKgkwy++F7K1PSg=",
"path": "gopkg.in/go-playground/validator.v8",

8
version.go Normal file
View 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"