diff --git a/.travis.yml b/.travis.yml index 695f0b7e..2f78c8af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,8 @@ language: go sudo: false go: - 1.4 - - 1.4.2 + - 1.5 + - 1.6 - tip script: diff --git a/README.md b/README.md index 2a111d29..47ebbed7 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![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 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.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ - "message": "hello world", + "message": "pong", }) }) r.Run() // listen and server on 0.0.0.0:8080 @@ -85,14 +85,21 @@ BenchmarkZeus_GithubAll | 2000 | 944234 | 300688 | 2648 ## Start using it 1. Download and install it: -```sh -$ go get github.com/gin-gonic/gin -``` + ```sh + $ go get github.com/gin-gonic/gin + ``` + 2. Import it in your code: -```go -import "github.com/gin-gonic/gin" -``` + ```go + 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 @@ -115,7 +122,7 @@ func main() { // By default it serves on :8080 unless a // PORT environment variable was defined. 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 ``` +### 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 ```go @@ -267,7 +300,7 @@ func main() { // Authorization group // authorized := r.Group("/", AuthRequired()) - // exactly the same than: + // exactly the same as: authorized := r.Group("/") // per group middleware! in this case we use the custom created // AuthRequired() middleware just in the "authorized" group. @@ -583,7 +616,7 @@ func main() { // /admin/secrets endpoint // hit "localhost:8080/admin/secrets 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) if secret, ok := secrets[user]; ok { 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 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) }() }) @@ -660,18 +693,17 @@ func main() { #### Graceful restart or stop 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 - -Refer the issue for more details: - -https://github.com/gin-gonic/gin/issues/296 +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. ```go router := gin.Default() router.GET("/", handler) // [...] endless.ListenAndServe(":4242", router) - ``` + +An alternative to endless: + +* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully. diff --git a/binding/binding_test.go b/binding/binding_test.go index 1024e49d..72f60152 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -139,6 +139,28 @@ func TestValidationDisabled(t *testing.T) { 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) { b := Form assert.Equal(t, b.Name(), "form") diff --git a/context.go b/context.go index 2fb69b73..b043d1b4 100644 --- a/context.go +++ b/context.go @@ -77,7 +77,7 @@ func (c *Context) Copy() *Context { 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" func (c *Context) HandlerName() string { 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 { return c.index >= abortIndex } @@ -279,7 +279,7 @@ func (c *Context) GetPostForm(key string) (string, bool) { // "application/json" --> JSON binding // "application/xml" --> XML binding // 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. // Like ParseBody() but this method also writes a 400 error if the json is not valid. func (c *Context) Bind(obj interface{}) error { diff --git a/examples/app-engine/hello.go b/examples/app-engine/hello.go index f5daf824..a5e17962 100644 --- a/examples/app-engine/hello.go +++ b/examples/app-engine/hello.go @@ -1,8 +1,8 @@ package hello import ( - "net/http" "github.com/gin-gonic/gin" + "net/http" ) // This function's name is a must. App Engine uses it to drive the requests properly. @@ -11,13 +11,13 @@ func init() { r := gin.New() // Define your handlers - r.GET("/", func(c *gin.Context){ + r.GET("/", func(c *gin.Context) { c.String(200, "Hello World!") }) - r.GET("/ping", func(c *gin.Context){ + r.GET("/ping", func(c *gin.Context) { c.String(200, "pong") }) // Handle all requests using net/http http.Handle("/", r) -} \ No newline at end of file +} diff --git a/gin_integration_test.go b/gin_integration_test.go index 4777c0c9..85216970 100644 --- a/gin_integration_test.go +++ b/gin_integration_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "net/http/httptest" ) func testRequest(t *testing.T, url string) { @@ -103,3 +104,28 @@ func TestBadUnixSocket(t *testing.T) { router := New() 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") +} diff --git a/gin_test.go b/gin_test.go index 15f480e4..af95ab84 100644 --- a/gin_test.go +++ b/gin_test.go @@ -244,7 +244,7 @@ func TestListOfRoutes(t *testing.T) { func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo) { for _, gotRoute := range gotRoutes { if gotRoute.Path == wantRoute.Path && gotRoute.Method == wantRoute.Method { - assert.Regexp(t, wantRoute.Path, gotRoute.Path) + assert.Regexp(t, wantRoute.Handler, gotRoute.Handler) return } } diff --git a/test_helpers.go b/helpers_test.go similarity index 100% rename from test_helpers.go rename to helpers_test.go