mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-16 21:32:11 +08:00
Merge 068942b5ce84ee37c62b0b52d3dad8cdb0db16eb into f469c1be3925ab32bd38fbf944b230c70abe9e2b
This commit is contained in:
commit
72a077e151
@ -8,13 +8,11 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type defaultValidator struct {
|
||||
once sync.Once
|
||||
validate *validator.Validate
|
||||
}
|
||||
|
||||
@ -76,7 +74,7 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error {
|
||||
|
||||
// validateStruct receives struct type
|
||||
func (v *defaultValidator) validateStruct(obj interface{}) error {
|
||||
v.lazyinit()
|
||||
v.lazyInit()
|
||||
return v.validate.Struct(obj)
|
||||
}
|
||||
|
||||
@ -85,13 +83,14 @@ func (v *defaultValidator) validateStruct(obj interface{}) error {
|
||||
// or struct level validations. See validator GoDoc for more info -
|
||||
// https://pkg.go.dev/github.com/go-playground/validator/v10
|
||||
func (v *defaultValidator) Engine() interface{} {
|
||||
v.lazyinit()
|
||||
v.lazyInit()
|
||||
return v.validate
|
||||
}
|
||||
|
||||
func (v *defaultValidator) lazyinit() {
|
||||
v.once.Do(func() {
|
||||
// lazyInit initialize the validate Settings, no need to use sync.once
|
||||
func (v *defaultValidator) lazyInit() {
|
||||
if v.validate == nil {
|
||||
v.validate = validator.New()
|
||||
v.validate.SetTagName("binding")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
errUnknownType = errors.New("unknown type")
|
||||
// ErrUnknownType unknown type
|
||||
ErrUnknownType = errors.New("unknown type")
|
||||
|
||||
// ErrConvertMapStringSlice can not covert to map[string][]string
|
||||
ErrConvertMapStringSlice = errors.New("can not convert to map slices of strings")
|
||||
@ -164,7 +165,7 @@ func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter
|
||||
return setter.TrySet(value, field, tagValue, setOpt)
|
||||
}
|
||||
|
||||
func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSetted bool, err error) {
|
||||
func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (bool, error) {
|
||||
vs, ok := form[tagValue]
|
||||
if !ok && !opt.isDefaultExists {
|
||||
return false, nil
|
||||
@ -240,7 +241,7 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
|
||||
case reflect.Map:
|
||||
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||
default:
|
||||
return errUnknownType
|
||||
return ErrUnknownType
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ func TestMappingUnknownFieldType(t *testing.T) {
|
||||
|
||||
err := mappingByPtr(&s, formSource{"U": {"unknown"}}, "form")
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, errUnknownType, err)
|
||||
assert.Equal(t, ErrUnknownType, err)
|
||||
}
|
||||
|
||||
func TestMappingURI(t *testing.T) {
|
||||
|
@ -32,7 +32,7 @@ func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField
|
||||
return setByForm(value, field, r.MultipartForm.Value, key, opt)
|
||||
}
|
||||
|
||||
func setByMultipartFormFile(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
|
||||
func setByMultipartFormFile(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (bool, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Ptr:
|
||||
switch value.Interface().(type) {
|
||||
@ -48,7 +48,7 @@ func setByMultipartFormFile(value reflect.Value, field reflect.StructField, file
|
||||
}
|
||||
case reflect.Slice:
|
||||
slice := reflect.MakeSlice(value.Type(), len(files), len(files))
|
||||
isSetted, err = setArrayOfMultipartFormFiles(slice, field, files)
|
||||
isSetted, err := setArrayOfMultipartFormFiles(slice, field, files)
|
||||
if err != nil || !isSetted {
|
||||
return isSetted, err
|
||||
}
|
||||
@ -60,7 +60,7 @@ func setByMultipartFormFile(value reflect.Value, field reflect.StructField, file
|
||||
return false, ErrMultiFileHeader
|
||||
}
|
||||
|
||||
func setArrayOfMultipartFormFiles(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (isSetted bool, err error) {
|
||||
func setArrayOfMultipartFormFiles(value reflect.Value, field reflect.StructField, files []*multipart.FileHeader) (bool, error) {
|
||||
if value.Len() != len(files) {
|
||||
return false, ErrMultiFileHeaderLenInvalid
|
||||
}
|
||||
|
14
context.go
14
context.go
@ -766,7 +766,7 @@ func (c *Context) ClientIP() string {
|
||||
|
||||
if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil {
|
||||
for _, headerName := range c.engine.RemoteIPHeaders {
|
||||
ip, valid := validateHeader(c.requestHeader(headerName))
|
||||
ip, valid := getIPFromHeader(c.requestHeader(headerName))
|
||||
if valid {
|
||||
return ip
|
||||
}
|
||||
@ -800,10 +800,17 @@ func (c *Context) RemoteIP() (net.IP, bool) {
|
||||
return remoteIP, false
|
||||
}
|
||||
|
||||
func validateHeader(header string) (clientIP string, valid bool) {
|
||||
// getIPFromHeader return clientIP,valid when validate ip address
|
||||
func getIPFromHeader(header string) (string, bool) {
|
||||
if header == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
var (
|
||||
clientIP string
|
||||
valid bool
|
||||
)
|
||||
|
||||
items := strings.Split(header, ",")
|
||||
for i, ipStr := range items {
|
||||
ipStr = strings.TrimSpace(ipStr)
|
||||
@ -820,7 +827,8 @@ func validateHeader(header string) (clientIP string, valid bool) {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
return clientIP, valid
|
||||
}
|
||||
|
||||
// ContentType returns the Content-Type header of the request.
|
||||
|
61
ginS/gins.go
61
ginS/gins.go
@ -7,100 +7,97 @@ package ginS
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var once sync.Once
|
||||
var internalEngine *gin.Engine
|
||||
var engine *gin.Engine
|
||||
|
||||
func engine() *gin.Engine {
|
||||
once.Do(func() {
|
||||
internalEngine = gin.Default()
|
||||
})
|
||||
return internalEngine
|
||||
func init() {
|
||||
if engine == nil {
|
||||
engine = gin.Default()
|
||||
}
|
||||
}
|
||||
|
||||
// LoadHTMLGlob is a wrapper for Engine.LoadHTMLGlob.
|
||||
func LoadHTMLGlob(pattern string) {
|
||||
engine().LoadHTMLGlob(pattern)
|
||||
engine.LoadHTMLGlob(pattern)
|
||||
}
|
||||
|
||||
// LoadHTMLFiles is a wrapper for Engine.LoadHTMLFiles.
|
||||
func LoadHTMLFiles(files ...string) {
|
||||
engine().LoadHTMLFiles(files...)
|
||||
engine.LoadHTMLFiles(files...)
|
||||
}
|
||||
|
||||
// SetHTMLTemplate is a wrapper for Engine.SetHTMLTemplate.
|
||||
func SetHTMLTemplate(templ *template.Template) {
|
||||
engine().SetHTMLTemplate(templ)
|
||||
engine.SetHTMLTemplate(templ)
|
||||
}
|
||||
|
||||
// NoRoute adds handlers for NoRoute. It return a 404 code by default.
|
||||
func NoRoute(handlers ...gin.HandlerFunc) {
|
||||
engine().NoRoute(handlers...)
|
||||
engine.NoRoute(handlers...)
|
||||
}
|
||||
|
||||
// NoMethod is a wrapper for Engine.NoMethod.
|
||||
func NoMethod(handlers ...gin.HandlerFunc) {
|
||||
engine().NoMethod(handlers...)
|
||||
engine.NoMethod(handlers...)
|
||||
}
|
||||
|
||||
// Group creates a new router group. You should add all the routes that have common middlewares or the same path prefix.
|
||||
// For example, all the routes that use a common middleware for authorization could be grouped.
|
||||
func Group(relativePath string, handlers ...gin.HandlerFunc) *gin.RouterGroup {
|
||||
return engine().Group(relativePath, handlers...)
|
||||
return engine.Group(relativePath, handlers...)
|
||||
}
|
||||
|
||||
// Handle is a wrapper for Engine.Handle.
|
||||
func Handle(httpMethod, relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||
return engine().Handle(httpMethod, relativePath, handlers...)
|
||||
return engine.Handle(httpMethod, relativePath, handlers...)
|
||||
}
|
||||
|
||||
// POST is a shortcut for router.Handle("POST", path, handle)
|
||||
func POST(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||
return engine().POST(relativePath, handlers...)
|
||||
return engine.POST(relativePath, handlers...)
|
||||
}
|
||||
|
||||
// GET is a shortcut for router.Handle("GET", path, handle)
|
||||
func GET(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||
return engine().GET(relativePath, handlers...)
|
||||
return engine.GET(relativePath, handlers...)
|
||||
}
|
||||
|
||||
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
|
||||
func DELETE(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||
return engine().DELETE(relativePath, handlers...)
|
||||
return engine.DELETE(relativePath, handlers...)
|
||||
}
|
||||
|
||||
// PATCH is a shortcut for router.Handle("PATCH", path, handle)
|
||||
func PATCH(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||
return engine().PATCH(relativePath, handlers...)
|
||||
return engine.PATCH(relativePath, handlers...)
|
||||
}
|
||||
|
||||
// PUT is a shortcut for router.Handle("PUT", path, handle)
|
||||
func PUT(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||
return engine().PUT(relativePath, handlers...)
|
||||
return engine.PUT(relativePath, handlers...)
|
||||
}
|
||||
|
||||
// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
|
||||
func OPTIONS(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||
return engine().OPTIONS(relativePath, handlers...)
|
||||
return engine.OPTIONS(relativePath, handlers...)
|
||||
}
|
||||
|
||||
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
|
||||
func HEAD(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||
return engine().HEAD(relativePath, handlers...)
|
||||
return engine.HEAD(relativePath, handlers...)
|
||||
}
|
||||
|
||||
// Any is a wrapper for Engine.Any.
|
||||
func Any(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||
return engine().Any(relativePath, handlers...)
|
||||
return engine.Any(relativePath, handlers...)
|
||||
}
|
||||
|
||||
// StaticFile is a wrapper for Engine.StaticFile.
|
||||
func StaticFile(relativePath, filepath string) gin.IRoutes {
|
||||
return engine().StaticFile(relativePath, filepath)
|
||||
return engine.StaticFile(relativePath, filepath)
|
||||
}
|
||||
|
||||
// Static serves files from the given file system root.
|
||||
@ -110,50 +107,50 @@ func StaticFile(relativePath, filepath string) gin.IRoutes {
|
||||
// use :
|
||||
// router.Static("/static", "/var/www")
|
||||
func Static(relativePath, root string) gin.IRoutes {
|
||||
return engine().Static(relativePath, root)
|
||||
return engine.Static(relativePath, root)
|
||||
}
|
||||
|
||||
// StaticFS is a wrapper for Engine.StaticFS.
|
||||
func StaticFS(relativePath string, fs http.FileSystem) gin.IRoutes {
|
||||
return engine().StaticFS(relativePath, fs)
|
||||
return engine.StaticFS(relativePath, fs)
|
||||
}
|
||||
|
||||
// Use attaches a global middleware to the router. ie. the middlewares attached though Use() will be
|
||||
// included in the handlers chain for every single request. Even 404, 405, static files...
|
||||
// For example, this is the right place for a logger or error management middleware.
|
||||
func Use(middlewares ...gin.HandlerFunc) gin.IRoutes {
|
||||
return engine().Use(middlewares...)
|
||||
return engine.Use(middlewares...)
|
||||
}
|
||||
|
||||
// Routes returns a slice of registered routes.
|
||||
func Routes() gin.RoutesInfo {
|
||||
return engine().Routes()
|
||||
return engine.Routes()
|
||||
}
|
||||
|
||||
// Run attaches to a http.Server and starts listening and serving HTTP requests.
|
||||
// It is a shortcut for http.ListenAndServe(addr, router)
|
||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||
func Run(addr ...string) (err error) {
|
||||
return engine().Run(addr...)
|
||||
return engine.Run(addr...)
|
||||
}
|
||||
|
||||
// RunTLS attaches to a http.Server and starts listening and serving HTTPS requests.
|
||||
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
|
||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||
func RunTLS(addr, certFile, keyFile string) (err error) {
|
||||
return engine().RunTLS(addr, certFile, keyFile)
|
||||
return engine.RunTLS(addr, certFile, keyFile)
|
||||
}
|
||||
|
||||
// RunUnix attaches to a http.Server and starts listening and serving HTTP requests
|
||||
// through the specified unix socket (ie. a file)
|
||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||
func RunUnix(file string) (err error) {
|
||||
return engine().RunUnix(file)
|
||||
return engine.RunUnix(file)
|
||||
}
|
||||
|
||||
// RunFd attaches the router to a http.Server and starts listening and serving HTTP requests
|
||||
// through the specified file descriptor.
|
||||
// Note: the method will block the calling goroutine indefinitely unless on error happens.
|
||||
func RunFd(fd int) (err error) {
|
||||
return engine().RunFd(fd)
|
||||
return engine.RunFd(fd)
|
||||
}
|
||||
|
@ -321,8 +321,7 @@ func TestRenderRedirect(t *testing.T) {
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
assert.PanicsWithValue(t, "Cannot redirect with status code 200", func() {
|
||||
err := data2.Render(w)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, data2.Render(w))
|
||||
})
|
||||
|
||||
data3 := Redirect{
|
||||
|
Loading…
x
Reference in New Issue
Block a user