mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-18 23:12:17 +08:00
commit
661abaa3b9
20
README.md
20
README.md
@ -17,7 +17,6 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
|
||||
## Contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Prerequisite](#prerequisite)
|
||||
- [Quick start](#quick-start)
|
||||
- [Benchmarks](#benchmarks)
|
||||
- [Gin v1.stable](#gin-v1-stable)
|
||||
@ -584,7 +583,7 @@ func main() {
|
||||
|
||||
To bind a request body into a type, use model binding. We currently support binding of JSON, XML, YAML and standard form values (foo=bar&boo=baz).
|
||||
|
||||
Gin uses [**go-playground/validator.v8**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](http://godoc.org/gopkg.in/go-playground/validator.v8#hdr-Baked_In_Validators_and_Tags).
|
||||
Gin uses [**go-playground/validator/v10**](https://github.com/go-playground/validator) for validation. Check the full docs on tags usage [here](https://godoc.org/github.com/go-playground/validator#hdr-Baked_In_Validators_and_Tags).
|
||||
|
||||
Note that you need to set the corresponding binding tag on all fields you want to bind. For example, when binding from JSON, set `json:"fieldname"`.
|
||||
|
||||
@ -704,25 +703,22 @@ package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"gopkg.in/go-playground/validator.v8"
|
||||
"gopkg.in/go-playground/validator.v10"
|
||||
)
|
||||
|
||||
// Booking contains binded and validated data.
|
||||
type Booking struct {
|
||||
CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
|
||||
CheckIn time.Time `form:"check_in" binding:"required" time_format:"2006-01-02"`
|
||||
CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"`
|
||||
}
|
||||
|
||||
func bookableDate(
|
||||
v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
|
||||
field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
|
||||
) bool {
|
||||
if date, ok := field.Interface().(time.Time); ok {
|
||||
var bookableDate validator.Func = func(fl validator.FieldLevel) bool {
|
||||
date, ok := fl.Field().Interface().(time.Time)
|
||||
if ok {
|
||||
today := time.Now()
|
||||
if today.After(date) {
|
||||
return false
|
||||
@ -756,8 +752,8 @@ func getBookable(c *gin.Context) {
|
||||
$ curl "localhost:8085/bookable?check_in=2018-04-16&check_out=2018-04-17"
|
||||
{"message":"Booking dates are valid!"}
|
||||
|
||||
$ curl "localhost:8085/bookable?check_in=2018-03-08&check_out=2018-03-09"
|
||||
{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}
|
||||
$ curl "localhost:8085/bookable?check_in=2018-03-10&check_out=2018-03-09"
|
||||
{"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"}
|
||||
```
|
||||
|
||||
[Struct level validations](https://github.com/go-playground/validator/releases/tag/v8.7) can also be registered this way.
|
||||
|
207
tree.go
207
tree.go
@ -425,110 +425,7 @@ func (n *node) getValue(path string, po Params, unescape bool) (value nodeValue)
|
||||
walk: // Outer loop for walking the tree
|
||||
for {
|
||||
prefix := n.path
|
||||
if len(path) > len(prefix) {
|
||||
if path[:len(prefix)] == prefix {
|
||||
path = path[len(prefix):]
|
||||
// If this node does not have a wildcard (param or catchAll)
|
||||
// child, we can just look up the next child node and continue
|
||||
// to walk down the tree
|
||||
if !n.wildChild {
|
||||
c := path[0]
|
||||
indices := n.indices
|
||||
for i, max := 0, len(indices); i < max; i++ {
|
||||
if c == indices[i] {
|
||||
n = n.children[i]
|
||||
prefix = n.path
|
||||
continue walk
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found.
|
||||
// We can recommend to redirect to the same URL without a
|
||||
// trailing slash if a leaf exists for that path.
|
||||
value.tsr = path == "/" && n.handlers != nil
|
||||
return
|
||||
}
|
||||
|
||||
// handle wildcard child
|
||||
n = n.children[0]
|
||||
switch n.nType {
|
||||
case param:
|
||||
// find param end (either '/' or path end)
|
||||
end := 0
|
||||
for end < len(path) && path[end] != '/' {
|
||||
end++
|
||||
}
|
||||
|
||||
// save param value
|
||||
if cap(value.params) < int(n.maxParams) {
|
||||
value.params = make(Params, 0, n.maxParams)
|
||||
}
|
||||
i := len(value.params)
|
||||
value.params = value.params[:i+1] // expand slice within preallocated capacity
|
||||
value.params[i].Key = n.path[1:]
|
||||
val := path[:end]
|
||||
if unescape {
|
||||
var err error
|
||||
if value.params[i].Value, err = url.QueryUnescape(val); err != nil {
|
||||
value.params[i].Value = val // fallback, in case of error
|
||||
}
|
||||
} else {
|
||||
value.params[i].Value = val
|
||||
}
|
||||
|
||||
// we need to go deeper!
|
||||
if end < len(path) {
|
||||
if len(n.children) > 0 {
|
||||
path = path[end:]
|
||||
n = n.children[0]
|
||||
prefix = n.path
|
||||
continue walk
|
||||
}
|
||||
|
||||
// ... but we can't
|
||||
value.tsr = len(path) == end+1
|
||||
return
|
||||
}
|
||||
|
||||
if value.handlers = n.handlers; value.handlers != nil {
|
||||
value.fullPath = n.fullPath
|
||||
return
|
||||
}
|
||||
if len(n.children) == 1 {
|
||||
// No handle found. Check if a handle for this path + a
|
||||
// trailing slash exists for TSR recommendation
|
||||
n = n.children[0]
|
||||
value.tsr = n.path == "/" && n.handlers != nil
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
case catchAll:
|
||||
// save param value
|
||||
if cap(value.params) < int(n.maxParams) {
|
||||
value.params = make(Params, 0, n.maxParams)
|
||||
}
|
||||
i := len(value.params)
|
||||
value.params = value.params[:i+1] // expand slice within preallocated capacity
|
||||
value.params[i].Key = n.path[2:]
|
||||
if unescape {
|
||||
var err error
|
||||
if value.params[i].Value, err = url.QueryUnescape(path); err != nil {
|
||||
value.params[i].Value = path // fallback, in case of error
|
||||
}
|
||||
} else {
|
||||
value.params[i].Value = path
|
||||
}
|
||||
|
||||
value.handlers = n.handlers
|
||||
value.fullPath = n.fullPath
|
||||
return
|
||||
|
||||
default:
|
||||
panic("invalid node type")
|
||||
}
|
||||
}
|
||||
} else if path == prefix {
|
||||
if path == prefix {
|
||||
// We should have reached the node containing the handle.
|
||||
// Check if this node has a handle registered.
|
||||
if value.handlers = n.handlers; value.handlers != nil {
|
||||
@ -556,6 +453,108 @@ walk: // Outer loop for walking the tree
|
||||
return
|
||||
}
|
||||
|
||||
if len(path) > len(prefix) && path[:len(prefix)] == prefix {
|
||||
path = path[len(prefix):]
|
||||
// If this node does not have a wildcard (param or catchAll)
|
||||
// child, we can just look up the next child node and continue
|
||||
// to walk down the tree
|
||||
if !n.wildChild {
|
||||
c := path[0]
|
||||
indices := n.indices
|
||||
for i, max := 0, len(indices); i < max; i++ {
|
||||
if c == indices[i] {
|
||||
n = n.children[i]
|
||||
prefix = n.path
|
||||
continue walk
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found.
|
||||
// We can recommend to redirect to the same URL without a
|
||||
// trailing slash if a leaf exists for that path.
|
||||
value.tsr = path == "/" && n.handlers != nil
|
||||
return
|
||||
}
|
||||
|
||||
// handle wildcard child
|
||||
n = n.children[0]
|
||||
switch n.nType {
|
||||
case param:
|
||||
// find param end (either '/' or path end)
|
||||
end := 0
|
||||
for end < len(path) && path[end] != '/' {
|
||||
end++
|
||||
}
|
||||
|
||||
// save param value
|
||||
if cap(value.params) < int(n.maxParams) {
|
||||
value.params = make(Params, 0, n.maxParams)
|
||||
}
|
||||
i := len(value.params)
|
||||
value.params = value.params[:i+1] // expand slice within preallocated capacity
|
||||
value.params[i].Key = n.path[1:]
|
||||
val := path[:end]
|
||||
if unescape {
|
||||
var err error
|
||||
if value.params[i].Value, err = url.QueryUnescape(val); err != nil {
|
||||
value.params[i].Value = val // fallback, in case of error
|
||||
}
|
||||
} else {
|
||||
value.params[i].Value = val
|
||||
}
|
||||
|
||||
// we need to go deeper!
|
||||
if end < len(path) {
|
||||
if len(n.children) > 0 {
|
||||
path = path[end:]
|
||||
n = n.children[0]
|
||||
prefix = n.path
|
||||
continue walk
|
||||
}
|
||||
|
||||
// ... but we can't
|
||||
value.tsr = len(path) == end+1
|
||||
return
|
||||
}
|
||||
|
||||
if value.handlers = n.handlers; value.handlers != nil {
|
||||
value.fullPath = n.fullPath
|
||||
return
|
||||
}
|
||||
if len(n.children) == 1 {
|
||||
// No handle found. Check if a handle for this path + a
|
||||
// trailing slash exists for TSR recommendation
|
||||
n = n.children[0]
|
||||
value.tsr = n.path == "/" && n.handlers != nil
|
||||
}
|
||||
return
|
||||
|
||||
case catchAll:
|
||||
// save param value
|
||||
if cap(value.params) < int(n.maxParams) {
|
||||
value.params = make(Params, 0, n.maxParams)
|
||||
}
|
||||
i := len(value.params)
|
||||
value.params = value.params[:i+1] // expand slice within preallocated capacity
|
||||
value.params[i].Key = n.path[2:]
|
||||
if unescape {
|
||||
var err error
|
||||
if value.params[i].Value, err = url.QueryUnescape(path); err != nil {
|
||||
value.params[i].Value = path // fallback, in case of error
|
||||
}
|
||||
} else {
|
||||
value.params[i].Value = path
|
||||
}
|
||||
|
||||
value.handlers = n.handlers
|
||||
value.fullPath = n.fullPath
|
||||
return
|
||||
|
||||
default:
|
||||
panic("invalid node type")
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found. We can recommend to redirect to the same URL with an
|
||||
// extra trailing slash if a leaf exists for that path
|
||||
value.tsr = (path == "/") ||
|
||||
|
Loading…
x
Reference in New Issue
Block a user