mirror of https://github.com/gin-gonic/gin.git
Push branch develop into master
This commit is contained in:
commit
7d0b329203
|
@ -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:
|
||||||
|
|
70
README.md
70
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)
|
[![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.
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,13 +11,13 @@ 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")
|
||||||
})
|
})
|
||||||
|
|
||||||
// Handle all requests using net/http
|
// Handle all requests using net/http
|
||||||
http.Handle("/", r)
|
http.Handle("/", r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue