v1.1 (#751)
* Implement QueryArray and PostArray methods * Refactor GetQuery and GetPostForm * Removed additional Iota I think assigning iota to each constant is not required * Add 1.7 test. * Add codecov.io * corrected a typo in README * remove coveralls services. Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> * Update TravisCI to Gitter webhook * Add codecov.yml Gitter webhook * Changed imports to gopkg instead of github in README (#733) * Add contribution guide * Update go get for stable version In the future, github default branch will be develop so running `go get github.com/gin-gonic/gin` will pull latest code from develop. * Changed imports to gopkg instead of github in README * Update README.md * Logger: skip ANSI color commands if output is not a tty
This commit is contained in:
parent
bb159f9260
commit
32cab500ec
@ -8,17 +8,15 @@ go:
|
|||||||
- tip
|
- tip
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- go get golang.org/x/tools/cmd/cover
|
|
||||||
- go get github.com/mattn/goveralls
|
|
||||||
- go test -v -covermode=count -coverprofile=coverage.out
|
- go test -v -covermode=count -coverprofile=coverage.out
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- goveralls -coverprofile=coverage.out -service=travis-ci -repotoken yFj7FrCeddvBzUaaCyG33jCLfWXeb93eA
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
webhooks:
|
webhooks:
|
||||||
urls:
|
urls:
|
||||||
- https://webhooks.gitter.im/e/acc2c57482e94b44f557
|
- https://webhooks.gitter.im/e/7f95bf605c4d356372f4
|
||||||
on_success: change # options: [always|never|change] default: always
|
on_success: change # options: [always|never|change] default: always
|
||||||
on_failure: always # options: [always|never|change] default: always
|
on_failure: always # options: [always|never|change] default: always
|
||||||
on_start: false # default: false
|
on_start: false # default: false
|
||||||
|
26
README.md
26
README.md
@ -1,16 +1,15 @@
|
|||||||
|
|
||||||
#Gin Web Framework
|
#Gin Web Framework
|
||||||
|
|
||||||
<img align="right" src="https://raw.githubusercontent.com/gin-gonic/gin/master/logo.jpg">
|
<img align="right" src="https://raw.githubusercontent.com/gin-gonic/gin/master/logo.jpg">
|
||||||
[![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin)
|
[![Build Status](https://travis-ci.org/gin-gonic/gin.svg)](https://travis-ci.org/gin-gonic/gin)
|
||||||
[![Coverage Status](https://coveralls.io/repos/gin-gonic/gin/badge.svg?branch=master)](https://coveralls.io/r/gin-gonic/gin?branch=master)
|
[![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)
|
[![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)
|
[![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 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.
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
![Gin console logger](https://gin-gonic.github.io/gin/other/console.png)
|
![Gin console logger](https://gin-gonic.github.io/gin/other/console.png)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@ -19,7 +18,7 @@ $ cat test.go
|
|||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/gin-gonic/gin"
|
import "gopkg.in/gin-gonic/gin.v1"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
@ -28,7 +27,7 @@ func main() {
|
|||||||
"message": "pong",
|
"message": "pong",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
r.Run() // listen and server on 0.0.0.0:8080
|
r.Run() // listen and serve on 0.0.0.0:8080
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -317,7 +316,7 @@ func main() {
|
|||||||
testing.GET("/analytics", analyticsEndpoint)
|
testing.GET("/analytics", analyticsEndpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen and server on 0.0.0.0:8080
|
// Listen and serve on 0.0.0.0:8080
|
||||||
r.Run(":8080")
|
r.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -367,7 +366,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Listen and server on 0.0.0.0:8080
|
// Listen and serve on 0.0.0.0:8080
|
||||||
router.Run(":8080")
|
router.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -378,8 +377,7 @@ func main() {
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gin-gonic/gin"
|
"gopkg.in/gin-gonic/gin.v1"
|
||||||
"github.com/gin-gonic/gin/binding"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginForm struct {
|
type LoginForm struct {
|
||||||
@ -447,7 +445,7 @@ func main() {
|
|||||||
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
|
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Listen and server on 0.0.0.0:8080
|
// Listen and serve on 0.0.0.0:8080
|
||||||
r.Run(":8080")
|
r.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -461,7 +459,7 @@ func main() {
|
|||||||
router.StaticFS("/more_static", http.Dir("my_file_system"))
|
router.StaticFS("/more_static", http.Dir("my_file_system"))
|
||||||
router.StaticFile("/favicon.ico", "./resources/favicon.ico")
|
router.StaticFile("/favicon.ico", "./resources/favicon.ico")
|
||||||
|
|
||||||
// Listen and server on 0.0.0.0:8080
|
// Listen and serve on 0.0.0.0:8080
|
||||||
router.Run(":8080")
|
router.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -593,7 +591,7 @@ func main() {
|
|||||||
log.Println(example)
|
log.Println(example)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Listen and server on 0.0.0.0:8080
|
// Listen and serve on 0.0.0.0:8080
|
||||||
r.Run(":8080")
|
r.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -631,7 +629,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Listen and server on 0.0.0.0:8080
|
// Listen and serve on 0.0.0.0:8080
|
||||||
r.Run(":8080")
|
r.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -664,7 +662,7 @@ func main() {
|
|||||||
log.Println("Done! in path " + c.Request.URL.Path)
|
log.Println("Done! in path " + c.Request.URL.Path)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Listen and server on 0.0.0.0:8080
|
// Listen and serve on 0.0.0.0:8080
|
||||||
r.Run(":8080")
|
r.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
5
codecov.yml
Normal file
5
codecov.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
coverage:
|
||||||
|
notify:
|
||||||
|
gitter:
|
||||||
|
default:
|
||||||
|
url: https://webhooks.gitter.im/e/d90dcdeeab2f1e357165
|
45
context.go
45
context.go
@ -230,13 +230,29 @@ func (c *Context) DefaultQuery(key, defaultValue string) string {
|
|||||||
// ("", false) == c.GetQuery("id")
|
// ("", false) == c.GetQuery("id")
|
||||||
// ("", true) == c.GetQuery("lastname")
|
// ("", true) == c.GetQuery("lastname")
|
||||||
func (c *Context) GetQuery(key string) (string, bool) {
|
func (c *Context) GetQuery(key string) (string, bool) {
|
||||||
req := c.Request
|
if values, ok := c.GetQueryArray(key); ok {
|
||||||
if values, ok := req.URL.Query()[key]; ok && len(values) > 0 {
|
return values[0], ok
|
||||||
return values[0], true
|
|
||||||
}
|
}
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryArray returns a slice of strings for a given query key.
|
||||||
|
// The length of the slice depends on the number of params with the given key.
|
||||||
|
func (c *Context) QueryArray(key string) []string {
|
||||||
|
values, _ := c.GetQueryArray(key)
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQueryArray returns a slice of strings for a given query key, plus
|
||||||
|
// a boolean value whether at least one value exists for the given key.
|
||||||
|
func (c *Context) GetQueryArray(key string) ([]string, bool) {
|
||||||
|
req := c.Request
|
||||||
|
if values, ok := req.URL.Query()[key]; ok && len(values) > 0 {
|
||||||
|
return values, true
|
||||||
|
}
|
||||||
|
return []string{}, false
|
||||||
|
}
|
||||||
|
|
||||||
// PostForm returns the specified key from a POST urlencoded form or multipart form
|
// PostForm returns the specified key from a POST urlencoded form or multipart form
|
||||||
// when it exists, otherwise it returns an empty string `("")`.
|
// when it exists, otherwise it returns an empty string `("")`.
|
||||||
func (c *Context) PostForm(key string) string {
|
func (c *Context) PostForm(key string) string {
|
||||||
@ -262,17 +278,34 @@ func (c *Context) DefaultPostForm(key, defaultValue string) string {
|
|||||||
// email= --> ("", true) := GetPostForm("email") // set email to ""
|
// email= --> ("", true) := GetPostForm("email") // set email to ""
|
||||||
// --> ("", false) := GetPostForm("email") // do nothing with email
|
// --> ("", false) := GetPostForm("email") // do nothing with email
|
||||||
func (c *Context) GetPostForm(key string) (string, bool) {
|
func (c *Context) GetPostForm(key string) (string, bool) {
|
||||||
|
if values, ok := c.GetPostFormArray(key); ok {
|
||||||
|
return values[0], ok
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostFormArray returns a slice of strings for a given form key.
|
||||||
|
// The length of the slice depends on the number of params with the given key.
|
||||||
|
func (c *Context) PostFormArray(key string) []string {
|
||||||
|
values, _ := c.GetPostFormArray(key)
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPostFormArray returns a slice of strings for a given form key, plus
|
||||||
|
// a boolean value whether at least one value exists for the given key.
|
||||||
|
func (c *Context) GetPostFormArray(key string) ([]string, bool) {
|
||||||
req := c.Request
|
req := c.Request
|
||||||
|
req.ParseForm()
|
||||||
req.ParseMultipartForm(32 << 20) // 32 MB
|
req.ParseMultipartForm(32 << 20) // 32 MB
|
||||||
if values := req.PostForm[key]; len(values) > 0 {
|
if values := req.PostForm[key]; len(values) > 0 {
|
||||||
return values[0], true
|
return values, true
|
||||||
}
|
}
|
||||||
if req.MultipartForm != nil && req.MultipartForm.File != nil {
|
if req.MultipartForm != nil && req.MultipartForm.File != nil {
|
||||||
if values := req.MultipartForm.Value[key]; len(values) > 0 {
|
if values := req.MultipartForm.Value[key]; len(values) > 0 {
|
||||||
return values[0], true
|
return values, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", false
|
return []string{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind checks the Content-Type to select a binding engine automatically,
|
// Bind checks the Content-Type to select a binding engine automatically,
|
||||||
|
@ -251,6 +251,22 @@ func TestContextQueryAndPostForm(t *testing.T) {
|
|||||||
assert.Equal(t, obj.Page, 11)
|
assert.Equal(t, obj.Page, 11)
|
||||||
assert.Equal(t, obj.Both, "")
|
assert.Equal(t, obj.Both, "")
|
||||||
assert.Equal(t, obj.Array, []string{"first", "second"})
|
assert.Equal(t, obj.Array, []string{"first", "second"})
|
||||||
|
|
||||||
|
values, ok := c.GetQueryArray("array[]")
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, "first", values[0])
|
||||||
|
assert.Equal(t, "second", values[1])
|
||||||
|
|
||||||
|
values = c.QueryArray("array[]")
|
||||||
|
assert.Equal(t, "first", values[0])
|
||||||
|
assert.Equal(t, "second", values[1])
|
||||||
|
|
||||||
|
values = c.QueryArray("nokey")
|
||||||
|
assert.Equal(t, 0, len(values))
|
||||||
|
|
||||||
|
values = c.QueryArray("both")
|
||||||
|
assert.Equal(t, 1, len(values))
|
||||||
|
assert.Equal(t, "GET", values[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextPostFormMultipart(t *testing.T) {
|
func TestContextPostFormMultipart(t *testing.T) {
|
||||||
@ -299,6 +315,22 @@ func TestContextPostFormMultipart(t *testing.T) {
|
|||||||
assert.False(t, ok)
|
assert.False(t, ok)
|
||||||
assert.Empty(t, value)
|
assert.Empty(t, value)
|
||||||
assert.Equal(t, c.DefaultPostForm("nokey", "nothing"), "nothing")
|
assert.Equal(t, c.DefaultPostForm("nokey", "nothing"), "nothing")
|
||||||
|
|
||||||
|
values, ok := c.GetPostFormArray("array")
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, "first", values[0])
|
||||||
|
assert.Equal(t, "second", values[1])
|
||||||
|
|
||||||
|
values = c.PostFormArray("array")
|
||||||
|
assert.Equal(t, "first", values[0])
|
||||||
|
assert.Equal(t, "second", values[1])
|
||||||
|
|
||||||
|
values = c.PostFormArray("nokey")
|
||||||
|
assert.Equal(t, 0, len(values))
|
||||||
|
|
||||||
|
values = c.PostFormArray("foo")
|
||||||
|
assert.Equal(t, 1, len(values))
|
||||||
|
assert.Equal(t, "bar", values[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextSetCookie(t *testing.T) {
|
func TestContextSetCookie(t *testing.T) {
|
||||||
|
15
logger.go
15
logger.go
@ -7,7 +7,10 @@ package gin
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -44,6 +47,11 @@ func Logger() HandlerFunc {
|
|||||||
// LoggerWithWriter instance a Logger middleware with the specified writter buffer.
|
// LoggerWithWriter instance a Logger middleware with the specified writter buffer.
|
||||||
// Example: os.Stdout, a file opened in write mode, a socket...
|
// Example: os.Stdout, a file opened in write mode, a socket...
|
||||||
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
||||||
|
isTerm := true
|
||||||
|
if outFile, ok := out.(*os.File); ok {
|
||||||
|
isTerm = terminal.IsTerminal(int(outFile.Fd()))
|
||||||
|
}
|
||||||
|
|
||||||
var skip map[string]struct{}
|
var skip map[string]struct{}
|
||||||
|
|
||||||
if length := len(notlogged); length > 0 {
|
if length := len(notlogged); length > 0 {
|
||||||
@ -71,8 +79,11 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
|||||||
clientIP := c.ClientIP()
|
clientIP := c.ClientIP()
|
||||||
method := c.Request.Method
|
method := c.Request.Method
|
||||||
statusCode := c.Writer.Status()
|
statusCode := c.Writer.Status()
|
||||||
statusColor := colorForStatus(statusCode)
|
var statusColor, methodColor string
|
||||||
methodColor := colorForMethod(method)
|
if isTerm {
|
||||||
|
statusColor = colorForStatus(statusCode)
|
||||||
|
methodColor = colorForMethod(method)
|
||||||
|
}
|
||||||
comment := c.Errors.ByType(ErrorTypePrivate).String()
|
comment := c.Errors.ByType(ErrorTypePrivate).String()
|
||||||
|
|
||||||
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %s |%s %s %-7s %s\n%s",
|
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %s |%s %s %-7s %s\n%s",
|
||||||
|
4
mode.go
4
mode.go
@ -20,8 +20,8 @@ const (
|
|||||||
)
|
)
|
||||||
const (
|
const (
|
||||||
debugCode = iota
|
debugCode = iota
|
||||||
releaseCode = iota
|
releaseCode
|
||||||
testCode = iota
|
testCode
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultWriter is the default io.Writer used the Gin for debug output and
|
// DefaultWriter is the default io.Writer used the Gin for debug output and
|
||||||
|
Loading…
Reference in New Issue
Block a user