update gin to v1.2
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
This commit is contained in:
parent
6864ffe9f0
commit
b9104de2c5
|
@ -1,46 +0,0 @@
|
||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment include:
|
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
|
||||||
* Being respectful of differing viewpoints and experiences
|
|
||||||
* Gracefully accepting constructive criticism
|
|
||||||
* Focusing on what is best for the community
|
|
||||||
* Showing empathy towards other community members
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
||||||
|
|
||||||
## Our Responsibilities
|
|
||||||
|
|
||||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
|
||||||
|
|
||||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at teamgingonic@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
|
||||||
|
|
||||||
[homepage]: http://contributor-covenant.org
|
|
||||||
[version]: http://contributor-covenant.org/version/1/4/
|
|
|
@ -1,13 +0,0 @@
|
||||||
## Contributing
|
|
||||||
|
|
||||||
- With issues:
|
|
||||||
- Use the search tool before opening a new issue.
|
|
||||||
- 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 `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.
|
|
||||||
- If your pull request contains a new feature, please document it on the README.
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Gin Web Framework
|
# Gin Web Framework
|
||||||
|
|
||||||
<img align="right" width="159px" src="https://raw.githubusercontent.com/gin-gonic/logo/master/color.png">
|
<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)
|
||||||
[![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin)
|
[![codecov](https://codecov.io/gh/gin-gonic/gin/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-gonic/gin)
|
||||||
|
@ -13,8 +13,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
|
||||||
![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
|
||||||
# assume the following codes in example.go file
|
$ cat test.go
|
||||||
$ cat example.go
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -33,11 +32,6 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
|
||||||
# run example.go and visit 0.0.0.0:8080/ping on browser
|
|
||||||
$ go run example.go
|
|
||||||
```
|
|
||||||
|
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
|
||||||
Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter)
|
Gin uses a custom version of [HttpRouter](https://github.com/julienschmidt/httprouter)
|
||||||
|
@ -109,38 +103,6 @@ import "github.com/gin-gonic/gin"
|
||||||
import "net/http"
|
import "net/http"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Use a vendor tool like [Govendor](https://github.com/kardianos/govendor)
|
|
||||||
|
|
||||||
1. `go get` govendor
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ 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 fetch github.com/gin-gonic/gin@v1.2
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Copy a starting template inside your project
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ curl https://raw.githubusercontent.com/gin-gonic/gin/master/examples/basic/main.go > main.go
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Run your project
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ go run main.go
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Examples
|
## API Examples
|
||||||
|
|
||||||
### Using GET, POST, PUT, PATCH, DELETE and OPTIONS
|
### Using GET, POST, PUT, PATCH, DELETE and OPTIONS
|
||||||
|
@ -276,7 +238,7 @@ func main() {
|
||||||
file, _ := c.FormFile("file")
|
file, _ := c.FormFile("file")
|
||||||
log.Println(file.Filename)
|
log.Println(file.Filename)
|
||||||
|
|
||||||
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
|
c.String(http.StatusOK, fmt.Printf("'%s' uploaded!", file.Filename))
|
||||||
})
|
})
|
||||||
router.Run(":8080")
|
router.Run(":8080")
|
||||||
}
|
}
|
||||||
|
@ -305,7 +267,7 @@ func main() {
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
log.Println(file.Filename)
|
log.Println(file.Filename)
|
||||||
}
|
}
|
||||||
c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
|
c.String(http.StatusOK, fmt.Printf("%d files uploaded!", len(files)))
|
||||||
})
|
})
|
||||||
router.Run(":8080")
|
router.Run(":8080")
|
||||||
}
|
}
|
||||||
|
@ -471,7 +433,7 @@ func startPage(c *gin.Context) {
|
||||||
var person Person
|
var person Person
|
||||||
// If `GET`, only `Form` binding engine (`query`) used.
|
// If `GET`, only `Form` binding engine (`query`) used.
|
||||||
// If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`).
|
// If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`).
|
||||||
// See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48
|
// See more at https://github.com/gin-gonic/gin/blob/develop/binding/binding.go#L45
|
||||||
if c.Bind(&person) == nil {
|
if c.Bind(&person) == nil {
|
||||||
log.Println(person.Name)
|
log.Println(person.Name)
|
||||||
log.Println(person.Address)
|
log.Println(person.Address)
|
||||||
|
@ -481,52 +443,6 @@ func startPage(c *gin.Context) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Bind HTML checkboxes
|
|
||||||
|
|
||||||
See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092)
|
|
||||||
|
|
||||||
main.go
|
|
||||||
|
|
||||||
```go
|
|
||||||
...
|
|
||||||
|
|
||||||
type myForm struct {
|
|
||||||
Colors []string `form:"colors[]"`
|
|
||||||
}
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
func formHandler(c *gin.Context) {
|
|
||||||
var fakeForm myForm
|
|
||||||
c.Bind(&fakeForm)
|
|
||||||
c.JSON(200, gin.H{"color": fakeForm.Colors})
|
|
||||||
}
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
form.html
|
|
||||||
|
|
||||||
```html
|
|
||||||
<form action="/" method="POST">
|
|
||||||
<p>Check some colors</p>
|
|
||||||
<label for="red">Red</label>
|
|
||||||
<input type="checkbox" name="colors[]" value="red" id="red" />
|
|
||||||
<label for="green">Green</label>
|
|
||||||
<input type="checkbox" name="colors[]" value="green" id="green" />
|
|
||||||
<label for="blue">Blue</label>
|
|
||||||
<input type="checkbox" name="colors[]" value="blue" id="blue" />
|
|
||||||
<input type="submit" />
|
|
||||||
</form>
|
|
||||||
```
|
|
||||||
|
|
||||||
result:
|
|
||||||
|
|
||||||
```
|
|
||||||
{"color":["red","green","blue"]}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multipart/Urlencoded binding
|
### Multipart/Urlencoded binding
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -605,29 +521,6 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### SecureJSON
|
|
||||||
|
|
||||||
Using SecureJSON to prevent json hijacking. Default prepends `"while(1),"` to response body if the given struct is array values.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func main() {
|
|
||||||
r := gin.Default()
|
|
||||||
|
|
||||||
// You can also use your own secure json prefix
|
|
||||||
// r.SecureJsonPrefix(")]}',\n")
|
|
||||||
|
|
||||||
r.GET("/someJSON", func(c *gin.Context) {
|
|
||||||
names := []string{"lena", "austin", "foo"}
|
|
||||||
|
|
||||||
// Will output : while(1);["lena","austin","foo"]
|
|
||||||
c.SecureJSON(http.StatusOK, names)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Listen and serve on 0.0.0.0:8080
|
|
||||||
r.Run(":8080")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Serving static files
|
### Serving static files
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -714,8 +607,6 @@ templates/users/index.tmpl
|
||||||
{{ end }}
|
{{ end }}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Custom Template renderer
|
|
||||||
|
|
||||||
You can also use your own html template render
|
You can also use your own html template render
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -729,8 +620,6 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Custom Delimiters
|
|
||||||
|
|
||||||
You may use custom delims
|
You may use custom delims
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -739,7 +628,7 @@ You may use custom delims
|
||||||
r.LoadHTMLGlob("/path/to/templates"))
|
r.LoadHTMLGlob("/path/to/templates"))
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Custom Template Funcs
|
#### Add custom template funcs
|
||||||
|
|
||||||
main.go
|
main.go
|
||||||
|
|
||||||
|
@ -989,7 +878,7 @@ func main() {
|
||||||
Cache: autocert.DirCache("/var/www/.cache"),
|
Cache: autocert.DirCache("/var/www/.cache"),
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Fatal(autotls.RunWithManager(r, &m))
|
log.Fatal(autotls.RunWithManager(r, m))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1067,7 +956,20 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Users [![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge)
|
## Contributing
|
||||||
|
|
||||||
|
- With issues:
|
||||||
|
- Use the search tool before opening a new issue.
|
||||||
|
- 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
|
||||||
|
- 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.
|
||||||
|
- If your pull request contains a new feature, please document it on the README.
|
||||||
|
|
||||||
|
## Users
|
||||||
|
|
||||||
Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework.
|
Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework.
|
||||||
|
|
||||||
|
|
|
@ -10,18 +10,16 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthUserKey is the cookie name for user credential in basic auth
|
|
||||||
const AuthUserKey = "user"
|
const AuthUserKey = "user"
|
||||||
|
|
||||||
// Accounts defines a key/value for user/pass list of authorized logins
|
type (
|
||||||
type Accounts map[string]string
|
Accounts map[string]string
|
||||||
|
authPair struct {
|
||||||
type authPair struct {
|
Value string
|
||||||
Value string
|
User string
|
||||||
User string
|
}
|
||||||
}
|
authPairs []authPair
|
||||||
|
)
|
||||||
type authPairs []authPair
|
|
||||||
|
|
||||||
func (a authPairs) searchCredential(authValue string) (string, bool) {
|
func (a authPairs) searchCredential(authValue string) (string, bool) {
|
||||||
if len(authValue) == 0 {
|
if len(authValue) == 0 {
|
||||||
|
@ -89,6 +87,6 @@ func secureCompare(given, actual string) bool {
|
||||||
if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
|
if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
|
||||||
return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
|
return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
|
||||||
}
|
}
|
||||||
// Securely compare actual to itself to keep constant time, but always return false
|
/* Securely compare actual to itself to keep constant time, but always return false */
|
||||||
return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
|
return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,15 +48,9 @@ type Context struct {
|
||||||
handlers HandlersChain
|
handlers HandlersChain
|
||||||
index int8
|
index int8
|
||||||
|
|
||||||
engine *Engine
|
engine *Engine
|
||||||
|
Keys map[string]interface{}
|
||||||
// Keys is a key/value pair exclusively for the context of each request
|
Errors errorMsgs
|
||||||
Keys map[string]interface{}
|
|
||||||
|
|
||||||
// Errors is a list of errors attached to all the handlers/middlewares who used this context
|
|
||||||
Errors errorMsgs
|
|
||||||
|
|
||||||
// Accepted defines a list of manually accepted formats for content negotiation
|
|
||||||
Accepted []string
|
Accepted []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,8 +79,8 @@ func (c *Context) Copy() *Context {
|
||||||
return &cp
|
return &cp
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()",
|
// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", this
|
||||||
// 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())
|
||||||
}
|
}
|
||||||
|
@ -117,8 +111,8 @@ func (c *Context) IsAborted() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abort prevents pending handlers from being called. Note that this will not stop the current handler.
|
// Abort prevents pending handlers from being called. Note that this will not stop the current handler.
|
||||||
// Let's say you have an authorization middleware that validates that the current request is authorized.
|
// Let's say you have an authorization middleware that validates that the current request is authorized. If the
|
||||||
// If the authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers
|
// authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers
|
||||||
// for this request are not called.
|
// for this request are not called.
|
||||||
func (c *Context) Abort() {
|
func (c *Context) Abort() {
|
||||||
c.index = abortIndex
|
c.index = abortIndex
|
||||||
|
@ -132,16 +126,15 @@ func (c *Context) AbortWithStatus(code int) {
|
||||||
c.Abort()
|
c.Abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbortWithStatusJSON calls `Abort()` and then `JSON` internally.
|
// AbortWithStatusJSON calls `Abort()` and then `JSON` internally. This method stops the chain, writes the status code and return a JSON body
|
||||||
// This method stops the chain, writes the status code and return a JSON body.
|
|
||||||
// It also sets the Content-Type as "application/json".
|
// It also sets the Content-Type as "application/json".
|
||||||
func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) {
|
func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) {
|
||||||
c.Abort()
|
c.Abort()
|
||||||
c.JSON(code, jsonObj)
|
c.JSON(code, jsonObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbortWithError calls `AbortWithStatus()` and `Error()` internally.
|
// AbortWithError calls `AbortWithStatus()` and `Error()` internally. This method stops the chain, writes the status code and
|
||||||
// This method stops the chain, writes the status code and pushes the specified error to `c.Errors`.
|
// pushes the specified error to `c.Errors`.
|
||||||
// See Context.Error() for more details.
|
// See Context.Error() for more details.
|
||||||
func (c *Context) AbortWithError(code int, err error) *Error {
|
func (c *Context) AbortWithError(code int, err error) *Error {
|
||||||
c.AbortWithStatus(code)
|
c.AbortWithStatus(code)
|
||||||
|
@ -152,15 +145,11 @@ func (c *Context) AbortWithError(code int, err error) *Error {
|
||||||
/********* ERROR MANAGEMENT *********/
|
/********* ERROR MANAGEMENT *********/
|
||||||
/************************************/
|
/************************************/
|
||||||
|
|
||||||
// Error attaches an error to the current context. The error is pushed to a list of errors.
|
// Attaches an error to the current context. The error is pushed to a list of errors.
|
||||||
// It's a good idea to call Error for each error that occurred during the resolution of a request.
|
// It's a good idea to call Error for each error that occurred during the resolution of a request.
|
||||||
// A middleware can be used to collect all the errors and push them to a database together,
|
// A middleware can be used to collect all the errors
|
||||||
// print a log, or append it in the HTTP response.
|
// and push them to a database together, print a log, or append it in the HTTP response.
|
||||||
// Error will panic if err is nil.
|
|
||||||
func (c *Context) Error(err error) *Error {
|
func (c *Context) Error(err error) *Error {
|
||||||
if err == nil {
|
|
||||||
panic("err is nil")
|
|
||||||
}
|
|
||||||
var parsedError *Error
|
var parsedError *Error
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case *Error:
|
case *Error:
|
||||||
|
@ -297,10 +286,10 @@ func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string)
|
||||||
|
|
||||||
// Param returns the value of the URL param.
|
// Param returns the value of the URL param.
|
||||||
// It is a shortcut for c.Params.ByName(key)
|
// It is a shortcut for c.Params.ByName(key)
|
||||||
// router.GET("/user/:id", func(c *gin.Context) {
|
// router.GET("/user/:id", func(c *gin.Context) {
|
||||||
// // a GET request to /user/john
|
// // a GET request to /user/john
|
||||||
// id := c.Param("id") // id == "john"
|
// id := c.Param("id") // id == "john"
|
||||||
// })
|
// })
|
||||||
func (c *Context) Param(key string) string {
|
func (c *Context) Param(key string) string {
|
||||||
return c.Params.ByName(key)
|
return c.Params.ByName(key)
|
||||||
}
|
}
|
||||||
|
@ -308,11 +297,11 @@ func (c *Context) Param(key string) string {
|
||||||
// Query returns the keyed url query value if it exists,
|
// Query returns the keyed url query value if it exists,
|
||||||
// otherwise it returns an empty string `("")`.
|
// otherwise it returns an empty string `("")`.
|
||||||
// It is shortcut for `c.Request.URL.Query().Get(key)`
|
// It is shortcut for `c.Request.URL.Query().Get(key)`
|
||||||
// GET /path?id=1234&name=Manu&value=
|
// GET /path?id=1234&name=Manu&value=
|
||||||
// c.Query("id") == "1234"
|
// c.Query("id") == "1234"
|
||||||
// c.Query("name") == "Manu"
|
// c.Query("name") == "Manu"
|
||||||
// c.Query("value") == ""
|
// c.Query("value") == ""
|
||||||
// c.Query("wtf") == ""
|
// c.Query("wtf") == ""
|
||||||
func (c *Context) Query(key string) string {
|
func (c *Context) Query(key string) string {
|
||||||
value, _ := c.GetQuery(key)
|
value, _ := c.GetQuery(key)
|
||||||
return value
|
return value
|
||||||
|
@ -321,10 +310,10 @@ func (c *Context) Query(key string) string {
|
||||||
// DefaultQuery returns the keyed url query value if it exists,
|
// DefaultQuery returns the keyed url query value if it exists,
|
||||||
// otherwise it returns the specified defaultValue string.
|
// otherwise it returns the specified defaultValue string.
|
||||||
// See: Query() and GetQuery() for further information.
|
// See: Query() and GetQuery() for further information.
|
||||||
// GET /?name=Manu&lastname=
|
// GET /?name=Manu&lastname=
|
||||||
// c.DefaultQuery("name", "unknown") == "Manu"
|
// c.DefaultQuery("name", "unknown") == "Manu"
|
||||||
// c.DefaultQuery("id", "none") == "none"
|
// c.DefaultQuery("id", "none") == "none"
|
||||||
// c.DefaultQuery("lastname", "none") == ""
|
// c.DefaultQuery("lastname", "none") == ""
|
||||||
func (c *Context) DefaultQuery(key, defaultValue string) string {
|
func (c *Context) DefaultQuery(key, defaultValue string) string {
|
||||||
if value, ok := c.GetQuery(key); ok {
|
if value, ok := c.GetQuery(key); ok {
|
||||||
return value
|
return value
|
||||||
|
@ -336,10 +325,10 @@ func (c *Context) DefaultQuery(key, defaultValue string) string {
|
||||||
// if it exists `(value, true)` (even when the value is an empty string),
|
// if it exists `(value, true)` (even when the value is an empty string),
|
||||||
// otherwise it returns `("", false)`.
|
// otherwise it returns `("", false)`.
|
||||||
// It is shortcut for `c.Request.URL.Query().Get(key)`
|
// It is shortcut for `c.Request.URL.Query().Get(key)`
|
||||||
// GET /?name=Manu&lastname=
|
// GET /?name=Manu&lastname=
|
||||||
// ("Manu", true) == c.GetQuery("name")
|
// ("Manu", true) == c.GetQuery("name")
|
||||||
// ("", 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) {
|
||||||
if values, ok := c.GetQueryArray(key); ok {
|
if values, ok := c.GetQueryArray(key); ok {
|
||||||
return values[0], ok
|
return values[0], ok
|
||||||
|
@ -385,9 +374,9 @@ func (c *Context) DefaultPostForm(key, defaultValue string) string {
|
||||||
// form or multipart form when it exists `(value, true)` (even when the value is an empty string),
|
// form or multipart form when it exists `(value, true)` (even when the value is an empty string),
|
||||||
// otherwise it returns ("", false).
|
// otherwise it returns ("", false).
|
||||||
// For example, during a PATCH request to update the user's email:
|
// For example, during a PATCH request to update the user's email:
|
||||||
// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com"
|
// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com"
|
||||||
// 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 {
|
if values, ok := c.GetPostFormArray(key); ok {
|
||||||
return values[0], ok
|
return values[0], ok
|
||||||
|
@ -433,8 +422,8 @@ func (c *Context) MultipartForm() (*multipart.Form, error) {
|
||||||
|
|
||||||
// Bind checks the Content-Type to select a binding engine automatically,
|
// Bind checks the Content-Type to select a binding engine automatically,
|
||||||
// Depending the "Content-Type" header different bindings are used:
|
// Depending the "Content-Type" header different bindings are used:
|
||||||
// "application/json" --> JSON binding
|
// "application/json" --> JSON binding
|
||||||
// "application/xml" --> XML binding
|
// "application/xml" --> XML binding
|
||||||
// otherwise --> returns an error
|
// otherwise --> returns an error
|
||||||
// It 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.
|
||||||
|
@ -563,7 +552,15 @@ func (c *Context) GetRawData() ([]byte, error) {
|
||||||
return ioutil.ReadAll(c.Request.Body)
|
return ioutil.ReadAll(c.Request.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {
|
func (c *Context) SetCookie(
|
||||||
|
name string,
|
||||||
|
value string,
|
||||||
|
maxAge int,
|
||||||
|
path string,
|
||||||
|
domain string,
|
||||||
|
secure bool,
|
||||||
|
httpOnly bool,
|
||||||
|
) {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = "/"
|
path = "/"
|
||||||
}
|
}
|
||||||
|
@ -617,13 +614,6 @@ func (c *Context) IndentedJSON(code int, obj interface{}) {
|
||||||
c.Render(code, render.IndentedJSON{Data: obj})
|
c.Render(code, render.IndentedJSON{Data: obj})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecureJSON serializes the given struct as Secure JSON into the response body.
|
|
||||||
// Default prepends "while(1)," to response body if the given struct is array values.
|
|
||||||
// It also sets the Content-Type as "application/json".
|
|
||||||
func (c *Context) SecureJSON(code int, obj interface{}) {
|
|
||||||
c.Render(code, render.SecureJSON{Prefix: c.engine.secureJsonPrefix, Data: obj})
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON serializes the given struct as JSON into the response body.
|
// JSON serializes the given struct as JSON into the response body.
|
||||||
// It also sets the Content-Type as "application/json".
|
// It also sets the Content-Type as "application/json".
|
||||||
func (c *Context) JSON(code int, obj interface{}) {
|
func (c *Context) JSON(code int, obj interface{}) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDebugging returns true if the framework is running in debug mode.
|
// IsDebugging returns true if the framework is running in debug mode.
|
||||||
// Use SetMode(gin.Release) to disable debug mode.
|
// Use SetMode(gin.Release) to switch to disable the debug mode.
|
||||||
func IsDebugging() bool {
|
func IsDebugging() bool {
|
||||||
return ginMode == debugCode
|
return ginMode == debugCode
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,15 @@
|
||||||
package gin
|
package gin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/binding"
|
"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.
|
// BindWith binds the passed struct pointer using the specified binding engine.
|
||||||
// See the binding package.
|
// See the binding package.
|
||||||
func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
|
func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
/*
|
|
||||||
Package gin implements a HTTP web framework called gin.
|
|
||||||
|
|
||||||
See https://gin-gonic.github.io/gin/ for more information about gin.
|
|
||||||
*/
|
|
||||||
package gin // import "github.com/gin-gonic/gin"
|
|
|
@ -6,14 +6,11 @@ package gin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/json-iterator/go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
|
||||||
|
|
||||||
type ErrorType uint64
|
type ErrorType uint64
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -26,13 +23,15 @@ const (
|
||||||
ErrorTypeNu = 2
|
ErrorTypeNu = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
type Error struct {
|
type (
|
||||||
Err error
|
Error struct {
|
||||||
Type ErrorType
|
Err error
|
||||||
Meta interface{}
|
Type ErrorType
|
||||||
}
|
Meta interface{}
|
||||||
|
}
|
||||||
|
|
||||||
type errorMsgs []*Error
|
errorMsgs []*Error
|
||||||
|
)
|
||||||
|
|
||||||
var _ error = &Error{}
|
var _ error = &Error{}
|
||||||
|
|
||||||
|
@ -72,7 +71,7 @@ func (msg *Error) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(msg.JSON())
|
return json.Marshal(msg.JSON())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error implements the error interface
|
// Implements the error interface
|
||||||
func (msg Error) Error() string {
|
func (msg Error) Error() string {
|
||||||
return msg.Err.Error()
|
return msg.Err.Error()
|
||||||
}
|
}
|
||||||
|
@ -81,7 +80,7 @@ func (msg *Error) IsType(flags ErrorType) bool {
|
||||||
return (msg.Type & flags) > 0
|
return (msg.Type & flags) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByType returns a readonly copy filtered the byte.
|
// Returns a readonly copy filtered the byte.
|
||||||
// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic
|
// ie ByType(gin.ErrorTypePublic) returns a slice of errors with type=ErrorTypePublic
|
||||||
func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
|
func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
|
||||||
if len(a) == 0 {
|
if len(a) == 0 {
|
||||||
|
@ -99,16 +98,17 @@ func (a errorMsgs) ByType(typ ErrorType) errorMsgs {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last returns the last error in the slice. It returns nil if the array is empty.
|
// Returns the last error in the slice. It returns nil if the array is empty.
|
||||||
// Shortcut for errors[len(errors)-1]
|
// Shortcut for errors[len(errors)-1]
|
||||||
func (a errorMsgs) Last() *Error {
|
func (a errorMsgs) Last() *Error {
|
||||||
if length := len(a); length > 0 {
|
length := len(a)
|
||||||
|
if length > 0 {
|
||||||
return a[length-1]
|
return a[length-1]
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errors returns an array will all the error messages.
|
// Returns an array will all the error messages.
|
||||||
// Example:
|
// Example:
|
||||||
// c.Error(errors.New("first"))
|
// c.Error(errors.New("first"))
|
||||||
// c.Error(errors.New("second"))
|
// c.Error(errors.New("second"))
|
||||||
|
|
|
@ -9,13 +9,14 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type onlyfilesFS struct {
|
type (
|
||||||
fs http.FileSystem
|
onlyfilesFS struct {
|
||||||
}
|
fs http.FileSystem
|
||||||
|
}
|
||||||
type neuteredReaddirFile struct {
|
neuteredReaddirFile struct {
|
||||||
http.File
|
http.File
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally
|
// Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally
|
||||||
// in router.Static().
|
// in router.Static().
|
||||||
|
@ -29,7 +30,7 @@ func Dir(root string, listDirectory bool) http.FileSystem {
|
||||||
return &onlyfilesFS{fs}
|
return &onlyfilesFS{fs}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open conforms to http.Filesystem
|
// Conforms to http.Filesystem
|
||||||
func (fs onlyfilesFS) Open(name string) (http.File, error) {
|
func (fs onlyfilesFS) Open(name string) (http.File, error) {
|
||||||
f, err := fs.fs.Open(name)
|
f, err := fs.fs.Open(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -38,7 +39,7 @@ func (fs onlyfilesFS) Open(name string) (http.File, error) {
|
||||||
return neuteredReaddirFile{f}, nil
|
return neuteredReaddirFile{f}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Readdir overrides the http.File default implementation
|
// Overrides the http.File default implementation
|
||||||
func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
|
func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
|
||||||
// this disables directory listing
|
// this disables directory listing
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
@ -26,73 +26,74 @@ type HandlersChain []HandlerFunc
|
||||||
|
|
||||||
// Last returns the last handler in the chain. ie. the last handler is the main own.
|
// Last returns the last handler in the chain. ie. the last handler is the main own.
|
||||||
func (c HandlersChain) Last() HandlerFunc {
|
func (c HandlersChain) Last() HandlerFunc {
|
||||||
if length := len(c); length > 0 {
|
length := len(c)
|
||||||
|
if length > 0 {
|
||||||
return c[length-1]
|
return c[length-1]
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type RouteInfo struct {
|
type (
|
||||||
Method string
|
RoutesInfo []RouteInfo
|
||||||
Path string
|
RouteInfo struct {
|
||||||
Handler string
|
Method string
|
||||||
}
|
Path string
|
||||||
|
Handler string
|
||||||
|
}
|
||||||
|
|
||||||
type RoutesInfo []RouteInfo
|
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
|
||||||
|
// 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
|
||||||
|
noMethod HandlersChain
|
||||||
|
pool sync.Pool
|
||||||
|
trees methodTrees
|
||||||
|
|
||||||
// Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
|
// Enables automatic redirection if the current route can't be matched but a
|
||||||
// Create an instance of Engine, by using New() or Default()
|
// handler for the path with (without) the trailing slash exists.
|
||||||
type Engine struct {
|
// For example if /foo/ is requested but a route only exists for /foo, the
|
||||||
RouterGroup
|
// client is redirected to /foo with http status code 301 for GET requests
|
||||||
delims render.Delims
|
// and 307 for all other request methods.
|
||||||
secureJsonPrefix string
|
RedirectTrailingSlash bool
|
||||||
HTMLRender render.HTMLRender
|
|
||||||
FuncMap template.FuncMap
|
|
||||||
allNoRoute HandlersChain
|
|
||||||
allNoMethod HandlersChain
|
|
||||||
noRoute HandlersChain
|
|
||||||
noMethod HandlersChain
|
|
||||||
pool sync.Pool
|
|
||||||
trees methodTrees
|
|
||||||
|
|
||||||
// Enables automatic redirection if the current route can't be matched but a
|
// If enabled, the router tries to fix the current request path, if no
|
||||||
// handler for the path with (without) the trailing slash exists.
|
// handle is registered for it.
|
||||||
// For example if /foo/ is requested but a route only exists for /foo, the
|
// First superfluous path elements like ../ or // are removed.
|
||||||
// client is redirected to /foo with http status code 301 for GET requests
|
// Afterwards the router does a case-insensitive lookup of the cleaned path.
|
||||||
// and 307 for all other request methods.
|
// If a handle can be found for this route, the router makes a redirection
|
||||||
RedirectTrailingSlash bool
|
// to the corrected path with status code 301 for GET requests and 307 for
|
||||||
|
// all other request methods.
|
||||||
|
// For example /FOO and /..//Foo could be redirected to /foo.
|
||||||
|
// RedirectTrailingSlash is independent of this option.
|
||||||
|
RedirectFixedPath bool
|
||||||
|
|
||||||
// If enabled, the router tries to fix the current request path, if no
|
// If enabled, the router checks if another method is allowed for the
|
||||||
// handle is registered for it.
|
// current route, if the current request can not be routed.
|
||||||
// First superfluous path elements like ../ or // are removed.
|
// If this is the case, the request is answered with 'Method Not Allowed'
|
||||||
// Afterwards the router does a case-insensitive lookup of the cleaned path.
|
// and HTTP status code 405.
|
||||||
// If a handle can be found for this route, the router makes a redirection
|
// If no other Method is allowed, the request is delegated to the NotFound
|
||||||
// to the corrected path with status code 301 for GET requests and 307 for
|
// handler.
|
||||||
// all other request methods.
|
HandleMethodNotAllowed bool
|
||||||
// For example /FOO and /..//Foo could be redirected to /foo.
|
ForwardedByClientIP bool
|
||||||
// RedirectTrailingSlash is independent of this option.
|
|
||||||
RedirectFixedPath bool
|
|
||||||
|
|
||||||
// If enabled, the router checks if another method is allowed for the
|
// #726 #755 If enabled, it will thrust some headers starting with
|
||||||
// current route, if the current request can not be routed.
|
// 'X-AppEngine...' for better integration with that PaaS.
|
||||||
// If this is the case, the request is answered with 'Method Not Allowed'
|
AppEngine bool
|
||||||
// and HTTP status code 405.
|
|
||||||
// If no other Method is allowed, the request is delegated to the NotFound
|
|
||||||
// handler.
|
|
||||||
HandleMethodNotAllowed bool
|
|
||||||
ForwardedByClientIP bool
|
|
||||||
|
|
||||||
// #726 #755 If enabled, it will thrust some headers starting with
|
// If enabled, the url.RawPath will be used to find parameters.
|
||||||
// 'X-AppEngine...' for better integration with that PaaS.
|
UseRawPath bool
|
||||||
AppEngine bool
|
// If true, the path value will be unescaped.
|
||||||
|
// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
|
||||||
// If enabled, the url.RawPath will be used to find parameters.
|
// as url.Path gonna be used, which is already unescaped.
|
||||||
UseRawPath bool
|
UnescapePathValues bool
|
||||||
// If true, the path value will be unescaped.
|
}
|
||||||
// If UseRawPath is false (by default), the UnescapePathValues effectively is true,
|
)
|
||||||
// as url.Path gonna be used, which is already unescaped.
|
|
||||||
UnescapePathValues bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ IRouter = &Engine{}
|
var _ IRouter = &Engine{}
|
||||||
|
|
||||||
|
@ -122,7 +123,6 @@ func New() *Engine {
|
||||||
UnescapePathValues: true,
|
UnescapePathValues: true,
|
||||||
trees: make(methodTrees, 0, 9),
|
trees: make(methodTrees, 0, 9),
|
||||||
delims: render.Delims{"{{", "}}"},
|
delims: render.Delims{"{{", "}}"},
|
||||||
secureJsonPrefix: "while(1);",
|
|
||||||
}
|
}
|
||||||
engine.RouterGroup.engine = engine
|
engine.RouterGroup.engine = engine
|
||||||
engine.pool.New = func() interface{} {
|
engine.pool.New = func() interface{} {
|
||||||
|
@ -147,11 +147,6 @@ func (engine *Engine) Delims(left, right string) *Engine {
|
||||||
return engine
|
return engine
|
||||||
}
|
}
|
||||||
|
|
||||||
func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
|
|
||||||
engine.secureJsonPrefix = prefix
|
|
||||||
return engine
|
|
||||||
}
|
|
||||||
|
|
||||||
func (engine *Engine) LoadHTMLGlob(pattern string) {
|
func (engine *Engine) LoadHTMLGlob(pattern string) {
|
||||||
if IsDebugging() {
|
if IsDebugging() {
|
||||||
debugPrintLoadTemplate(template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern)))
|
debugPrintLoadTemplate(template.Must(template.New("").Delims(engine.delims.Left, engine.delims.Right).Funcs(engine.FuncMap).ParseGlob(pattern)))
|
||||||
|
@ -291,7 +286,7 @@ func (engine *Engine) RunUnix(file string) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP conforms to the http.Handler interface.
|
// Conforms to the http.Handler interface.
|
||||||
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
c := engine.pool.Get().(*Context)
|
c := engine.pool.Get().(*Context)
|
||||||
c.writermem.reset(w)
|
c.writermem.reset(w)
|
||||||
|
@ -303,7 +298,7 @@ func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
engine.pool.Put(c)
|
engine.pool.Put(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleContext re-enter a context that has been rewritten.
|
// Re-enter a context that has been rewritten.
|
||||||
// This can be done by setting c.Request.Path to your new target.
|
// This can be done by setting c.Request.Path to your new target.
|
||||||
// Disclaimer: You can loop yourself to death with this, use wisely.
|
// Disclaimer: You can loop yourself to death with this, use wisely.
|
||||||
func (engine *Engine) HandleContext(c *Context) {
|
func (engine *Engine) HandleContext(c *Context) {
|
||||||
|
@ -351,6 +346,7 @@ func (engine *Engine) handleHTTPRequest(context *Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: unit test
|
||||||
if engine.HandleMethodNotAllowed {
|
if engine.HandleMethodNotAllowed {
|
||||||
for _, tree := range engine.trees {
|
for _, tree := range engine.trees {
|
||||||
if tree.method != httpMethod {
|
if tree.method != httpMethod {
|
||||||
|
|
|
@ -25,17 +25,14 @@ var (
|
||||||
disableColor = false
|
disableColor = false
|
||||||
)
|
)
|
||||||
|
|
||||||
// DisableConsoleColor disables color output in the console
|
|
||||||
func DisableConsoleColor() {
|
func DisableConsoleColor() {
|
||||||
disableColor = true
|
disableColor = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorLogger returns a handlerfunc for any error type
|
|
||||||
func ErrorLogger() HandlerFunc {
|
func ErrorLogger() HandlerFunc {
|
||||||
return ErrorLoggerT(ErrorTypeAny)
|
return ErrorLoggerT(ErrorTypeAny)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorLoggerT returns a handlerfunc for a given error type
|
|
||||||
func ErrorLoggerT(typ ErrorType) HandlerFunc {
|
func ErrorLoggerT(typ ErrorType) HandlerFunc {
|
||||||
return func(c *Context) {
|
return func(c *Context) {
|
||||||
c.Next()
|
c.Next()
|
||||||
|
@ -77,7 +74,6 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
||||||
// Start timer
|
// Start timer
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
path := c.Request.URL.Path
|
path := c.Request.URL.Path
|
||||||
raw := c.Request.URL.RawQuery
|
|
||||||
|
|
||||||
// Process request
|
// Process request
|
||||||
c.Next()
|
c.Next()
|
||||||
|
@ -98,10 +94,6 @@ func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
|
||||||
}
|
}
|
||||||
comment := c.Errors.ByType(ErrorTypePrivate).String()
|
comment := c.Errors.ByType(ErrorTypePrivate).String()
|
||||||
|
|
||||||
if raw != "" {
|
|
||||||
path = path + "?" + raw
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %s %-7s %s\n%s",
|
fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %s %-7s %s\n%s",
|
||||||
end.Format("2006/01/02 - 15:04:05"),
|
end.Format("2006/01/02 - 15:04:05"),
|
||||||
statusColor, statusCode, reset,
|
statusColor, statusCode, reset,
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -64,10 +64,6 @@ func DisableBindValidation() {
|
||||||
binding.Validator = nil
|
binding.Validator = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func EnableJsonDecoderUseNumber() {
|
|
||||||
binding.EnableDecoderUseNumber = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func Mode() string {
|
func Mode() string {
|
||||||
return modeName
|
return modeName
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
package gin
|
package gin
|
||||||
|
|
||||||
// cleanPath is the URL version of path.Clean, it returns a canonical URL path
|
// CleanPath is the URL version of path.Clean, it returns a canonical URL path
|
||||||
// for p, eliminating . and .. elements.
|
// for p, eliminating . and .. elements.
|
||||||
//
|
//
|
||||||
// The following rules are applied iteratively until no further processing can
|
// The following rules are applied iteratively until no further processing can
|
||||||
|
|
|
@ -16,34 +16,36 @@ const (
|
||||||
defaultStatus = 200
|
defaultStatus = 200
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResponseWriter interface {
|
type (
|
||||||
http.ResponseWriter
|
ResponseWriter interface {
|
||||||
http.Hijacker
|
http.ResponseWriter
|
||||||
http.Flusher
|
http.Hijacker
|
||||||
http.CloseNotifier
|
http.Flusher
|
||||||
|
http.CloseNotifier
|
||||||
|
|
||||||
// Returns the HTTP response status code of the current request.
|
// Returns the HTTP response status code of the current request.
|
||||||
Status() int
|
Status() int
|
||||||
|
|
||||||
// Returns the number of bytes already written into the response http body.
|
// Returns the number of bytes already written into the response http body.
|
||||||
// See Written()
|
// See Written()
|
||||||
Size() int
|
Size() int
|
||||||
|
|
||||||
// Writes the string into the response body.
|
// Writes the string into the response body.
|
||||||
WriteString(string) (int, error)
|
WriteString(string) (int, error)
|
||||||
|
|
||||||
// Returns true if the response body was already written.
|
// Returns true if the response body was already written.
|
||||||
Written() bool
|
Written() bool
|
||||||
|
|
||||||
// Forces to write the http header (status code + headers).
|
// Forces to write the http header (status code + headers).
|
||||||
WriteHeaderNow()
|
WriteHeaderNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
type responseWriter struct {
|
responseWriter struct {
|
||||||
http.ResponseWriter
|
http.ResponseWriter
|
||||||
size int
|
size int
|
||||||
status int
|
status int
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
var _ ResponseWriter = &responseWriter{}
|
var _ ResponseWriter = &responseWriter{}
|
||||||
|
|
||||||
|
@ -95,7 +97,7 @@ func (w *responseWriter) Written() bool {
|
||||||
return w.size != noWritten
|
return w.size != noWritten
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hijack implements the http.Hijacker interface
|
// Implements the http.Hijacker interface
|
||||||
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
if w.size < 0 {
|
if w.size < 0 {
|
||||||
w.size = 0
|
w.size = 0
|
||||||
|
@ -103,12 +105,12 @@ func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
return w.ResponseWriter.(http.Hijacker).Hijack()
|
return w.ResponseWriter.(http.Hijacker).Hijack()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseNotify implements the http.CloseNotify interface
|
// Implements the http.CloseNotify interface
|
||||||
func (w *responseWriter) CloseNotify() <-chan bool {
|
func (w *responseWriter) CloseNotify() <-chan bool {
|
||||||
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush implements the http.Flush interface
|
// Implements the http.Flush interface
|
||||||
func (w *responseWriter) Flush() {
|
func (w *responseWriter) Flush() {
|
||||||
w.ResponseWriter.(http.Flusher).Flush()
|
w.ResponseWriter.(http.Flusher).Flush()
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,37 +11,39 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IRouter interface {
|
type (
|
||||||
IRoutes
|
IRouter interface {
|
||||||
Group(string, ...HandlerFunc) *RouterGroup
|
IRoutes
|
||||||
}
|
Group(string, ...HandlerFunc) *RouterGroup
|
||||||
|
}
|
||||||
|
|
||||||
type IRoutes interface {
|
IRoutes interface {
|
||||||
Use(...HandlerFunc) IRoutes
|
Use(...HandlerFunc) IRoutes
|
||||||
|
|
||||||
Handle(string, string, ...HandlerFunc) IRoutes
|
Handle(string, string, ...HandlerFunc) IRoutes
|
||||||
Any(string, ...HandlerFunc) IRoutes
|
Any(string, ...HandlerFunc) IRoutes
|
||||||
GET(string, ...HandlerFunc) IRoutes
|
GET(string, ...HandlerFunc) IRoutes
|
||||||
POST(string, ...HandlerFunc) IRoutes
|
POST(string, ...HandlerFunc) IRoutes
|
||||||
DELETE(string, ...HandlerFunc) IRoutes
|
DELETE(string, ...HandlerFunc) IRoutes
|
||||||
PATCH(string, ...HandlerFunc) IRoutes
|
PATCH(string, ...HandlerFunc) IRoutes
|
||||||
PUT(string, ...HandlerFunc) IRoutes
|
PUT(string, ...HandlerFunc) IRoutes
|
||||||
OPTIONS(string, ...HandlerFunc) IRoutes
|
OPTIONS(string, ...HandlerFunc) IRoutes
|
||||||
HEAD(string, ...HandlerFunc) IRoutes
|
HEAD(string, ...HandlerFunc) IRoutes
|
||||||
|
|
||||||
StaticFile(string, string) IRoutes
|
StaticFile(string, string) IRoutes
|
||||||
Static(string, string) IRoutes
|
Static(string, string) IRoutes
|
||||||
StaticFS(string, http.FileSystem) IRoutes
|
StaticFS(string, http.FileSystem) IRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
// RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix
|
// RouterGroup is used internally to configure router, a RouterGroup is associated with a prefix
|
||||||
// and an array of handlers (middleware)
|
// and an array of handlers (middleware)
|
||||||
type RouterGroup struct {
|
RouterGroup struct {
|
||||||
Handlers HandlersChain
|
Handlers HandlersChain
|
||||||
basePath string
|
basePath string
|
||||||
engine *Engine
|
engine *Engine
|
||||||
root bool
|
root bool
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
var _ IRouter = &RouterGroup{}
|
var _ IRouter = &RouterGroup{}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateTestContext returns a fresh engine and context for testing purposes
|
|
||||||
func CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine) {
|
func CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine) {
|
||||||
r = New()
|
r = New()
|
||||||
c = r.allocateContext()
|
c = r.allocateContext()
|
||||||
|
|
|
@ -105,7 +105,9 @@ func (n *node) incrementChildPrio(pos int) int {
|
||||||
newPos := pos
|
newPos := pos
|
||||||
for newPos > 0 && n.children[newPos-1].priority < prio {
|
for newPos > 0 && n.children[newPos-1].priority < prio {
|
||||||
// swap node positions
|
// swap node positions
|
||||||
n.children[newPos-1], n.children[newPos] = n.children[newPos], n.children[newPos-1]
|
tmpN := n.children[newPos-1]
|
||||||
|
n.children[newPos-1] = n.children[newPos]
|
||||||
|
n.children[newPos] = tmpN
|
||||||
|
|
||||||
newPos--
|
newPos--
|
||||||
}
|
}
|
||||||
|
@ -357,7 +359,7 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle
|
||||||
n.handlers = handlers
|
n.handlers = handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
// getValue returns the handle registered with the given path (key). The values of
|
// Returns the handle registered with the given path (key). The values of
|
||||||
// wildcards are saved to a map.
|
// wildcards are saved to a map.
|
||||||
// If no handle can be found, a TSR (trailing slash redirect) recommendation is
|
// If no handle can be found, a TSR (trailing slash redirect) recommendation is
|
||||||
// made if a handle exists with an extra (without the) trailing slash for the
|
// made if a handle exists with an extra (without the) trailing slash for the
|
||||||
|
@ -499,7 +501,7 @@ walk: // Outer loop for walking the tree
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// findCaseInsensitivePath makes a case-insensitive lookup of the given path and tries to find a handler.
|
// Makes a case-insensitive lookup of the given path and tries to find a handler.
|
||||||
// It can optionally also fix trailing slashes.
|
// It can optionally also fix trailing slashes.
|
||||||
// It returns the case-corrected path and a bool indicating whether the lookup
|
// It returns the case-corrected path and a bool indicating whether the lookup
|
||||||
// was successful.
|
// was successful.
|
||||||
|
|
|
@ -100,10 +100,12 @@ func parseAccept(acceptHeader string) []string {
|
||||||
parts := strings.Split(acceptHeader, ",")
|
parts := strings.Split(acceptHeader, ",")
|
||||||
out := make([]string, 0, len(parts))
|
out := make([]string, 0, len(parts))
|
||||||
for _, part := range parts {
|
for _, part := range parts {
|
||||||
if index := strings.IndexByte(part, ';'); index >= 0 {
|
index := strings.IndexByte(part, ';')
|
||||||
|
if index >= 0 {
|
||||||
part = part[0:index]
|
part = part[0:index]
|
||||||
}
|
}
|
||||||
if part = strings.TrimSpace(part); len(part) > 0 {
|
part = strings.TrimSpace(part)
|
||||||
|
if len(part) > 0 {
|
||||||
out = append(out, part)
|
out = append(out, part)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,10 +129,12 @@
|
||||||
"revisionTime": "2017-01-09T09:34:21Z"
|
"revisionTime": "2017-01-09T09:34:21Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "YYaHHK72ywsjJG11nEnz6JdGqSg=",
|
"checksumSHA1": "cpELhz30Sco7IBwvI6FyuShnjnQ=",
|
||||||
"path": "github.com/gin-gonic/gin",
|
"path": "github.com/gin-gonic/gin",
|
||||||
"revision": "5cb25a6410a299dcc31e7f31b41c60c90e30b3f6",
|
"revision": "d459835d2b077e44f7c9b453505ee29881d5d12d",
|
||||||
"revisionTime": "2017-07-14T17:28:16Z"
|
"revisionTime": "2017-07-02T09:28:26Z",
|
||||||
|
"version": "v1.2",
|
||||||
|
"versionExact": "v1.2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "A09l4+1MIuaXhh3Nn6aoOm+rr3Q=",
|
"checksumSHA1": "A09l4+1MIuaXhh3Nn6aoOm+rr3Q=",
|
||||||
|
|
Loading…
Reference in New Issue