Jelajahi Sumber

Merge branch 'develop' into performance

Conflicts:
	binding/form_mapping.go
	context_test.go
Manu Mtz-Almeida 10 tahun lalu
induk
melakukan
a4eadceb45
14 mengubah file dengan 68 tambahan dan 123 penghapusan
  1. 7 3
      Godeps/Godeps.json
  2. 7 1
      binding/binding.go
  3. 4 1
      binding/get_form.go
  4. 5 3
      binding/json.go
  5. 4 1
      binding/post_form.go
  6. 0 79
      binding/validate.go
  7. 5 3
      binding/xml.go
  8. 2 2
      context.go
  9. 1 1
      debug.go
  10. 2 4
      gin.go
  11. 13 11
      logger.go
  12. 10 7
      mode.go
  13. 3 2
      render/render.go
  14. 5 5
      response_writer.go

+ 7 - 3
Godeps/Godeps.json

@@ -1,10 +1,14 @@
 {
 	"ImportPath": "github.com/gin-gonic/gin",
-	"GoVersion": "go1.3",
+	"GoVersion": "go1.4.2",
 	"Deps": [
 		{
-			"ImportPath": "github.com/julienschmidt/httprouter",
-			"Rev": "b428fda53bb0a764fea9c76c9413512eda291dec"
+			"ImportPath": "github.com/mattn/go-colorable",
+			"Rev": "043ae16291351db8465272edf465c9f388161627"
+		},
+		{
+			"ImportPath": "github.com/stretchr/testify/assert",
+			"Rev": "de7fcff264cd05cc0c90c509ea789a436a0dd206"
 		}
 	]
 }

+ 7 - 1
binding/binding.go

@@ -4,7 +4,11 @@
 
 package binding
 
-import "net/http"
+import (
+	"net/http"
+
+	"gopkg.in/joeybloggs/go-validate-yourself.v4"
+)
 
 const (
 	MIMEJSON              = "application/json"
@@ -21,6 +25,8 @@ type Binding interface {
 	Bind(*http.Request, interface{}) error
 }
 
+var _validator = validator.NewValidator("binding", validator.BakedInValidators)
+
 var (
 	JSON     = jsonBinding{}
 	XML      = xmlBinding{}

+ 4 - 1
binding/get_form.go

@@ -19,5 +19,8 @@ func (_ getFormBinding) Bind(req *http.Request, obj interface{}) error {
 	if err := mapForm(obj, req.Form); err != nil {
 		return err
 	}
-	return Validate(obj)
+	if err := _validator.ValidateStruct(obj); err != nil {
+		return error(err)
+	}
+	return nil
 }

+ 5 - 3
binding/json.go

@@ -18,9 +18,11 @@ func (_ jsonBinding) Name() string {
 
 func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
 	decoder := json.NewDecoder(req.Body)
-	if err := decoder.Decode(obj); err == nil {
-		return Validate(obj)
-	} else {
+	if err := decoder.Decode(obj); err != nil {
 		return err
 	}
+	if err := _validator.ValidateStruct(obj); err != nil {
+		return error(err)
+	}
+	return nil
 }

+ 4 - 1
binding/post_form.go

@@ -19,5 +19,8 @@ func (_ postFormBinding) Bind(req *http.Request, obj interface{}) error {
 	if err := mapForm(obj, req.PostForm); err != nil {
 		return err
 	}
-	return Validate(obj)
+	if err := _validator.ValidateStruct(obj); err != nil {
+		return error(err)
+	}
+	return nil
 }

+ 0 - 79
binding/validate.go

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

+ 5 - 3
binding/xml.go

@@ -17,9 +17,11 @@ func (_ xmlBinding) Name() string {
 
 func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error {
 	decoder := xml.NewDecoder(req.Body)
-	if err := decoder.Decode(obj); err == nil {
-		return Validate(obj)
-	} else {
+	if err := decoder.Decode(obj); err != nil {
 		return err
 	}
+	if err := _validator.ValidateStruct(obj); err != nil {
+		return error(err)
+	}
+	return nil
 }

+ 2 - 2
context.go

@@ -235,9 +235,9 @@ func (c *Context) HTMLString(code int, format string, values ...interface{}) {
 // Returns a HTTP redirect to the specific location.
 func (c *Context) Redirect(code int, location string) {
 	if code >= 300 && code <= 308 {
-		c.Render(code, render.Redirect, location)
+		c.Render(code, render.Redirect, c.Request, location)
 	} else {
-		log.Panicf("Cannot send a redirect with status code %d", code)
+		log.Panicf("Cannot redirect with status code %d", code)
 	}
 }
 

+ 1 - 1
debug.go

@@ -7,7 +7,7 @@ package gin
 import "log"
 
 func IsDebugging() bool {
-	return gin_mode == debugCode
+	return ginMode == debugCode
 }
 
 func debugRoute(httpMethod, absolutePath string, handlers []HandlerFunc) {

+ 2 - 4
gin.go

@@ -115,8 +115,7 @@ func (engine *Engine) allocateContext() (context *Context) {
 
 func (engine *Engine) LoadHTMLGlob(pattern string) {
 	if IsDebugging() {
-		r := &render.HTMLDebugRender{Glob: pattern}
-		engine.HTMLRender = r
+		engine.HTMLRender = &render.HTMLDebugRender{Glob: pattern}
 	} else {
 		templ := template.Must(template.ParseGlob(pattern))
 		engine.SetHTMLTemplate(templ)
@@ -125,8 +124,7 @@ func (engine *Engine) LoadHTMLGlob(pattern string) {
 
 func (engine *Engine) LoadHTMLFiles(files ...string) {
 	if IsDebugging() {
-		r := &render.HTMLDebugRender{Files: files}
-		engine.HTMLRender = r
+		engine.HTMLRender = &render.HTMLDebugRender{Files: files}
 	} else {
 		templ := template.Must(template.ParseFiles(files...))
 		engine.SetHTMLTemplate(templ)

+ 13 - 11
logger.go

@@ -5,10 +5,9 @@
 package gin
 
 import (
-	"log"
+	"fmt"
+	"io"
 	"time"
-
-	"github.com/mattn/go-colorable"
 )
 
 var (
@@ -30,18 +29,20 @@ func ErrorLoggerT(typ uint32) HandlerFunc {
 	return func(c *Context) {
 		c.Next()
 
-		errs := c.Errors.ByType(typ)
-		if len(errs) > 0 {
-			// -1 status code = do not change current one
-			c.JSON(-1, c.Errors)
+		if !c.Writer.Written() {
+			errs := c.Errors.ByType(typ)
+			if len(errs) > 0 {
+				c.JSON(-1, c.Errors)
+			}
 		}
 	}
 }
 
 func Logger() HandlerFunc {
-	stdlogger := log.New(colorable.NewColorableStdout(), "", 0)
-	//errlogger := log.New(os.Stderr, "", 0)
+	return LoggerWithFile(DefaultLogFile)
+}
 
+func LoggerWithFile(out io.Writer) HandlerFunc {
 	return func(c *Context) {
 		// Start timer
 		start := time.Now()
@@ -58,15 +59,16 @@ func Logger() HandlerFunc {
 		statusCode := c.Writer.Status()
 		statusColor := colorForStatus(statusCode)
 		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"),
 			statusColor, statusCode, reset,
 			latency,
 			clientIP,
 			methodColor, reset, method,
 			c.Request.URL.Path,
-			c.Errors.String(),
+			comment,
 		)
 	}
 }

+ 10 - 7
mode.go

@@ -7,6 +7,8 @@ package gin
 import (
 	"log"
 	"os"
+
+	"github.com/mattn/go-colorable"
 )
 
 const GIN_MODE = "GIN_MODE"
@@ -22,8 +24,9 @@ const (
 	testCode    = iota
 )
 
-var gin_mode int = debugCode
-var mode_name string = DebugMode
+var DefaultLogFile = colorable.NewColorableStdout()
+var ginMode int = debugCode
+var modeName string = DebugMode
 
 func init() {
 	value := os.Getenv(GIN_MODE)
@@ -37,17 +40,17 @@ func init() {
 func SetMode(value string) {
 	switch value {
 	case DebugMode:
-		gin_mode = debugCode
+		ginMode = debugCode
 	case ReleaseMode:
-		gin_mode = releaseCode
+		ginMode = releaseCode
 	case TestMode:
-		gin_mode = testCode
+		ginMode = testCode
 	default:
 		log.Panic("gin mode unknown: " + value)
 	}
-	mode_name = value
+	modeName = value
 }
 
 func Mode() string {
-	return mode_name
+	return modeName
 }

+ 3 - 2
render/render.go

@@ -44,8 +44,9 @@ var (
 )
 
 func (_ redirectRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
-	w.Header().Set("Location", data[0].(string))
-	w.WriteHeader(code)
+	req := data[0].(*http.Request)
+	location := data[1].(string)
+	http.Redirect(w, req, location, code)
 	return nil
 }
 

+ 5 - 5
response_writer.go

@@ -12,8 +12,8 @@ import (
 )
 
 const (
-	NoWritten     = -1
-	DefaultStatus = 200
+	noWritten     = -1
+	defaultStatus = 200
 )
 
 type (
@@ -38,8 +38,8 @@ type (
 
 func (w *responseWriter) reset(writer http.ResponseWriter) {
 	w.ResponseWriter = writer
-	w.size = NoWritten
-	w.status = DefaultStatus
+	w.size = noWritten
+	w.status = defaultStatus
 }
 
 func (w *responseWriter) WriteHeader(code int) {
@@ -74,7 +74,7 @@ func (w *responseWriter) Size() int {
 }
 
 func (w *responseWriter) Written() bool {
-	return w.size != NoWritten
+	return w.size != noWritten
 }
 
 // Implements the http.Hijacker interface