Merge 068942b5ce84ee37c62b0b52d3dad8cdb0db16eb into f469c1be3925ab32bd38fbf944b230c70abe9e2b

This commit is contained in:
heige 2021-09-28 21:52:02 +09:00 committed by GitHub
commit 72a077e151
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 55 additions and 51 deletions

View File

@ -8,13 +8,11 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"strings" "strings"
"sync"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
) )
type defaultValidator struct { type defaultValidator struct {
once sync.Once
validate *validator.Validate validate *validator.Validate
} }
@ -76,7 +74,7 @@ func (v *defaultValidator) ValidateStruct(obj interface{}) error {
// validateStruct receives struct type // validateStruct receives struct type
func (v *defaultValidator) validateStruct(obj interface{}) error { func (v *defaultValidator) validateStruct(obj interface{}) error {
v.lazyinit() v.lazyInit()
return v.validate.Struct(obj) 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 - // or struct level validations. See validator GoDoc for more info -
// https://pkg.go.dev/github.com/go-playground/validator/v10 // https://pkg.go.dev/github.com/go-playground/validator/v10
func (v *defaultValidator) Engine() interface{} { func (v *defaultValidator) Engine() interface{} {
v.lazyinit() v.lazyInit()
return v.validate return v.validate
} }
func (v *defaultValidator) lazyinit() { // lazyInit initialize the validate Settings, no need to use sync.once
v.once.Do(func() { func (v *defaultValidator) lazyInit() {
if v.validate == nil {
v.validate = validator.New() v.validate = validator.New()
v.validate.SetTagName("binding") v.validate.SetTagName("binding")
}) }
} }

View File

@ -17,7 +17,8 @@ import (
) )
var ( var (
errUnknownType = errors.New("unknown type") // ErrUnknownType unknown type
ErrUnknownType = errors.New("unknown type")
// ErrConvertMapStringSlice can not covert to map[string][]string // ErrConvertMapStringSlice can not covert to map[string][]string
ErrConvertMapStringSlice = errors.New("can not convert to map slices of strings") 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) 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] vs, ok := form[tagValue]
if !ok && !opt.isDefaultExists { if !ok && !opt.isDefaultExists {
return false, nil return false, nil
@ -240,7 +241,7 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
case reflect.Map: case reflect.Map:
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface()) return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
default: default:
return errUnknownType return ErrUnknownType
} }
return nil return nil
} }

View File

@ -124,7 +124,7 @@ func TestMappingUnknownFieldType(t *testing.T) {
err := mappingByPtr(&s, formSource{"U": {"unknown"}}, "form") err := mappingByPtr(&s, formSource{"U": {"unknown"}}, "form")
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, errUnknownType, err) assert.Equal(t, ErrUnknownType, err)
} }
func TestMappingURI(t *testing.T) { func TestMappingURI(t *testing.T) {

View File

@ -32,7 +32,7 @@ func (r *multipartRequest) TrySet(value reflect.Value, field reflect.StructField
return setByForm(value, field, r.MultipartForm.Value, key, opt) 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() { switch value.Kind() {
case reflect.Ptr: case reflect.Ptr:
switch value.Interface().(type) { switch value.Interface().(type) {
@ -48,7 +48,7 @@ func setByMultipartFormFile(value reflect.Value, field reflect.StructField, file
} }
case reflect.Slice: case reflect.Slice:
slice := reflect.MakeSlice(value.Type(), len(files), len(files)) 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 { if err != nil || !isSetted {
return isSetted, err return isSetted, err
} }
@ -60,7 +60,7 @@ func setByMultipartFormFile(value reflect.Value, field reflect.StructField, file
return false, ErrMultiFileHeader 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) { if value.Len() != len(files) {
return false, ErrMultiFileHeaderLenInvalid return false, ErrMultiFileHeaderLenInvalid
} }

View File

@ -766,7 +766,7 @@ func (c *Context) ClientIP() string {
if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil { if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil {
for _, headerName := range c.engine.RemoteIPHeaders { for _, headerName := range c.engine.RemoteIPHeaders {
ip, valid := validateHeader(c.requestHeader(headerName)) ip, valid := getIPFromHeader(c.requestHeader(headerName))
if valid { if valid {
return ip return ip
} }
@ -800,10 +800,17 @@ func (c *Context) RemoteIP() (net.IP, bool) {
return remoteIP, false 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 == "" { if header == "" {
return "", false return "", false
} }
var (
clientIP string
valid bool
)
items := strings.Split(header, ",") items := strings.Split(header, ",")
for i, ipStr := range items { for i, ipStr := range items {
ipStr = strings.TrimSpace(ipStr) ipStr = strings.TrimSpace(ipStr)
@ -820,7 +827,8 @@ func validateHeader(header string) (clientIP string, valid bool) {
valid = true valid = true
} }
} }
return
return clientIP, valid
} }
// ContentType returns the Content-Type header of the request. // ContentType returns the Content-Type header of the request.

View File

@ -7,100 +7,97 @@ package ginS
import ( import (
"html/template" "html/template"
"net/http" "net/http"
"sync"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
var once sync.Once var engine *gin.Engine
var internalEngine *gin.Engine
func engine() *gin.Engine { func init() {
once.Do(func() { if engine == nil {
internalEngine = gin.Default() engine = gin.Default()
}) }
return internalEngine
} }
// LoadHTMLGlob is a wrapper for Engine.LoadHTMLGlob. // LoadHTMLGlob is a wrapper for Engine.LoadHTMLGlob.
func LoadHTMLGlob(pattern string) { func LoadHTMLGlob(pattern string) {
engine().LoadHTMLGlob(pattern) engine.LoadHTMLGlob(pattern)
} }
// LoadHTMLFiles is a wrapper for Engine.LoadHTMLFiles. // LoadHTMLFiles is a wrapper for Engine.LoadHTMLFiles.
func LoadHTMLFiles(files ...string) { func LoadHTMLFiles(files ...string) {
engine().LoadHTMLFiles(files...) engine.LoadHTMLFiles(files...)
} }
// SetHTMLTemplate is a wrapper for Engine.SetHTMLTemplate. // SetHTMLTemplate is a wrapper for Engine.SetHTMLTemplate.
func SetHTMLTemplate(templ *template.Template) { func SetHTMLTemplate(templ *template.Template) {
engine().SetHTMLTemplate(templ) engine.SetHTMLTemplate(templ)
} }
// NoRoute adds handlers for NoRoute. It return a 404 code by default. // NoRoute adds handlers for NoRoute. It return a 404 code by default.
func NoRoute(handlers ...gin.HandlerFunc) { func NoRoute(handlers ...gin.HandlerFunc) {
engine().NoRoute(handlers...) engine.NoRoute(handlers...)
} }
// NoMethod is a wrapper for Engine.NoMethod. // NoMethod is a wrapper for Engine.NoMethod.
func NoMethod(handlers ...gin.HandlerFunc) { 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. // 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. // For example, all the routes that use a common middleware for authorization could be grouped.
func Group(relativePath string, handlers ...gin.HandlerFunc) *gin.RouterGroup { 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. // Handle is a wrapper for Engine.Handle.
func Handle(httpMethod, relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { 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) // POST is a shortcut for router.Handle("POST", path, handle)
func POST(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { 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) // GET is a shortcut for router.Handle("GET", path, handle)
func GET(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { 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) // DELETE is a shortcut for router.Handle("DELETE", path, handle)
func DELETE(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { 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) // PATCH is a shortcut for router.Handle("PATCH", path, handle)
func PATCH(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { 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) // PUT is a shortcut for router.Handle("PUT", path, handle)
func PUT(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { 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) // OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle)
func OPTIONS(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { 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) // HEAD is a shortcut for router.Handle("HEAD", path, handle)
func HEAD(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { 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. // Any is a wrapper for Engine.Any.
func Any(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes { 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. // StaticFile is a wrapper for Engine.StaticFile.
func StaticFile(relativePath, filepath string) gin.IRoutes { 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. // Static serves files from the given file system root.
@ -110,50 +107,50 @@ func StaticFile(relativePath, filepath string) gin.IRoutes {
// use : // use :
// router.Static("/static", "/var/www") // router.Static("/static", "/var/www")
func Static(relativePath, root string) gin.IRoutes { func Static(relativePath, root string) gin.IRoutes {
return engine().Static(relativePath, root) return engine.Static(relativePath, root)
} }
// StaticFS is a wrapper for Engine.StaticFS. // StaticFS is a wrapper for Engine.StaticFS.
func StaticFS(relativePath string, fs http.FileSystem) gin.IRoutes { 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 // 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... // 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. // For example, this is the right place for a logger or error management middleware.
func Use(middlewares ...gin.HandlerFunc) gin.IRoutes { func Use(middlewares ...gin.HandlerFunc) gin.IRoutes {
return engine().Use(middlewares...) return engine.Use(middlewares...)
} }
// Routes returns a slice of registered routes. // Routes returns a slice of registered routes.
func Routes() gin.RoutesInfo { func Routes() gin.RoutesInfo {
return engine().Routes() return engine.Routes()
} }
// Run attaches to a http.Server and starts listening and serving HTTP requests. // Run attaches to a http.Server and starts listening and serving HTTP requests.
// It is a shortcut for http.ListenAndServe(addr, router) // It is a shortcut for http.ListenAndServe(addr, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens. // Note: this method will block the calling goroutine indefinitely unless an error happens.
func Run(addr ...string) (err error) { 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. // RunTLS attaches to a http.Server and starts listening and serving HTTPS requests.
// It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router) // It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens. // Note: this method will block the calling goroutine indefinitely unless an error happens.
func RunTLS(addr, certFile, keyFile string) (err error) { 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 // RunUnix attaches to a http.Server and starts listening and serving HTTP requests
// through the specified unix socket (ie. a file) // through the specified unix socket (ie. a file)
// Note: this method will block the calling goroutine indefinitely unless an error happens. // Note: this method will block the calling goroutine indefinitely unless an error happens.
func RunUnix(file string) (err error) { 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 // RunFd attaches the router to a http.Server and starts listening and serving HTTP requests
// through the specified file descriptor. // through the specified file descriptor.
// Note: the method will block the calling goroutine indefinitely unless on error happens. // Note: the method will block the calling goroutine indefinitely unless on error happens.
func RunFd(fd int) (err error) { func RunFd(fd int) (err error) {
return engine().RunFd(fd) return engine.RunFd(fd)
} }

View File

@ -321,8 +321,7 @@ func TestRenderRedirect(t *testing.T) {
w = httptest.NewRecorder() w = httptest.NewRecorder()
assert.PanicsWithValue(t, "Cannot redirect with status code 200", func() { assert.PanicsWithValue(t, "Cannot redirect with status code 200", func() {
err := data2.Render(w) assert.NoError(t, data2.Render(w))
assert.NoError(t, err)
}) })
data3 := Redirect{ data3 := Redirect{