forked from mirror/gin
Merge branch 'master' into develop
This commit is contained in:
commit
d875f07409
11
.travis.yml
11
.travis.yml
|
@ -10,13 +10,14 @@ git:
|
|||
depth: 3
|
||||
|
||||
install:
|
||||
- go get -v github.com/kardianos/govendor
|
||||
- govendor sync
|
||||
- go get -u github.com/campoy/embedmd
|
||||
- make install
|
||||
|
||||
script:
|
||||
- embedmd -d README.md
|
||||
- go test -v -covermode=count -coverprofile=coverage.out
|
||||
- make vet
|
||||
- make fmt-check
|
||||
- make embedmd
|
||||
- make misspell-check
|
||||
- make test
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
|
|
@ -6,13 +6,22 @@
|
|||
- [NEW] Add support for Let's Encrypt via gin-gonic/autotls
|
||||
- [NEW] Improve README examples and add extra at examples folder
|
||||
- [NEW] Improved support with App Engine
|
||||
- [NEW] Add custom template delimiters, see #860
|
||||
- [NEW] Add Template Func Maps, see #962
|
||||
- [NEW] Add \*context.Handler(), see #928
|
||||
- [NEW] Add \*context.GetRawData()
|
||||
- [NEW] Add \*context.GetHeader() (request)
|
||||
- [NEW] Add \*context.AbortWithStatusJSON() (JSON content type)
|
||||
- [NEW] Add \*context.Keys type cast helpers
|
||||
- [NEW] Add \*context.ShouldBindWith()
|
||||
- [NEW] Add \*context.MustBindWith()
|
||||
- [NEW] Add \*engine.SetFuncMap()
|
||||
- [DEPRECATE] On next release: \*context.BindWith(), see #855
|
||||
- [FIX] Refactor render
|
||||
- [FIX] Reworked tests
|
||||
- [FIX] logger now supports cygwin
|
||||
- [FIX] Use X-Forwarded-For before X-Real-Ip
|
||||
- [FIX] time.Time binding (#904)
|
||||
|
||||
### Gin 1.1.4
|
||||
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
GOFMT ?= gofmt "-s"
|
||||
PACKAGES ?= $(shell go list ./... | grep -v /vendor/)
|
||||
GOFILES := $(shell find . -name "*.go" -type f -not -path "./vendor/*")
|
||||
|
||||
all: build
|
||||
|
||||
install: deps
|
||||
govendor sync
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -v -covermode=count -coverprofile=coverage.out
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
$(GOFMT) -w $(GOFILES)
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check:
|
||||
# get all go files and run go fmt on them
|
||||
@diff=$$($(GOFMT) -d $(GOFILES)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
|
||||
vet:
|
||||
go vet $(PACKAGES)
|
||||
|
||||
deps:
|
||||
@hash govendor > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/kardianos/govendor; \
|
||||
fi
|
||||
@hash embedmd > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/campoy/embedmd; \
|
||||
fi
|
||||
|
||||
embedmd:
|
||||
embedmd -d *.md
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/golang/lint/golint; \
|
||||
fi
|
||||
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
|
||||
|
||||
.PHONY: misspell-check
|
||||
misspell-check:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
misspell -error $(GOFILES)
|
||||
|
||||
.PHONY: misspell
|
||||
misspell:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
misspell -w $(GOFILES)
|
102
README.md
102
README.md
|
@ -1,6 +1,12 @@
|
|||
# Gin Web Framework <img align="right" src="https://raw.githubusercontent.com/gin-gonic/gin/master/logo.jpg">
|
||||
# Gin Web Framework
|
||||
|
||||
[![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin) [![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin) [![Go Report Card](https://goreportcard.com/badge/github.com/gin-gonic/gin)](https://goreportcard.com/report/github.com/gin-gonic/gin) [![GoDoc](https://godoc.org/github.com/gin-gonic/gin?status.svg)](https://godoc.org/github.com/gin-gonic/gin) [![Join the chat at https://gitter.im/gin-gonic/gin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Sourcegraph Badge](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge)
|
||||
<img align="right" src="https://raw.githubusercontent.com/gin-gonic/gin/master/logo.png">
|
||||
|
||||
[![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin)
|
||||
[![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/gin-gonic/gin)](https://goreportcard.com/report/github.com/gin-gonic/gin)
|
||||
[![GoDoc](https://godoc.org/github.com/gin-gonic/gin?status.svg)](https://godoc.org/github.com/gin-gonic/gin)
|
||||
[![Join the chat at https://gitter.im/gin-gonic/gin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gin-gonic/gin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to [httprouter](https://github.com/julienschmidt/httprouter). If you need performance and good productivity, you will love Gin.
|
||||
|
||||
|
@ -13,7 +19,7 @@ $ cat test.go
|
|||
```go
|
||||
package main
|
||||
|
||||
import "gopkg.in/gin-gonic/gin.v1"
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
@ -82,13 +88,13 @@ BenchmarkZeus_GithubAll | 2000 | 944234 | 300688 | 2648
|
|||
1. Download and install it:
|
||||
|
||||
```sh
|
||||
$ go get gopkg.in/gin-gonic/gin.v1
|
||||
$ go get github.com/gin-gonic/gin
|
||||
```
|
||||
|
||||
2. Import it in your code:
|
||||
|
||||
```go
|
||||
import "gopkg.in/gin-gonic/gin.v1"
|
||||
import "github.com/gin-gonic/gin"
|
||||
```
|
||||
|
||||
3. (Optional) Import `net/http`. This is required for example if using constants such as `http.StatusOK`.
|
||||
|
@ -97,12 +103,36 @@ import "gopkg.in/gin-gonic/gin.v1"
|
|||
import "net/http"
|
||||
```
|
||||
|
||||
4. (Optional) Use latest changes (note: they may be broken and/or unstable):
|
||||
### Use a vendor tool like [Govendor](https://github.com/kardianos/govendor)
|
||||
|
||||
1. `go get` govendor
|
||||
|
||||
```sh
|
||||
$ GIN_PATH=$GOPATH/src/gopkg.in/gin-gonic/gin.v1
|
||||
$ git -C $GIN_PATH checkout develop
|
||||
$ git -C $GIN_PATH pull origin develop
|
||||
$ go get github.com/kardianos/govendor
|
||||
```
|
||||
2. Create your project folder and `cd` inside
|
||||
|
||||
```sh
|
||||
$ mkdir -p ~/go/src/github.com/myusername/project && cd "$_"
|
||||
```
|
||||
|
||||
3. Vendor init your project and add gin
|
||||
|
||||
```sh
|
||||
$ govendor init
|
||||
$ govendor add github.com/gin-gonic/gin@v1.2
|
||||
```
|
||||
|
||||
4. Copy a starting template inside your project
|
||||
|
||||
```sh
|
||||
$ cp ~/go/src/github.com/gin-gonic/gin/examples/basic/* .
|
||||
```
|
||||
|
||||
5. Run your project
|
||||
|
||||
```sh
|
||||
$ go run main.go
|
||||
```
|
||||
|
||||
## API Examples
|
||||
|
@ -451,7 +481,7 @@ func startPage(c *gin.Context) {
|
|||
package main
|
||||
|
||||
import (
|
||||
"gopkg.in/gin-gonic/gin.v1"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type LoginForm struct {
|
||||
|
@ -463,7 +493,7 @@ func main() {
|
|||
router := gin.Default()
|
||||
router.POST("/login", func(c *gin.Context) {
|
||||
// you can bind multipart form with explicit binding declaration:
|
||||
// c.BindWith(&form, binding.Form)
|
||||
// c.MustBindWith(&form, binding.Form)
|
||||
// or you can simply use autobinding with Bind method:
|
||||
var form LoginForm
|
||||
// in this case proper binding will be automatically selected
|
||||
|
@ -622,6 +652,54 @@ func main() {
|
|||
}
|
||||
```
|
||||
|
||||
You may use custom delims
|
||||
|
||||
```go
|
||||
r := gin.Default()
|
||||
r.Delims("{[{", "}]}")
|
||||
r.LoadHTMLGlob("/path/to/templates"))
|
||||
```
|
||||
|
||||
#### Add custom template funcs
|
||||
|
||||
main.go
|
||||
|
||||
```go
|
||||
...
|
||||
|
||||
func formatAsDate(t time.Time) string {
|
||||
year, month, day := t.Date()
|
||||
return fmt.Sprintf("%d/%02d/%02d", year, month, day)
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
router.SetFuncMap(template.FuncMap{
|
||||
"formatAsDate": formatAsDate,
|
||||
})
|
||||
|
||||
...
|
||||
|
||||
router.GET("/raw", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
|
||||
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
|
||||
})
|
||||
})
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
raw.tmpl
|
||||
|
||||
```html
|
||||
Date: {[{.now | formatAsDate}]}
|
||||
```
|
||||
|
||||
Result:
|
||||
```
|
||||
Date: 2017/07/01
|
||||
```
|
||||
|
||||
### Multitemplate
|
||||
|
||||
Gin allow by default use only one html.Template. Check [a multitemplate render](https://github.com/gin-contrib/multitemplate) for using features like go 1.6 `block template`.
|
||||
|
@ -917,7 +995,7 @@ func main() {
|
|||
- Please provide source code and commit sha if you found a bug.
|
||||
- Review existing issues and provide feedback or react to them.
|
||||
- With pull requests:
|
||||
- Open your pull request against develop
|
||||
- Open your pull request against master
|
||||
- Your pull request should have no more than two commits, if not you should squash them.
|
||||
- It should pass all tests in the available continuous integrations systems such as TravisCI.
|
||||
- You should add/modify tests to cover your proposed code changes.
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2017 Manu Martinez-Almeida. 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
|
||||
|
||||
import (
|
||||
|
|
|
@ -48,7 +48,8 @@ var (
|
|||
func Default(method, contentType string) Binding {
|
||||
if method == "GET" {
|
||||
return Form
|
||||
} else {
|
||||
}
|
||||
|
||||
switch contentType {
|
||||
case MIMEJSON:
|
||||
return JSON
|
||||
|
@ -62,7 +63,6 @@ func Default(method, contentType string) Binding {
|
|||
return Form
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validate(obj interface{}) error {
|
||||
if Validator == nil {
|
||||
|
|
|
@ -12,9 +12,8 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin/binding/example"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/ugorji/go/codec"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/ugorji/go/codec"
|
||||
)
|
||||
|
||||
type FooStruct struct {
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
|
|
|
@ -6,7 +6,6 @@ package binding
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"net/http"
|
||||
)
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
package binding
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
type protobufBinding struct{}
|
||||
|
|
119
context.go
119
context.go
|
@ -16,9 +16,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/sse"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"github.com/gin-gonic/gin/render"
|
||||
"gopkg.in/gin-contrib/sse.v0"
|
||||
)
|
||||
|
||||
// Content-Type MIME of the most common data formats
|
||||
|
@ -85,6 +85,11 @@ func (c *Context) HandlerName() string {
|
|||
return nameOfFunction(c.handlers.Last())
|
||||
}
|
||||
|
||||
// Handler returns the main handler.
|
||||
func (c *Context) Handler() HandlerFunc {
|
||||
return c.handlers.Last()
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/*********** FLOW CONTROL ***********/
|
||||
/************************************/
|
||||
|
@ -191,6 +196,94 @@ func (c *Context) MustGet(key string) interface{} {
|
|||
panic("Key \"" + key + "\" does not exist")
|
||||
}
|
||||
|
||||
// GetString returns the value associated with the key as a string.
|
||||
func (c *Context) GetString(key string) (s string) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
s, _ = val.(string)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetBool returns the value associated with the key as a boolean.
|
||||
func (c *Context) GetBool(key string) (b bool) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
b, _ = val.(bool)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetInt returns the value associated with the key as an integer.
|
||||
func (c *Context) GetInt(key string) (i int) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
i, _ = val.(int)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetInt64 returns the value associated with the key as an integer.
|
||||
func (c *Context) GetInt64(key string) (i64 int64) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
i64, _ = val.(int64)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetFloat64 returns the value associated with the key as a float64.
|
||||
func (c *Context) GetFloat64(key string) (f64 float64) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
f64, _ = val.(float64)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetTime returns the value associated with the key as time.
|
||||
func (c *Context) GetTime(key string) (t time.Time) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
t, _ = val.(time.Time)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetDuration returns the value associated with the key as a duration.
|
||||
func (c *Context) GetDuration(key string) (d time.Duration) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
d, _ = val.(time.Duration)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetStringSlice returns the value associated with the key as a slice of strings.
|
||||
func (c *Context) GetStringSlice(key string) (ss []string) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
ss, _ = val.([]string)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetStringMap returns the value associated with the key as a map of interfaces.
|
||||
func (c *Context) GetStringMap(key string) (sm map[string]interface{}) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
sm, _ = val.(map[string]interface{})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetStringMapString returns the value associated with the key as a map of strings.
|
||||
func (c *Context) GetStringMapString(key string) (sms map[string]string) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
sms, _ = val.(map[string]string)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
|
||||
func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
smss, _ = val.(map[string][]string)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/************************************/
|
||||
/************ INPUT DATA ************/
|
||||
/************************************/
|
||||
|
@ -341,22 +434,30 @@ func (c *Context) MultipartForm() (*multipart.Form, error) {
|
|||
// Like ParseBody() but this method also writes a 400 error if the json is not valid.
|
||||
func (c *Context) Bind(obj interface{}) error {
|
||||
b := binding.Default(c.Request.Method, c.ContentType())
|
||||
return c.BindWith(obj, b)
|
||||
return c.MustBindWith(obj, b)
|
||||
}
|
||||
|
||||
// BindJSON is a shortcut for c.BindWith(obj, binding.JSON)
|
||||
// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON)
|
||||
func (c *Context) BindJSON(obj interface{}) error {
|
||||
return c.BindWith(obj, binding.JSON)
|
||||
return c.MustBindWith(obj, binding.JSON)
|
||||
}
|
||||
|
||||
// BindWith binds the passed struct pointer using the specified binding engine.
|
||||
// MustBindWith binds the passed struct pointer using the specified binding
|
||||
// engine. It will abort the request with HTTP 400 if any error ocurrs.
|
||||
// See the binding package.
|
||||
func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
|
||||
if err := b.Bind(c.Request, obj); err != nil {
|
||||
func (c *Context) MustBindWith(obj interface{}, b binding.Binding) (err error) {
|
||||
if err = c.ShouldBindWith(obj, b); err != nil {
|
||||
c.AbortWithError(400, err).SetType(ErrorTypeBind)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ShouldBindWith binds the passed struct pointer using the specified binding
|
||||
// engine.
|
||||
// See the binding package.
|
||||
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
|
||||
return b.Bind(c.Request, obj)
|
||||
}
|
||||
|
||||
// ClientIP implements a best effort algorithm to return the real client IP, it parses
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
// +build appengine
|
||||
|
||||
// Copyright 2017 Manu Martinez-Almeida. 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
|
||||
|
||||
func init() {
|
||||
|
|
164
context_test.go
164
context_test.go
|
@ -12,13 +12,14 @@ import (
|
|||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/sse"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/net/context"
|
||||
"gopkg.in/gin-contrib/sse.v0"
|
||||
)
|
||||
|
||||
var _ context.Context = &Context{}
|
||||
|
@ -168,6 +169,85 @@ func TestContextSetGetValues(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestContextGetString(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Set("string", "this is a string")
|
||||
assert.Equal(t, "this is a string", c.GetString("string"))
|
||||
}
|
||||
|
||||
func TestContextSetGetBool(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Set("bool", true)
|
||||
assert.Equal(t, true, c.GetBool("bool"))
|
||||
}
|
||||
|
||||
func TestContextGetInt(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Set("int", 1)
|
||||
assert.Equal(t, 1, c.GetInt("int"))
|
||||
}
|
||||
|
||||
func TestContextGetInt64(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Set("int64", int64(42424242424242))
|
||||
assert.Equal(t, int64(42424242424242), c.GetInt64("int64"))
|
||||
}
|
||||
|
||||
func TestContextGetFloat64(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Set("float64", 4.2)
|
||||
assert.Equal(t, 4.2, c.GetFloat64("float64"))
|
||||
}
|
||||
|
||||
func TestContextGetTime(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
t1, _ := time.Parse("1/2/2006 15:04:05", "01/01/2017 12:00:00")
|
||||
c.Set("time", t1)
|
||||
assert.Equal(t, t1, c.GetTime("time"))
|
||||
}
|
||||
|
||||
func TestContextGetDuration(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Set("duration", time.Second)
|
||||
assert.Equal(t, time.Second, c.GetDuration("duration"))
|
||||
}
|
||||
|
||||
func TestContextGetStringSlice(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Set("slice", []string{"foo"})
|
||||
assert.Equal(t, []string{"foo"}, c.GetStringSlice("slice"))
|
||||
}
|
||||
|
||||
func TestContextGetStringMap(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
var m = make(map[string]interface{})
|
||||
m["foo"] = 1
|
||||
c.Set("map", m)
|
||||
|
||||
assert.Equal(t, m, c.GetStringMap("map"))
|
||||
assert.Equal(t, 1, c.GetStringMap("map")["foo"])
|
||||
}
|
||||
|
||||
func TestContextGetStringMapString(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
var m = make(map[string]string)
|
||||
m["foo"] = "bar"
|
||||
c.Set("map", m)
|
||||
|
||||
assert.Equal(t, m, c.GetStringMapString("map"))
|
||||
assert.Equal(t, "bar", c.GetStringMapString("map")["foo"])
|
||||
}
|
||||
|
||||
func TestContextGetStringMapStringSlice(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
var m = make(map[string][]string)
|
||||
m["foo"] = []string{"foo"}
|
||||
c.Set("map", m)
|
||||
|
||||
assert.Equal(t, m, c.GetStringMapStringSlice("map"))
|
||||
assert.Equal(t, []string{"foo"}, c.GetStringMapStringSlice("map")["foo"])
|
||||
}
|
||||
|
||||
func TestContextCopy(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.index = 2
|
||||
|
@ -198,6 +278,17 @@ func handlerNameTest(c *Context) {
|
|||
|
||||
}
|
||||
|
||||
var handlerTest HandlerFunc = func(c *Context) {
|
||||
|
||||
}
|
||||
|
||||
func TestContextHandler(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.handlers = HandlersChain{func(c *Context) {}, handlerTest}
|
||||
|
||||
assert.Equal(t, reflect.ValueOf(handlerTest).Pointer(), reflect.ValueOf(c.Handler()).Pointer())
|
||||
}
|
||||
|
||||
func TestContextQuery(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10&id=", nil)
|
||||
|
@ -384,12 +475,21 @@ func TestContextSetCookie(t *testing.T) {
|
|||
assert.Equal(t, c.Writer.Header().Get("Set-Cookie"), "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure")
|
||||
}
|
||||
|
||||
func TestContextSetCookiePathEmpty(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.SetCookie("user", "gin", 1, "", "localhost", true, true)
|
||||
assert.Equal(t, c.Writer.Header().Get("Set-Cookie"), "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure")
|
||||
}
|
||||
|
||||
func TestContextGetCookie(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest("GET", "/get", nil)
|
||||
c.Request.Header.Set("Cookie", "user=gin")
|
||||
cookie, _ := c.Cookie("user")
|
||||
assert.Equal(t, cookie, "gin")
|
||||
|
||||
_, err := c.Cookie("nokey")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestContextBodyAllowedForStatus(t *testing.T) {
|
||||
|
@ -737,6 +837,68 @@ func TestContextRenderRedirectAll(t *testing.T) {
|
|||
assert.NotPanics(t, func() { c.Redirect(308, "/resource") })
|
||||
}
|
||||
|
||||
func TestContextNegotiationWithJSON(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
c.Request, _ = http.NewRequest("POST", "", nil)
|
||||
|
||||
c.Negotiate(200, Negotiate{
|
||||
Offered: []string{MIMEJSON, MIMEXML},
|
||||
Data: H{"foo": "bar"},
|
||||
})
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
|
||||
assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestContextNegotiationWithXML(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
c.Request, _ = http.NewRequest("POST", "", nil)
|
||||
|
||||
c.Negotiate(200, Negotiate{
|
||||
Offered: []string{MIMEXML, MIMEJSON},
|
||||
Data: H{"foo": "bar"},
|
||||
})
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
|
||||
assert.Equal(t, "application/xml; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestContextNegotiationWithHTML(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, router := CreateTestContext(w)
|
||||
c.Request, _ = http.NewRequest("POST", "", nil)
|
||||
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
||||
router.SetHTMLTemplate(templ)
|
||||
|
||||
c.Negotiate(200, Negotiate{
|
||||
Offered: []string{MIMEHTML},
|
||||
Data: H{"name": "gin"},
|
||||
HTMLName: "t",
|
||||
})
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
assert.Equal(t, "Hello gin", w.Body.String())
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestContextNegotiationNotSupport(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
c.Request, _ = http.NewRequest("POST", "", nil)
|
||||
|
||||
c.Negotiate(200, Negotiate{
|
||||
Offered: []string{MIMEPOSTForm},
|
||||
})
|
||||
|
||||
assert.Equal(t, 406, w.Code)
|
||||
assert.Equal(t, c.index, abortIndex)
|
||||
assert.True(t, c.IsAborted())
|
||||
}
|
||||
|
||||
func TestContextNegotiationFormat(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest("POST", "", nil)
|
||||
|
|
|
@ -7,6 +7,7 @@ package gin
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
@ -66,6 +67,25 @@ func TestDebugPrintRoutes(t *testing.T) {
|
|||
assert.Regexp(t, `^\[GIN-debug\] GET /path/to/route/:param --> (.*/vendor/)?github.com/gin-gonic/gin.handlerNameTest \(2 handlers\)\n$`, w.String())
|
||||
}
|
||||
|
||||
func TestDebugPrintLoadTemplate(t *testing.T) {
|
||||
var w bytes.Buffer
|
||||
setup(&w)
|
||||
defer teardown()
|
||||
|
||||
templ := template.Must(template.New("").Delims("{[{", "}]}").ParseGlob("./fixtures/basic/hello.tmpl"))
|
||||
debugPrintLoadTemplate(templ)
|
||||
assert.Equal(t, w.String(), "[GIN-debug] Loaded HTML Templates (2): \n\t- \n\t- hello.tmpl\n\n")
|
||||
}
|
||||
|
||||
func TestDebugPrintWARNINGSetHTMLTemplate(t *testing.T) {
|
||||
var w bytes.Buffer
|
||||
setup(&w)
|
||||
defer teardown()
|
||||
|
||||
debugPrintWARNINGSetHTMLTemplate()
|
||||
assert.Equal(t, w.String(), "[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")
|
||||
}
|
||||
|
||||
func setup(w io.Writer) {
|
||||
SetMode(DebugMode)
|
||||
log.SetOutput(w)
|
||||
|
|
|
@ -4,9 +4,22 @@
|
|||
|
||||
package gin
|
||||
|
||||
import "log"
|
||||
import (
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"log"
|
||||
)
|
||||
|
||||
func (c *Context) GetCookie(name string) (string, error) {
|
||||
log.Println("GetCookie() method is deprecated. Use Cookie() instead.")
|
||||
return c.Cookie(name)
|
||||
}
|
||||
|
||||
// BindWith binds the passed struct pointer using the specified binding engine.
|
||||
// See the binding package.
|
||||
func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
|
||||
log.Println(`BindWith(\"interface{}, binding.Binding\") error is going to
|
||||
be deprecated, please check issue #662 and either use MustBindWith() if you
|
||||
want HTTP 400 to be automatically returned if any error occur, of use
|
||||
ShouldBindWith() if you need to manage the error.`)
|
||||
return c.MustBindWith(obj, b)
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ func (msg *Error) IsType(flags ErrorType) bool {
|
|||
return (msg.Type & flags) > 0
|
||||
}
|
||||
|
||||
// Returns a readonly copy filterd the byte.
|
||||
// Returns a readonly copy filtered the byte.
|
||||
// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic
|
||||
func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
|
||||
if len(a) == 0 {
|
||||
|
|
|
@ -54,6 +54,13 @@ func TestError(t *testing.T) {
|
|||
"status": "200",
|
||||
"data": "some data",
|
||||
})
|
||||
|
||||
type customError struct {
|
||||
status string
|
||||
data string
|
||||
}
|
||||
err.SetMeta(customError{status: "200", data: "other data"})
|
||||
assert.Equal(t, err.JSON(), customError{status: "200", data: "other data"})
|
||||
}
|
||||
|
||||
func TestErrorSlice(t *testing.T) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package hello
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// This function's name is a must. App Engine uses it to drive the requests properly.
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
all: deps build
|
||||
|
||||
.PHONY: deps
|
||||
deps:
|
||||
go get -d -v github.com/dustin/go-broadcast/...
|
||||
go get -d -v github.com/manucorporat/stats/...
|
||||
|
||||
.PHONY: build
|
||||
build: deps
|
||||
go build -o realtime-advanced main.go rooms.go routes.go stats.go
|
|
@ -11,7 +11,6 @@ import (
|
|||
)
|
||||
|
||||
func rateLimit(c *gin.Context) {
|
||||
|
||||
ip := c.ClientIP()
|
||||
value := int(ips.Add(ip, 1))
|
||||
if value%50 == 0 {
|
||||
|
|
|
@ -8,11 +8,13 @@ import (
|
|||
"github.com/manucorporat/stats"
|
||||
)
|
||||
|
||||
var ips = stats.New()
|
||||
var messages = stats.New()
|
||||
var users = stats.New()
|
||||
var mutexStats sync.RWMutex
|
||||
var savedStats map[string]uint64
|
||||
var (
|
||||
ips = stats.New()
|
||||
messages = stats.New()
|
||||
users = stats.New()
|
||||
mutexStats sync.RWMutex
|
||||
savedStats map[string]uint64
|
||||
)
|
||||
|
||||
func statsWorker() {
|
||||
c := time.Tick(1 * time.Second)
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
all: deps build
|
||||
|
||||
.PHONY: deps
|
||||
deps:
|
||||
go get -d -v github.com/dustin/go-broadcast/...
|
||||
|
||||
.PHONY: build
|
||||
build: deps
|
||||
go build -o realtime-chat main.go rooms.go template.go
|
|
@ -0,0 +1 @@
|
|||
<h1>Hello {[{.name}]}</h1>
|
|
@ -0,0 +1 @@
|
|||
Date: {[{.now | formatAsDate}]}
|
4
fs.go
4
fs.go
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2017 Manu Martinez-Almeida. 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
|
||||
|
||||
import (
|
||||
|
|
30
gin.go
30
gin.go
|
@ -45,7 +45,9 @@ type (
|
|||
// Create an instance of Engine, by using New() or Default()
|
||||
Engine struct {
|
||||
RouterGroup
|
||||
delims render.Delims
|
||||
HTMLRender render.HTMLRender
|
||||
FuncMap template.FuncMap
|
||||
allNoRoute HandlersChain
|
||||
allNoMethod HandlersChain
|
||||
noRoute HandlersChain
|
||||
|
@ -111,6 +113,7 @@ func New() *Engine {
|
|||
basePath: "/",
|
||||
root: true,
|
||||
},
|
||||
FuncMap: template.FuncMap{},
|
||||
RedirectTrailingSlash: true,
|
||||
RedirectFixedPath: false,
|
||||
HandleMethodNotAllowed: false,
|
||||
|
@ -119,6 +122,7 @@ func New() *Engine {
|
|||
UseRawPath: false,
|
||||
UnescapePathValues: true,
|
||||
trees: make(methodTrees, 0, 9),
|
||||
delims: render.Delims{"{{", "}}"},
|
||||
}
|
||||
engine.RouterGroup.engine = engine
|
||||
engine.pool.New = func() interface{} {
|
||||
|
@ -138,21 +142,26 @@ func (engine *Engine) allocateContext() *Context {
|
|||
return &Context{engine: engine}
|
||||
}
|
||||
|
||||
func (engine *Engine) Delims(left, right string) *Engine {
|
||||
engine.delims = render.Delims{left, right}
|
||||
return engine
|
||||
}
|
||||
|
||||
func (engine *Engine) LoadHTMLGlob(pattern string) {
|
||||
if IsDebugging() {
|
||||
debugPrintLoadTemplate(template.Must(template.ParseGlob(pattern)))
|
||||
engine.HTMLRender = render.HTMLDebug{Glob: pattern}
|
||||
debugPrintLoadTemplate(template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern)))
|
||||
engine.HTMLRender = render.HTMLDebug{Glob: pattern, FuncMap: engine.FuncMap, Delims: engine.delims}
|
||||
} else {
|
||||
templ := template.Must(template.ParseGlob(pattern))
|
||||
templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern))
|
||||
engine.SetHTMLTemplate(templ)
|
||||
}
|
||||
}
|
||||
|
||||
func (engine *Engine) LoadHTMLFiles(files ...string) {
|
||||
if IsDebugging() {
|
||||
engine.HTMLRender = render.HTMLDebug{Files: files}
|
||||
engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
|
||||
} else {
|
||||
templ := template.Must(template.ParseFiles(files...))
|
||||
templ := template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseFiles(files...))
|
||||
engine.SetHTMLTemplate(templ)
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +170,12 @@ func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
|
|||
if len(engine.trees) > 0 {
|
||||
debugPrintWARNINGSetHTMLTemplate()
|
||||
}
|
||||
engine.HTMLRender = render.HTMLProduction{Template: templ}
|
||||
|
||||
engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
|
||||
}
|
||||
|
||||
func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
|
||||
engine.FuncMap = funcMap
|
||||
}
|
||||
|
||||
// NoRoute adds handlers for NoRoute. It return a 404 code by default.
|
||||
|
@ -318,8 +332,8 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
|
|||
context.Next()
|
||||
context.writermem.WriteHeaderNow()
|
||||
return
|
||||
|
||||
} else if httpMethod != "CONNECT" && path != "/" {
|
||||
}
|
||||
if httpMethod != "CONNECT" && path != "/" {
|
||||
if tsr && engine.RedirectTrailingSlash {
|
||||
redirectTrailingSlash(context)
|
||||
return
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2017 Manu Martinez-Almeida. 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
|
||||
|
||||
import (
|
||||
|
@ -6,18 +10,18 @@ import (
|
|||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http/httptest"
|
||||
)
|
||||
|
||||
func testRequest(t *testing.T, url string) {
|
||||
resp, err := http.Get(url)
|
||||
defer resp.Body.Close()
|
||||
assert.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, ioerr := ioutil.ReadAll(resp.Body)
|
||||
assert.NoError(t, ioerr)
|
||||
|
|
112
gin_test.go
112
gin_test.go
|
@ -5,14 +5,98 @@
|
|||
package gin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func formatAsDate(t time.Time) string {
|
||||
year, month, day := t.Date()
|
||||
return fmt.Sprintf("%d/%02d/%02d", year, month, day)
|
||||
}
|
||||
|
||||
func setupHTMLFiles(t *testing.T) func() {
|
||||
go func() {
|
||||
SetMode(TestMode)
|
||||
router := New()
|
||||
router.Delims("{[{", "}]}")
|
||||
router.SetFuncMap(template.FuncMap{
|
||||
"formatAsDate": formatAsDate,
|
||||
})
|
||||
router.LoadHTMLFiles("./fixtures/basic/hello.tmpl", "./fixtures/basic/raw.tmpl")
|
||||
router.GET("/test", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"})
|
||||
})
|
||||
router.GET("/raw", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
|
||||
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
|
||||
})
|
||||
})
|
||||
router.Run(":8888")
|
||||
}()
|
||||
t.Log("waiting 1 second for server startup")
|
||||
time.Sleep(1 * time.Second)
|
||||
return func() {}
|
||||
}
|
||||
|
||||
func setupHTMLGlob(t *testing.T) func() {
|
||||
go func() {
|
||||
SetMode(DebugMode)
|
||||
router := New()
|
||||
router.Delims("{[{", "}]}")
|
||||
router.SetFuncMap(template.FuncMap{
|
||||
"formatAsDate": formatAsDate,
|
||||
})
|
||||
router.LoadHTMLGlob("./fixtures/basic/*")
|
||||
router.GET("/test", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"})
|
||||
})
|
||||
router.GET("/raw", func(c *Context) {
|
||||
c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{
|
||||
"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),
|
||||
})
|
||||
})
|
||||
router.Run(":8888")
|
||||
}()
|
||||
t.Log("waiting 1 second for server startup")
|
||||
time.Sleep(1 * time.Second)
|
||||
return func() {}
|
||||
}
|
||||
|
||||
//TODO
|
||||
// func (engine *Engine) LoadHTMLGlob(pattern string) {
|
||||
func TestLoadHTMLGlob(t *testing.T) {
|
||||
td := setupHTMLGlob(t)
|
||||
res, err := http.Get("http://127.0.0.1:8888/test")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
resp, _ := ioutil.ReadAll(res.Body)
|
||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
|
||||
|
||||
td()
|
||||
}
|
||||
|
||||
func TestLoadHTMLGlobFromFuncMap(t *testing.T) {
|
||||
time.Now()
|
||||
td := setupHTMLGlob(t)
|
||||
res, err := http.Get("http://127.0.0.1:8888/raw")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
resp, _ := ioutil.ReadAll(res.Body)
|
||||
assert.Equal(t, "Date: 2017/07/01\n", string(resp[:]))
|
||||
|
||||
td()
|
||||
}
|
||||
|
||||
// func (engine *Engine) LoadHTMLFiles(files ...string) {
|
||||
// func (engine *Engine) RunTLS(addr string, cert string, key string) error {
|
||||
|
||||
|
@ -42,6 +126,32 @@ func TestCreateEngine(t *testing.T) {
|
|||
// SetMode(TestMode)
|
||||
// }
|
||||
|
||||
func TestLoadHTMLFiles(t *testing.T) {
|
||||
td := setupHTMLFiles(t)
|
||||
res, err := http.Get("http://127.0.0.1:8888/test")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
resp, _ := ioutil.ReadAll(res.Body)
|
||||
assert.Equal(t, "<h1>Hello world</h1>", string(resp[:]))
|
||||
td()
|
||||
}
|
||||
|
||||
func TestLoadHTMLFilesFuncMap(t *testing.T) {
|
||||
time.Now()
|
||||
td := setupHTMLFiles(t)
|
||||
res, err := http.Get("http://127.0.0.1:8888/raw")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
resp, _ := ioutil.ReadAll(res.Body)
|
||||
assert.Equal(t, "Date: 2017/07/01\n", string(resp[:]))
|
||||
|
||||
td()
|
||||
}
|
||||
|
||||
func TestLoadHTMLReleaseMode(t *testing.T) {
|
||||
|
||||
}
|
||||
|
|
|
@ -7,11 +7,10 @@ package gin
|
|||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"testing"
|
||||
|
||||
"github.com/gin-contrib/sse"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/gin-contrib/sse.v0"
|
||||
)
|
||||
|
||||
func TestMiddlewareGeneralCase(t *testing.T) {
|
||||
|
|
2
path.go
2
path.go
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2013 Julien Schmidt. All rights reserved.
|
||||
// Based on the path package, Copyright 2009 The Go Authors.
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
// at https://github.com/julienschmidt/httprouter/blob/master/LICENSE.
|
||||
|
||||
package gin
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2013 Julien Schmidt. All rights reserved.
|
||||
// Based on the path package, Copyright 2009 The Go Authors.
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
// at https://github.com/julienschmidt/httprouter/blob/master/LICENSE
|
||||
|
||||
package gin
|
||||
|
||||
|
|
|
@ -10,17 +10,25 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
Delims struct {
|
||||
Left string
|
||||
Right string
|
||||
}
|
||||
|
||||
HTMLRender interface {
|
||||
Instance(string, interface{}) Render
|
||||
}
|
||||
|
||||
HTMLProduction struct {
|
||||
Template *template.Template
|
||||
Delims Delims
|
||||
}
|
||||
|
||||
HTMLDebug struct {
|
||||
Files []string
|
||||
Glob string
|
||||
Delims Delims
|
||||
FuncMap template.FuncMap
|
||||
}
|
||||
|
||||
HTML struct {
|
||||
|
@ -48,11 +56,14 @@ func (r HTMLDebug) Instance(name string, data interface{}) Render {
|
|||
}
|
||||
}
|
||||
func (r HTMLDebug) loadTemplate() *template.Template {
|
||||
if r.FuncMap == nil {
|
||||
r.FuncMap = template.FuncMap{}
|
||||
}
|
||||
if len(r.Files) > 0 {
|
||||
return template.Must(template.ParseFiles(r.Files...))
|
||||
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFiles(r.Files...))
|
||||
}
|
||||
if len(r.Glob) > 0 {
|
||||
return template.Must(template.ParseGlob(r.Glob))
|
||||
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseGlob(r.Glob))
|
||||
}
|
||||
panic("the HTML debug render was created without files or glob pattern")
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright 2017 Manu Martinez-Almeida. 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
|
||||
|
||||
import (
|
||||
|
|
5
tree.go
5
tree.go
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2013 Julien Schmidt. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
// at https://github.com/julienschmidt/httprouter/blob/master/LICENSE
|
||||
|
||||
package gin
|
||||
|
||||
|
@ -432,7 +432,8 @@ walk: // Outer loop for walking the tree
|
|||
|
||||
if handlers = n.handlers; handlers != nil {
|
||||
return
|
||||
} else if len(n.children) == 1 {
|
||||
}
|
||||
if len(n.children) == 1 {
|
||||
// No handle found. Check if a handle for this path + a
|
||||
// trailing slash exists for TSR recommendation
|
||||
n = n.children[0]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2013 Julien Schmidt. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
// at https://github.com/julienschmidt/httprouter/blob/master/LICENSE
|
||||
|
||||
package gin
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"comment": "v1.1.4",
|
||||
"comment": "v1.2",
|
||||
"ignore": "test",
|
||||
"package": [
|
||||
{
|
||||
|
@ -16,10 +16,22 @@
|
|||
"revisionTime": "2014-06-27T04:00:55Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "kBeNcaKk56FguvPSUCEaH6AxpRc=",
|
||||
"checksumSHA1": "QeKwBtN2df+j+4stw3bQJ6yO4EY=",
|
||||
"path": "github.com/gin-contrib/sse",
|
||||
"revision": "22d885f9ecc78bf4ee5d72b937e4bbcdc58e8cae",
|
||||
"revisionTime": "2017-01-09T09:34:21Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "FJKrZuFmeLJp8HDeJc6UkIDBPUw=",
|
||||
"path": "github.com/gin-gonic/autotls",
|
||||
"revision": "5b3297bdcee778ff3bbdc99ab7c41e1c2677d22d",
|
||||
"revisionTime": "2017-04-16T09:39:34Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "qlPUeFabwF4RKAOF1H+yBFU1Veg=",
|
||||
"path": "github.com/golang/protobuf/proto",
|
||||
"revision": "8ee79997227bf9b34611aee7946ae64735e6fd93",
|
||||
"revisionTime": "2016-11-17T03:31:26Z"
|
||||
"revision": "5a0f697c9ed9d68fef0116532c6e05cfeae00e55",
|
||||
"revisionTime": "2017-06-01T23:02:30Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "9if9IBLsxkarJ804NPWAzgskIAk=",
|
||||
|
@ -53,6 +65,18 @@
|
|||
"revision": "c88ee250d0221a57af388746f5cf03768c21d6e2",
|
||||
"revisionTime": "2017-02-15T20:11:44Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "W0j4I7QpxXlChjyhAojZmFjU6Bg=",
|
||||
"path": "golang.org/x/crypto/acme",
|
||||
"revision": "adbae1b6b6fb4b02448a0fc0dbbc9ba2b95b294d",
|
||||
"revisionTime": "2017-06-19T06:03:41Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "TrKJW+flz7JulXU3sqnBJjGzgQc=",
|
||||
"path": "golang.org/x/crypto/acme/autocert",
|
||||
"revision": "adbae1b6b6fb4b02448a0fc0dbbc9ba2b95b294d",
|
||||
"revisionTime": "2017-06-19T06:03:41Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "9jjO5GjLa0XF/nfWihF02RoH4qc=",
|
||||
"comment": "release-branch.go1.7",
|
||||
|
@ -61,17 +85,11 @@
|
|||
"revisionTime": "2016-10-18T08:54:36Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "/oZpHfYc+ZgOwYAhlvcMhmETYpw=",
|
||||
"checksumSHA1": "TVEkpH3gq84iQ39I4R+mlDwjuVI=",
|
||||
"path": "golang.org/x/sys/unix",
|
||||
"revision": "99f16d856c9836c42d24e7ab64ea72916925fa97",
|
||||
"revisionTime": "2017-03-08T15:04:45Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "pyAPYrymvmZl0M/Mr4yfjOQjA8I=",
|
||||
"path": "gopkg.in/gin-contrib/sse.v0",
|
||||
"revision": "22d885f9ecc78bf4ee5d72b937e4bbcdc58e8cae",
|
||||
"revisionTime": "2017-01-09T09:34:21Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "39V1idWER42Lmcmg2Uy40wMzOlo=",
|
||||
"comment": "v8.18.1",
|
||||
|
|
Loading…
Reference in New Issue