Push branch develop into master

This commit is contained in:
Javier Provecho Fernandez 2016-04-10 23:54:45 +02:00
commit 7d0b329203
8 changed files with 109 additions and 28 deletions

View File

@ -2,7 +2,8 @@ language: go
sudo: false sudo: false
go: go:
- 1.4 - 1.4
- 1.4.2 - 1.5
- 1.6
- tip - tip
script: script:

View File

@ -6,7 +6,7 @@
[![GoDoc](https://godoc.org/github.com/gin-gonic/gin?status.svg)](https://godoc.org/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) [![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 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. 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.
@ -24,7 +24,7 @@ func main() {
r := gin.Default() r := gin.Default()
r.GET("/ping", func(c *gin.Context) { r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{ c.JSON(200, gin.H{
"message": "hello world", "message": "pong",
}) })
}) })
r.Run() // listen and server on 0.0.0.0:8080 r.Run() // listen and server on 0.0.0.0:8080
@ -85,14 +85,21 @@ BenchmarkZeus_GithubAll | 2000 | 944234 | 300688 | 2648
## Start using it ## Start using it
1. Download and install it: 1. Download and install it:
```sh ```sh
$ go get github.com/gin-gonic/gin $ go get github.com/gin-gonic/gin
``` ```
2. Import it in your code: 2. Import it in your code:
```go ```go
import "github.com/gin-gonic/gin" import "github.com/gin-gonic/gin"
``` ```
3. (Optional) Import `net/http`. This is required for example if using constants such as `http.StatusOK`.
```go
import "net/http"
```
##API Examples ##API Examples
@ -115,7 +122,7 @@ func main() {
// By default it serves on :8080 unless a // By default it serves on :8080 unless a
// PORT environment variable was defined. // PORT environment variable was defined.
router.Run() router.Run()
// router.Run.Run(":3000") for a hard coded port // router.Run(":3000") for a hard coded port
} }
``` ```
@ -211,6 +218,32 @@ func main() {
id: 1234; page: 1; name: manu; message: this_is_great id: 1234; page: 1; name: manu; message: this_is_great
``` ```
### Another example: upload file
References issue [#548](https://github.com/gin-gonic/gin/issues/548).
```go
func main() {
router := gin.Default()
router.POST("/upload", func(c *gin.Context) {
file, header , err := c.Request.FormFile("upload")
filename := header.Filename
fmt.Println(header.Filename)
out, err := os.Create("./tmp/"+filename+".png")
if err != nil {
log.Fatal(err)
}
defer out.Close()
_, err = io.Copy(out, file)
if err != nil {
log.Fatal(err)
}
})
router.Run(":8080")
}
```
#### Grouping routes #### Grouping routes
```go ```go
@ -267,7 +300,7 @@ func main() {
// Authorization group // Authorization group
// authorized := r.Group("/", AuthRequired()) // authorized := r.Group("/", AuthRequired())
// exactly the same than: // exactly the same as:
authorized := r.Group("/") authorized := r.Group("/")
// per group middleware! in this case we use the custom created // per group middleware! in this case we use the custom created
// AuthRequired() middleware just in the "authorized" group. // AuthRequired() middleware just in the "authorized" group.
@ -583,7 +616,7 @@ func main() {
// /admin/secrets endpoint // /admin/secrets endpoint
// hit "localhost:8080/admin/secrets // hit "localhost:8080/admin/secrets
authorized.GET("/secrets", func(c *gin.Context) { authorized.GET("/secrets", func(c *gin.Context) {
// get user, it was setted by the BasicAuth middleware // get user, it was set by the BasicAuth middleware
user := c.MustGet(gin.AuthUserKey).(string) user := c.MustGet(gin.AuthUserKey).(string)
if secret, ok := secrets[user]; ok { if secret, ok := secrets[user]; ok {
c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret}) c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
@ -612,7 +645,7 @@ func main() {
// simulate a long task with time.Sleep(). 5 seconds // simulate a long task with time.Sleep(). 5 seconds
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
// note than you are using the copied context "c_cp", IMPORTANT // note that you are using the copied context "cCp", IMPORTANT
log.Println("Done! in path " + cCp.Request.URL.Path) log.Println("Done! in path " + cCp.Request.URL.Path)
}() }()
}) })
@ -660,18 +693,17 @@ func main() {
#### Graceful restart or stop #### Graceful restart or stop
Do you want to graceful restart or stop your web server? Do you want to graceful restart or stop your web server?
There be some ways. There are some ways this can be done.
We can using fvbock/endless to replace the default ListenAndServe We can use [fvbock/endless](https://github.com/fvbock/endless) to replace the default `ListenAndServe`. Refer issue [#296](https://github.com/gin-gonic/gin/issues/296) for more details.
Refer the issue for more details:
https://github.com/gin-gonic/gin/issues/296
```go ```go
router := gin.Default() router := gin.Default()
router.GET("/", handler) router.GET("/", handler)
// [...] // [...]
endless.ListenAndServe(":4242", router) endless.ListenAndServe(":4242", router)
``` ```
An alternative to endless:
* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully.

View File

@ -139,6 +139,28 @@ func TestValidationDisabled(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestExistsSucceeds(t *testing.T) {
type HogeStruct struct {
Hoge *int `json:"hoge" binding:"exists"`
}
var obj HogeStruct
req := requestWithBody("POST", "/", `{"hoge": 0}`)
err := JSON.Bind(req, &obj)
assert.NoError(t, err)
}
func TestExistsFails(t *testing.T) {
type HogeStruct struct {
Hoge *int `json:"foo" binding:"exists"`
}
var obj HogeStruct
req := requestWithBody("POST", "/", `{"boen": 0}`)
err := JSON.Bind(req, &obj)
assert.Error(t, err)
}
func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) { func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) {
b := Form b := Form
assert.Equal(t, b.Name(), "form") assert.Equal(t, b.Name(), "form")

View File

@ -77,7 +77,7 @@ func (c *Context) Copy() *Context {
return &cp return &cp
} }
// HandlerName returns the main handle's name. For example if the handler is "handleGetUsers()", this // HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", this
// function will return "main.handleGetUsers" // function will return "main.handleGetUsers"
func (c *Context) HandlerName() string { func (c *Context) HandlerName() string {
return nameOfFunction(c.handlers.Last()) return nameOfFunction(c.handlers.Last())
@ -98,7 +98,7 @@ func (c *Context) Next() {
} }
} }
// IsAborted returns true if the currect context was aborted. // IsAborted returns true if the current context was aborted.
func (c *Context) IsAborted() bool { func (c *Context) IsAborted() bool {
return c.index >= abortIndex return c.index >= abortIndex
} }
@ -279,7 +279,7 @@ func (c *Context) GetPostForm(key string) (string, bool) {
// "application/json" --> JSON binding // "application/json" --> JSON binding
// "application/xml" --> XML binding // "application/xml" --> XML binding
// otherwise --> returns an error // otherwise --> returns an error
// If Parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. // It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
// It decodes the json payload into the struct specified as a pointer. // It decodes the json payload into the struct specified as a pointer.
// Like ParseBody() but this method also writes a 400 error if the json is not valid. // Like ParseBody() but this method also writes a 400 error if the json is not valid.
func (c *Context) Bind(obj interface{}) error { func (c *Context) Bind(obj interface{}) error {

View File

@ -1,8 +1,8 @@
package hello package hello
import ( import (
"net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http"
) )
// This function's name is a must. App Engine uses it to drive the requests properly. // This function's name is a must. App Engine uses it to drive the requests properly.
@ -11,10 +11,10 @@ func init() {
r := gin.New() r := gin.New()
// Define your handlers // Define your handlers
r.GET("/", func(c *gin.Context){ r.GET("/", func(c *gin.Context) {
c.String(200, "Hello World!") c.String(200, "Hello World!")
}) })
r.GET("/ping", func(c *gin.Context){ r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong") c.String(200, "pong")
}) })

View File

@ -11,6 +11,7 @@ import (
"time" "time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"net/http/httptest"
) )
func testRequest(t *testing.T, url string) { func testRequest(t *testing.T, url string) {
@ -103,3 +104,28 @@ func TestBadUnixSocket(t *testing.T) {
router := New() router := New()
assert.Error(t, router.RunUnix("#/tmp/unix_unit_test")) assert.Error(t, router.RunUnix("#/tmp/unix_unit_test"))
} }
func TestWithHttptestWithAutoSelectedPort(t *testing.T) {
router := New()
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
ts := httptest.NewServer(router)
defer ts.Close()
testRequest(t, ts.URL+"/example")
}
func TestWithHttptestWithSpecifiedPort(t *testing.T) {
router := New()
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
l, _ := net.Listen("tcp", ":8033")
ts := httptest.Server{
Listener: l,
Config: &http.Server{Handler: router},
}
ts.Start()
defer ts.Close()
testRequest(t, "http://localhost:8033/example")
}

View File

@ -244,7 +244,7 @@ func TestListOfRoutes(t *testing.T) {
func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo) { func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo) {
for _, gotRoute := range gotRoutes { for _, gotRoute := range gotRoutes {
if gotRoute.Path == wantRoute.Path && gotRoute.Method == wantRoute.Method { if gotRoute.Path == wantRoute.Path && gotRoute.Method == wantRoute.Method {
assert.Regexp(t, wantRoute.Path, gotRoute.Path) assert.Regexp(t, wantRoute.Handler, gotRoute.Handler)
return return
} }
} }