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"
"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")
})
}
}

View File

@ -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
}

View File

@ -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) {

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)
}
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
}

View File

@ -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.

View File

@ -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)
}

View File

@ -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{