mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			98 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			98 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
| package web
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"mime"
 | |
| 	"net/http"
 | |
| 	"reflect"
 | |
| )
 | |
| 
 | |
| // Bind deserializes JSON payload from the request
 | |
| func Bind(req *http.Request, v interface{}) error {
 | |
| 	if req.Body != nil {
 | |
| 		m, _, err := mime.ParseMediaType(req.Header.Get("Content-type"))
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if m != "application/json" {
 | |
| 			return errors.New("bad content type")
 | |
| 		}
 | |
| 		defer func() { _ = req.Body.Close() }()
 | |
| 		err = json.NewDecoder(req.Body).Decode(v)
 | |
| 		if err != nil && !errors.Is(err, io.EOF) {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return validate(v)
 | |
| }
 | |
| 
 | |
| type Validator interface {
 | |
| 	Validate() error
 | |
| }
 | |
| 
 | |
| func validate(obj interface{}) error {
 | |
| 	// First check if obj is nil, because we cannot validate those.
 | |
| 	if obj == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	// Second, check if obj has a nil interface value.
 | |
| 	// This is to prevent panics when obj is an instance of uninitialised struct pointer / interface.
 | |
| 	t := reflect.TypeOf(obj)
 | |
| 	v := reflect.ValueOf(obj)
 | |
| 
 | |
| 	if v.Kind() == reflect.Ptr && v.IsNil() {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	// If type has a Validate() method - use that
 | |
| 	if validator, ok := obj.(Validator); ok {
 | |
| 		return validator.Validate()
 | |
| 	}
 | |
| 
 | |
| 	// Otherwise, use reflection to match `binding:"Required"` struct field tags.
 | |
| 	// Resolve all pointers and interfaces, until we get a concrete type.
 | |
| 	for v.Kind() == reflect.Interface || v.Kind() == reflect.Ptr {
 | |
| 		t = t.Elem()
 | |
| 		v = v.Elem()
 | |
| 	}
 | |
| 	switch v.Kind() {
 | |
| 	// For arrays and slices - iterate over each element and validate it recursively
 | |
| 	case reflect.Slice, reflect.Array:
 | |
| 		for i := 0; i < v.Len(); i++ {
 | |
| 			e := v.Index(i).Interface()
 | |
| 			if err := validate(e); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 	// For structs - iterate over each field, check for the "Required" constraint (Macaron legacy), then validate it recursively
 | |
| 	case reflect.Struct:
 | |
| 		for i := 0; i < v.NumField(); i++ {
 | |
| 			field := t.Field(i)
 | |
| 			value := v.Field(i)
 | |
| 			rule := field.Tag.Get("binding")
 | |
| 			if !value.CanInterface() {
 | |
| 				continue
 | |
| 			}
 | |
| 			if rule == "Required" {
 | |
| 				zero := reflect.Zero(field.Type).Interface()
 | |
| 				if value.Kind() == reflect.Slice {
 | |
| 					if value.Len() == 0 {
 | |
| 						return fmt.Errorf("required slice %s must not be empty", field.Name)
 | |
| 					}
 | |
| 				} else if reflect.DeepEqual(zero, value.Interface()) {
 | |
| 					return fmt.Errorf("required value %s must not be empty", field.Name)
 | |
| 				}
 | |
| 			}
 | |
| 			if err := validate(value.Interface()); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 	default: // ignore
 | |
| 	}
 | |
| 	return nil
 | |
| }
 |