Merge branch 'develop' into performance
Conflicts: binding/form_mapping.go context_test.go
This commit is contained in:
		
							
								
								
									
										10
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							| @ -1,10 +1,14 @@ | |||||||
| { | { | ||||||
| 	"ImportPath": "github.com/gin-gonic/gin", | 	"ImportPath": "github.com/gin-gonic/gin", | ||||||
| 	"GoVersion": "go1.3", | 	"GoVersion": "go1.4.2", | ||||||
| 	"Deps": [ | 	"Deps": [ | ||||||
| 		{ | 		{ | ||||||
| 			"ImportPath": "github.com/julienschmidt/httprouter", | 			"ImportPath": "github.com/mattn/go-colorable", | ||||||
| 			"Rev": "b428fda53bb0a764fea9c76c9413512eda291dec" | 			"Rev": "043ae16291351db8465272edf465c9f388161627" | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"ImportPath": "github.com/stretchr/testify/assert", | ||||||
|  | 			"Rev": "de7fcff264cd05cc0c90c509ea789a436a0dd206" | ||||||
| 		} | 		} | ||||||
| 	] | 	] | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,7 +4,11 @@ | |||||||
|  |  | ||||||
| package binding | package binding | ||||||
|  |  | ||||||
| import "net/http" | import ( | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"gopkg.in/joeybloggs/go-validate-yourself.v4" | ||||||
|  | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	MIMEJSON              = "application/json" | 	MIMEJSON              = "application/json" | ||||||
| @ -21,6 +25,8 @@ type Binding interface { | |||||||
| 	Bind(*http.Request, interface{}) error | 	Bind(*http.Request, interface{}) error | ||||||
| } | } | ||||||
|  |  | ||||||
|  | var _validator = validator.NewValidator("binding", validator.BakedInValidators) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	JSON     = jsonBinding{} | 	JSON     = jsonBinding{} | ||||||
| 	XML      = xmlBinding{} | 	XML      = xmlBinding{} | ||||||
|  | |||||||
| @ -19,5 +19,8 @@ func (_ getFormBinding) Bind(req *http.Request, obj interface{}) error { | |||||||
| 	if err := mapForm(obj, req.Form); err != nil { | 	if err := mapForm(obj, req.Form); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	return Validate(obj) | 	if err := _validator.ValidateStruct(obj); err != nil { | ||||||
|  | 		return error(err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,9 +18,11 @@ func (_ jsonBinding) Name() string { | |||||||
|  |  | ||||||
| func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error { | func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error { | ||||||
| 	decoder := json.NewDecoder(req.Body) | 	decoder := json.NewDecoder(req.Body) | ||||||
| 	if err := decoder.Decode(obj); err == nil { | 	if err := decoder.Decode(obj); err != nil { | ||||||
| 		return Validate(obj) |  | ||||||
| 	} else { |  | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | 	if err := _validator.ValidateStruct(obj); err != nil { | ||||||
|  | 		return error(err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
|  | |||||||
| @ -19,5 +19,8 @@ func (_ postFormBinding) Bind(req *http.Request, obj interface{}) error { | |||||||
| 	if err := mapForm(obj, req.PostForm); err != nil { | 	if err := mapForm(obj, req.PostForm); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	return Validate(obj) | 	if err := _validator.ValidateStruct(obj); err != nil { | ||||||
|  | 		return error(err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,79 +0,0 @@ | |||||||
| // Copyright 2014 Manu Martinez-Almeida.  All rights reserved. |  | ||||||
| // Use of this source code is governed by a MIT style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
|  |  | ||||||
| package binding |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"reflect" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func Validate(obj interface{}) error { |  | ||||||
| 	return validate(obj, "{{ROOT}}") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func validate(obj interface{}, parent string) error { |  | ||||||
| 	typ, val := inspectObject(obj) |  | ||||||
| 	switch typ.Kind() { |  | ||||||
| 	case reflect.Struct: |  | ||||||
| 		return validateStruct(typ, val, parent) |  | ||||||
|  |  | ||||||
| 	case reflect.Slice: |  | ||||||
| 		return validateSlice(typ, val, parent) |  | ||||||
|  |  | ||||||
| 	default: |  | ||||||
| 		return errors.New("The object is not a slice or struct.") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func inspectObject(obj interface{}) (typ reflect.Type, val reflect.Value) { |  | ||||||
| 	typ = reflect.TypeOf(obj) |  | ||||||
| 	val = reflect.ValueOf(obj) |  | ||||||
| 	if typ.Kind() == reflect.Ptr { |  | ||||||
| 		typ = typ.Elem() |  | ||||||
| 		val = val.Elem() |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func validateSlice(typ reflect.Type, val reflect.Value, parent string) error { |  | ||||||
| 	if typ.Elem().Kind() == reflect.Struct { |  | ||||||
| 		for i := 0; i < val.Len(); i++ { |  | ||||||
| 			itemValue := val.Index(i).Interface() |  | ||||||
| 			if err := validate(itemValue, parent); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func validateStruct(typ reflect.Type, val reflect.Value, parent string) error { |  | ||||||
| 	for i := 0; i < typ.NumField(); i++ { |  | ||||||
| 		field := typ.Field(i) |  | ||||||
| 		// Allow ignored and unexported fields in the struct |  | ||||||
| 		// TODO should include  || field.Tag.Get("form") == "-" |  | ||||||
| 		if len(field.PkgPath) > 0 { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		fieldValue := val.Field(i).Interface() |  | ||||||
| 		requiredField := strings.Index(field.Tag.Get("binding"), "required") > -1 |  | ||||||
|  |  | ||||||
| 		if requiredField { |  | ||||||
| 			zero := reflect.Zero(field.Type).Interface() |  | ||||||
| 			if reflect.DeepEqual(zero, fieldValue) { |  | ||||||
| 				return errors.New("Required " + field.Name + " in " + parent) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		fieldType := field.Type.Kind() |  | ||||||
| 		if fieldType == reflect.Struct || fieldType == reflect.Slice { |  | ||||||
| 			if err := validate(fieldValue, field.Name); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| @ -17,9 +17,11 @@ func (_ xmlBinding) Name() string { | |||||||
|  |  | ||||||
| func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error { | func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error { | ||||||
| 	decoder := xml.NewDecoder(req.Body) | 	decoder := xml.NewDecoder(req.Body) | ||||||
| 	if err := decoder.Decode(obj); err == nil { | 	if err := decoder.Decode(obj); err != nil { | ||||||
| 		return Validate(obj) |  | ||||||
| 	} else { |  | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | 	if err := _validator.ValidateStruct(obj); err != nil { | ||||||
|  | 		return error(err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
|  | |||||||
| @ -235,9 +235,9 @@ func (c *Context) HTMLString(code int, format string, values ...interface{}) { | |||||||
| // Returns a HTTP redirect to the specific location. | // Returns a HTTP redirect to the specific location. | ||||||
| func (c *Context) Redirect(code int, location string) { | func (c *Context) Redirect(code int, location string) { | ||||||
| 	if code >= 300 && code <= 308 { | 	if code >= 300 && code <= 308 { | ||||||
| 		c.Render(code, render.Redirect, location) | 		c.Render(code, render.Redirect, c.Request, location) | ||||||
| 	} else { | 	} else { | ||||||
| 		log.Panicf("Cannot send a redirect with status code %d", code) | 		log.Panicf("Cannot redirect with status code %d", code) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								debug.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								debug.go
									
									
									
									
									
								
							| @ -7,7 +7,7 @@ package gin | |||||||
| import "log" | import "log" | ||||||
|  |  | ||||||
| func IsDebugging() bool { | func IsDebugging() bool { | ||||||
| 	return gin_mode == debugCode | 	return ginMode == debugCode | ||||||
| } | } | ||||||
|  |  | ||||||
| func debugRoute(httpMethod, absolutePath string, handlers []HandlerFunc) { | func debugRoute(httpMethod, absolutePath string, handlers []HandlerFunc) { | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								gin.go
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								gin.go
									
									
									
									
									
								
							| @ -115,8 +115,7 @@ func (engine *Engine) allocateContext() (context *Context) { | |||||||
|  |  | ||||||
| func (engine *Engine) LoadHTMLGlob(pattern string) { | func (engine *Engine) LoadHTMLGlob(pattern string) { | ||||||
| 	if IsDebugging() { | 	if IsDebugging() { | ||||||
| 		r := &render.HTMLDebugRender{Glob: pattern} | 		engine.HTMLRender = &render.HTMLDebugRender{Glob: pattern} | ||||||
| 		engine.HTMLRender = r |  | ||||||
| 	} else { | 	} else { | ||||||
| 		templ := template.Must(template.ParseGlob(pattern)) | 		templ := template.Must(template.ParseGlob(pattern)) | ||||||
| 		engine.SetHTMLTemplate(templ) | 		engine.SetHTMLTemplate(templ) | ||||||
| @ -125,8 +124,7 @@ func (engine *Engine) LoadHTMLGlob(pattern string) { | |||||||
|  |  | ||||||
| func (engine *Engine) LoadHTMLFiles(files ...string) { | func (engine *Engine) LoadHTMLFiles(files ...string) { | ||||||
| 	if IsDebugging() { | 	if IsDebugging() { | ||||||
| 		r := &render.HTMLDebugRender{Files: files} | 		engine.HTMLRender = &render.HTMLDebugRender{Files: files} | ||||||
| 		engine.HTMLRender = r |  | ||||||
| 	} else { | 	} else { | ||||||
| 		templ := template.Must(template.ParseFiles(files...)) | 		templ := template.Must(template.ParseFiles(files...)) | ||||||
| 		engine.SetHTMLTemplate(templ) | 		engine.SetHTMLTemplate(templ) | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								logger.go
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								logger.go
									
									
									
									
									
								
							| @ -5,10 +5,9 @@ | |||||||
| package gin | package gin | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"log" | 	"fmt" | ||||||
|  | 	"io" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/mattn/go-colorable" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| @ -30,18 +29,20 @@ func ErrorLoggerT(typ uint32) HandlerFunc { | |||||||
| 	return func(c *Context) { | 	return func(c *Context) { | ||||||
| 		c.Next() | 		c.Next() | ||||||
|  |  | ||||||
|  | 		if !c.Writer.Written() { | ||||||
| 			errs := c.Errors.ByType(typ) | 			errs := c.Errors.ByType(typ) | ||||||
| 			if len(errs) > 0 { | 			if len(errs) > 0 { | ||||||
| 			// -1 status code = do not change current one |  | ||||||
| 				c.JSON(-1, c.Errors) | 				c.JSON(-1, c.Errors) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func Logger() HandlerFunc { | func Logger() HandlerFunc { | ||||||
| 	stdlogger := log.New(colorable.NewColorableStdout(), "", 0) | 	return LoggerWithFile(DefaultLogFile) | ||||||
| 	//errlogger := log.New(os.Stderr, "", 0) | } | ||||||
|  |  | ||||||
|  | func LoggerWithFile(out io.Writer) HandlerFunc { | ||||||
| 	return func(c *Context) { | 	return func(c *Context) { | ||||||
| 		// Start timer | 		// Start timer | ||||||
| 		start := time.Now() | 		start := time.Now() | ||||||
| @ -58,15 +59,16 @@ func Logger() HandlerFunc { | |||||||
| 		statusCode := c.Writer.Status() | 		statusCode := c.Writer.Status() | ||||||
| 		statusColor := colorForStatus(statusCode) | 		statusColor := colorForStatus(statusCode) | ||||||
| 		methodColor := colorForMethod(method) | 		methodColor := colorForMethod(method) | ||||||
|  | 		comment := c.Errors.String() | ||||||
|  |  | ||||||
| 		stdlogger.Printf("[GIN] %v |%s %3d %s| %12v | %s |%s  %s %-7s %s\n%s", | 		fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %12v | %s |%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, | ||||||
| 			latency, | 			latency, | ||||||
| 			clientIP, | 			clientIP, | ||||||
| 			methodColor, reset, method, | 			methodColor, reset, method, | ||||||
| 			c.Request.URL.Path, | 			c.Request.URL.Path, | ||||||
| 			c.Errors.String(), | 			comment, | ||||||
| 		) | 		) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								mode.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								mode.go
									
									
									
									
									
								
							| @ -7,6 +7,8 @@ package gin | |||||||
| import ( | import ( | ||||||
| 	"log" | 	"log" | ||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
|  | 	"github.com/mattn/go-colorable" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const GIN_MODE = "GIN_MODE" | const GIN_MODE = "GIN_MODE" | ||||||
| @ -22,8 +24,9 @@ const ( | |||||||
| 	testCode    = iota | 	testCode    = iota | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var gin_mode int = debugCode | var DefaultLogFile = colorable.NewColorableStdout() | ||||||
| var mode_name string = DebugMode | var ginMode int = debugCode | ||||||
|  | var modeName string = DebugMode | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	value := os.Getenv(GIN_MODE) | 	value := os.Getenv(GIN_MODE) | ||||||
| @ -37,17 +40,17 @@ func init() { | |||||||
| func SetMode(value string) { | func SetMode(value string) { | ||||||
| 	switch value { | 	switch value { | ||||||
| 	case DebugMode: | 	case DebugMode: | ||||||
| 		gin_mode = debugCode | 		ginMode = debugCode | ||||||
| 	case ReleaseMode: | 	case ReleaseMode: | ||||||
| 		gin_mode = releaseCode | 		ginMode = releaseCode | ||||||
| 	case TestMode: | 	case TestMode: | ||||||
| 		gin_mode = testCode | 		ginMode = testCode | ||||||
| 	default: | 	default: | ||||||
| 		log.Panic("gin mode unknown: " + value) | 		log.Panic("gin mode unknown: " + value) | ||||||
| 	} | 	} | ||||||
| 	mode_name = value | 	modeName = value | ||||||
| } | } | ||||||
|  |  | ||||||
| func Mode() string { | func Mode() string { | ||||||
| 	return mode_name | 	return modeName | ||||||
| } | } | ||||||
|  | |||||||
| @ -44,8 +44,9 @@ var ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func (_ redirectRender) Render(w http.ResponseWriter, code int, data ...interface{}) error { | func (_ redirectRender) Render(w http.ResponseWriter, code int, data ...interface{}) error { | ||||||
| 	w.Header().Set("Location", data[0].(string)) | 	req := data[0].(*http.Request) | ||||||
| 	w.WriteHeader(code) | 	location := data[1].(string) | ||||||
|  | 	http.Redirect(w, req, location, code) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -12,8 +12,8 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| 	NoWritten     = -1 | 	noWritten     = -1 | ||||||
| 	DefaultStatus = 200 | 	defaultStatus = 200 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type ( | type ( | ||||||
| @ -38,8 +38,8 @@ type ( | |||||||
|  |  | ||||||
| func (w *responseWriter) reset(writer http.ResponseWriter) { | func (w *responseWriter) reset(writer http.ResponseWriter) { | ||||||
| 	w.ResponseWriter = writer | 	w.ResponseWriter = writer | ||||||
| 	w.size = NoWritten | 	w.size = noWritten | ||||||
| 	w.status = DefaultStatus | 	w.status = defaultStatus | ||||||
| } | } | ||||||
|  |  | ||||||
| func (w *responseWriter) WriteHeader(code int) { | func (w *responseWriter) WriteHeader(code int) { | ||||||
| @ -74,7 +74,7 @@ func (w *responseWriter) Size() int { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (w *responseWriter) Written() bool { | func (w *responseWriter) Written() bool { | ||||||
| 	return w.size != NoWritten | 	return w.size != noWritten | ||||||
| } | } | ||||||
|  |  | ||||||
| // Implements the http.Hijacker interface | // Implements the http.Hijacker interface | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user