github.com/astaxie/beego@v1.12.3/context/input.go (about)

     1  // Copyright 2014 beego Author. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package context
    16  
    17  import (
    18  	"bytes"
    19  	"compress/gzip"
    20  	"errors"
    21  	"io"
    22  	"io/ioutil"
    23  	"net"
    24  	"net/http"
    25  	"net/url"
    26  	"reflect"
    27  	"regexp"
    28  	"strconv"
    29  	"strings"
    30  	"sync"
    31  
    32  	"github.com/astaxie/beego/session"
    33  )
    34  
    35  // Regexes for checking the accept headers
    36  // TODO make sure these are correct
    37  var (
    38  	acceptsHTMLRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`)
    39  	acceptsXMLRegex  = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`)
    40  	acceptsJSONRegex = regexp.MustCompile(`(application/json)(?:,|$)`)
    41  	acceptsYAMLRegex = regexp.MustCompile(`(application/x-yaml)(?:,|$)`)
    42  	maxParam         = 50
    43  )
    44  
    45  // BeegoInput operates the http request header, data, cookie and body.
    46  // it also contains router params and current session.
    47  type BeegoInput struct {
    48  	Context       *Context
    49  	CruSession    session.Store
    50  	pnames        []string
    51  	pvalues       []string
    52  	data          map[interface{}]interface{} // store some values in this context when calling context in filter or controller.
    53  	dataLock      sync.RWMutex
    54  	RequestBody   []byte
    55  	RunMethod     string
    56  	RunController reflect.Type
    57  }
    58  
    59  // NewInput return BeegoInput generated by Context.
    60  func NewInput() *BeegoInput {
    61  	return &BeegoInput{
    62  		pnames:  make([]string, 0, maxParam),
    63  		pvalues: make([]string, 0, maxParam),
    64  		data:    make(map[interface{}]interface{}),
    65  	}
    66  }
    67  
    68  // Reset init the BeegoInput
    69  func (input *BeegoInput) Reset(ctx *Context) {
    70  	input.Context = ctx
    71  	input.CruSession = nil
    72  	input.pnames = input.pnames[:0]
    73  	input.pvalues = input.pvalues[:0]
    74  	input.dataLock.Lock()
    75  	input.data = nil
    76  	input.dataLock.Unlock()
    77  	input.RequestBody = []byte{}
    78  }
    79  
    80  // Protocol returns request protocol name, such as HTTP/1.1 .
    81  func (input *BeegoInput) Protocol() string {
    82  	return input.Context.Request.Proto
    83  }
    84  
    85  // URI returns full request url with query string, fragment.
    86  func (input *BeegoInput) URI() string {
    87  	return input.Context.Request.RequestURI
    88  }
    89  
    90  // URL returns request url path (without query string, fragment).
    91  func (input *BeegoInput) URL() string {
    92  	return input.Context.Request.URL.EscapedPath()
    93  }
    94  
    95  // Site returns base site url as scheme://domain type.
    96  func (input *BeegoInput) Site() string {
    97  	return input.Scheme() + "://" + input.Domain()
    98  }
    99  
   100  // Scheme returns request scheme as "http" or "https".
   101  func (input *BeegoInput) Scheme() string {
   102  	if scheme := input.Header("X-Forwarded-Proto"); scheme != "" {
   103  		return scheme
   104  	}
   105  	if input.Context.Request.URL.Scheme != "" {
   106  		return input.Context.Request.URL.Scheme
   107  	}
   108  	if input.Context.Request.TLS == nil {
   109  		return "http"
   110  	}
   111  	return "https"
   112  }
   113  
   114  // Domain returns host name.
   115  // Alias of Host method.
   116  func (input *BeegoInput) Domain() string {
   117  	return input.Host()
   118  }
   119  
   120  // Host returns host name.
   121  // if no host info in request, return localhost.
   122  func (input *BeegoInput) Host() string {
   123  	if input.Context.Request.Host != "" {
   124  		if hostPart, _, err := net.SplitHostPort(input.Context.Request.Host); err == nil {
   125  			return hostPart
   126  		}
   127  		return input.Context.Request.Host
   128  	}
   129  	return "localhost"
   130  }
   131  
   132  // Method returns http request method.
   133  func (input *BeegoInput) Method() string {
   134  	return input.Context.Request.Method
   135  }
   136  
   137  // Is returns boolean of this request is on given method, such as Is("POST").
   138  func (input *BeegoInput) Is(method string) bool {
   139  	return input.Method() == method
   140  }
   141  
   142  // IsGet Is this a GET method request?
   143  func (input *BeegoInput) IsGet() bool {
   144  	return input.Is("GET")
   145  }
   146  
   147  // IsPost Is this a POST method request?
   148  func (input *BeegoInput) IsPost() bool {
   149  	return input.Is("POST")
   150  }
   151  
   152  // IsHead Is this a Head method request?
   153  func (input *BeegoInput) IsHead() bool {
   154  	return input.Is("HEAD")
   155  }
   156  
   157  // IsOptions Is this a OPTIONS method request?
   158  func (input *BeegoInput) IsOptions() bool {
   159  	return input.Is("OPTIONS")
   160  }
   161  
   162  // IsPut Is this a PUT method request?
   163  func (input *BeegoInput) IsPut() bool {
   164  	return input.Is("PUT")
   165  }
   166  
   167  // IsDelete Is this a DELETE method request?
   168  func (input *BeegoInput) IsDelete() bool {
   169  	return input.Is("DELETE")
   170  }
   171  
   172  // IsPatch Is this a PATCH method request?
   173  func (input *BeegoInput) IsPatch() bool {
   174  	return input.Is("PATCH")
   175  }
   176  
   177  // IsAjax returns boolean of this request is generated by ajax.
   178  func (input *BeegoInput) IsAjax() bool {
   179  	return input.Header("X-Requested-With") == "XMLHttpRequest"
   180  }
   181  
   182  // IsSecure returns boolean of this request is in https.
   183  func (input *BeegoInput) IsSecure() bool {
   184  	return input.Scheme() == "https"
   185  }
   186  
   187  // IsWebsocket returns boolean of this request is in webSocket.
   188  func (input *BeegoInput) IsWebsocket() bool {
   189  	return input.Header("Upgrade") == "websocket"
   190  }
   191  
   192  // IsUpload returns boolean of whether file uploads in this request or not..
   193  func (input *BeegoInput) IsUpload() bool {
   194  	return strings.Contains(input.Header("Content-Type"), "multipart/form-data")
   195  }
   196  
   197  // AcceptsHTML Checks if request accepts html response
   198  func (input *BeegoInput) AcceptsHTML() bool {
   199  	return acceptsHTMLRegex.MatchString(input.Header("Accept"))
   200  }
   201  
   202  // AcceptsXML Checks if request accepts xml response
   203  func (input *BeegoInput) AcceptsXML() bool {
   204  	return acceptsXMLRegex.MatchString(input.Header("Accept"))
   205  }
   206  
   207  // AcceptsJSON Checks if request accepts json response
   208  func (input *BeegoInput) AcceptsJSON() bool {
   209  	return acceptsJSONRegex.MatchString(input.Header("Accept"))
   210  }
   211  
   212  // AcceptsYAML Checks if request accepts json response
   213  func (input *BeegoInput) AcceptsYAML() bool {
   214  	return acceptsYAMLRegex.MatchString(input.Header("Accept"))
   215  }
   216  
   217  // IP returns request client ip.
   218  // if in proxy, return first proxy id.
   219  // if error, return RemoteAddr.
   220  func (input *BeegoInput) IP() string {
   221  	ips := input.Proxy()
   222  	if len(ips) > 0 && ips[0] != "" {
   223  		rip, _, err := net.SplitHostPort(ips[0])
   224  		if err != nil {
   225  			rip = ips[0]
   226  		}
   227  		return rip
   228  	}
   229  	if ip, _, err := net.SplitHostPort(input.Context.Request.RemoteAddr); err == nil {
   230  		return ip
   231  	}
   232  	return input.Context.Request.RemoteAddr
   233  }
   234  
   235  // Proxy returns proxy client ips slice.
   236  func (input *BeegoInput) Proxy() []string {
   237  	if ips := input.Header("X-Forwarded-For"); ips != "" {
   238  		return strings.Split(ips, ",")
   239  	}
   240  	return []string{}
   241  }
   242  
   243  // Referer returns http referer header.
   244  func (input *BeegoInput) Referer() string {
   245  	return input.Header("Referer")
   246  }
   247  
   248  // Refer returns http referer header.
   249  func (input *BeegoInput) Refer() string {
   250  	return input.Referer()
   251  }
   252  
   253  // SubDomains returns sub domain string.
   254  // if aa.bb.domain.com, returns aa.bb .
   255  func (input *BeegoInput) SubDomains() string {
   256  	parts := strings.Split(input.Host(), ".")
   257  	if len(parts) >= 3 {
   258  		return strings.Join(parts[:len(parts)-2], ".")
   259  	}
   260  	return ""
   261  }
   262  
   263  // Port returns request client port.
   264  // when error or empty, return 80.
   265  func (input *BeegoInput) Port() int {
   266  	if _, portPart, err := net.SplitHostPort(input.Context.Request.Host); err == nil {
   267  		port, _ := strconv.Atoi(portPart)
   268  		return port
   269  	}
   270  	return 80
   271  }
   272  
   273  // UserAgent returns request client user agent string.
   274  func (input *BeegoInput) UserAgent() string {
   275  	return input.Header("User-Agent")
   276  }
   277  
   278  // ParamsLen return the length of the params
   279  func (input *BeegoInput) ParamsLen() int {
   280  	return len(input.pnames)
   281  }
   282  
   283  // Param returns router param by a given key.
   284  func (input *BeegoInput) Param(key string) string {
   285  	for i, v := range input.pnames {
   286  		if v == key && i <= len(input.pvalues) {
   287  			// we cannot use url.PathEscape(input.pvalues[i])
   288  			// for example, if the value is /a/b
   289  			// after url.PathEscape(input.pvalues[i]), the value is %2Fa%2Fb
   290  			// However, the value is used in ControllerRegister.ServeHTTP
   291  			// and split by "/", so function crash...
   292  			return input.pvalues[i]
   293  		}
   294  	}
   295  	return ""
   296  }
   297  
   298  // Params returns the map[key]value.
   299  func (input *BeegoInput) Params() map[string]string {
   300  	m := make(map[string]string)
   301  	for i, v := range input.pnames {
   302  		if i <= len(input.pvalues) {
   303  			m[v] = input.pvalues[i]
   304  		}
   305  	}
   306  	return m
   307  }
   308  
   309  // SetParam will set the param with key and value
   310  func (input *BeegoInput) SetParam(key, val string) {
   311  	// check if already exists
   312  	for i, v := range input.pnames {
   313  		if v == key && i <= len(input.pvalues) {
   314  			input.pvalues[i] = val
   315  			return
   316  		}
   317  	}
   318  	input.pvalues = append(input.pvalues, val)
   319  	input.pnames = append(input.pnames, key)
   320  }
   321  
   322  // ResetParams clears any of the input's Params
   323  // This function is used to clear parameters so they may be reset between filter
   324  // passes.
   325  func (input *BeegoInput) ResetParams() {
   326  	input.pnames = input.pnames[:0]
   327  	input.pvalues = input.pvalues[:0]
   328  }
   329  
   330  // Query returns input data item string by a given string.
   331  func (input *BeegoInput) Query(key string) string {
   332  	if val := input.Param(key); val != "" {
   333  		return val
   334  	}
   335  	if input.Context.Request.Form == nil {
   336  		input.dataLock.Lock()
   337  		if input.Context.Request.Form == nil {
   338  			input.Context.Request.ParseForm()
   339  		}
   340  		input.dataLock.Unlock()
   341  	}
   342  	input.dataLock.RLock()
   343  	defer input.dataLock.RUnlock()
   344  	return input.Context.Request.Form.Get(key)
   345  }
   346  
   347  // Header returns request header item string by a given string.
   348  // if non-existed, return empty string.
   349  func (input *BeegoInput) Header(key string) string {
   350  	return input.Context.Request.Header.Get(key)
   351  }
   352  
   353  // Cookie returns request cookie item string by a given key.
   354  // if non-existed, return empty string.
   355  func (input *BeegoInput) Cookie(key string) string {
   356  	ck, err := input.Context.Request.Cookie(key)
   357  	if err != nil {
   358  		return ""
   359  	}
   360  	return ck.Value
   361  }
   362  
   363  // Session returns current session item value by a given key.
   364  // if non-existed, return nil.
   365  func (input *BeegoInput) Session(key interface{}) interface{} {
   366  	return input.CruSession.Get(key)
   367  }
   368  
   369  // CopyBody returns the raw request body data as bytes.
   370  func (input *BeegoInput) CopyBody(MaxMemory int64) []byte {
   371  	if input.Context.Request.Body == nil {
   372  		return []byte{}
   373  	}
   374  
   375  	var requestbody []byte
   376  	safe := &io.LimitedReader{R: input.Context.Request.Body, N: MaxMemory}
   377  	if input.Header("Content-Encoding") == "gzip" {
   378  		reader, err := gzip.NewReader(safe)
   379  		if err != nil {
   380  			return nil
   381  		}
   382  		requestbody, _ = ioutil.ReadAll(reader)
   383  	} else {
   384  		requestbody, _ = ioutil.ReadAll(safe)
   385  	}
   386  
   387  	input.Context.Request.Body.Close()
   388  	bf := bytes.NewBuffer(requestbody)
   389  	input.Context.Request.Body = http.MaxBytesReader(input.Context.ResponseWriter, ioutil.NopCloser(bf), MaxMemory)
   390  	input.RequestBody = requestbody
   391  	return requestbody
   392  }
   393  
   394  // Data return the implicit data in the input
   395  func (input *BeegoInput) Data() map[interface{}]interface{} {
   396  	input.dataLock.Lock()
   397  	defer input.dataLock.Unlock()
   398  	if input.data == nil {
   399  		input.data = make(map[interface{}]interface{})
   400  	}
   401  	return input.data
   402  }
   403  
   404  // GetData returns the stored data in this context.
   405  func (input *BeegoInput) GetData(key interface{}) interface{} {
   406  	input.dataLock.Lock()
   407  	defer input.dataLock.Unlock()
   408  	if v, ok := input.data[key]; ok {
   409  		return v
   410  	}
   411  	return nil
   412  }
   413  
   414  // SetData stores data with given key in this context.
   415  // This data are only available in this context.
   416  func (input *BeegoInput) SetData(key, val interface{}) {
   417  	input.dataLock.Lock()
   418  	defer input.dataLock.Unlock()
   419  	if input.data == nil {
   420  		input.data = make(map[interface{}]interface{})
   421  	}
   422  	input.data[key] = val
   423  }
   424  
   425  // ParseFormOrMulitForm parseForm or parseMultiForm based on Content-type
   426  func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error {
   427  	// Parse the body depending on the content type.
   428  	if strings.Contains(input.Header("Content-Type"), "multipart/form-data") {
   429  		if err := input.Context.Request.ParseMultipartForm(maxMemory); err != nil {
   430  			return errors.New("Error parsing request body:" + err.Error())
   431  		}
   432  	} else if err := input.Context.Request.ParseForm(); err != nil {
   433  		return errors.New("Error parsing request body:" + err.Error())
   434  	}
   435  	return nil
   436  }
   437  
   438  // Bind data from request.Form[key] to dest
   439  // like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie
   440  // var id int  beegoInput.Bind(&id, "id")  id ==123
   441  // var isok bool  beegoInput.Bind(&isok, "isok")  isok ==true
   442  // var ft float64  beegoInput.Bind(&ft, "ft")  ft ==1.2
   443  // ol := make([]int, 0, 2)  beegoInput.Bind(&ol, "ol")  ol ==[1 2]
   444  // ul := make([]string, 0, 2)  beegoInput.Bind(&ul, "ul")  ul ==[str array]
   445  // user struct{Name}  beegoInput.Bind(&user, "user")  user == {Name:"astaxie"}
   446  func (input *BeegoInput) Bind(dest interface{}, key string) error {
   447  	value := reflect.ValueOf(dest)
   448  	if value.Kind() != reflect.Ptr {
   449  		return errors.New("beego: non-pointer passed to Bind: " + key)
   450  	}
   451  	value = value.Elem()
   452  	if !value.CanSet() {
   453  		return errors.New("beego: non-settable variable passed to Bind: " + key)
   454  	}
   455  	typ := value.Type()
   456  	// Get real type if dest define with interface{}.
   457  	// e.g  var dest interface{} dest=1.0
   458  	if value.Kind() == reflect.Interface {
   459  		typ = value.Elem().Type()
   460  	}
   461  	rv := input.bind(key, typ)
   462  	if !rv.IsValid() {
   463  		return errors.New("beego: reflect value is empty")
   464  	}
   465  	value.Set(rv)
   466  	return nil
   467  }
   468  
   469  func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value {
   470  	if input.Context.Request.Form == nil {
   471  		input.Context.Request.ParseForm()
   472  	}
   473  	rv := reflect.Zero(typ)
   474  	switch typ.Kind() {
   475  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   476  		val := input.Query(key)
   477  		if len(val) == 0 {
   478  			return rv
   479  		}
   480  		rv = input.bindInt(val, typ)
   481  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   482  		val := input.Query(key)
   483  		if len(val) == 0 {
   484  			return rv
   485  		}
   486  		rv = input.bindUint(val, typ)
   487  	case reflect.Float32, reflect.Float64:
   488  		val := input.Query(key)
   489  		if len(val) == 0 {
   490  			return rv
   491  		}
   492  		rv = input.bindFloat(val, typ)
   493  	case reflect.String:
   494  		val := input.Query(key)
   495  		if len(val) == 0 {
   496  			return rv
   497  		}
   498  		rv = input.bindString(val, typ)
   499  	case reflect.Bool:
   500  		val := input.Query(key)
   501  		if len(val) == 0 {
   502  			return rv
   503  		}
   504  		rv = input.bindBool(val, typ)
   505  	case reflect.Slice:
   506  		rv = input.bindSlice(&input.Context.Request.Form, key, typ)
   507  	case reflect.Struct:
   508  		rv = input.bindStruct(&input.Context.Request.Form, key, typ)
   509  	case reflect.Ptr:
   510  		rv = input.bindPoint(key, typ)
   511  	case reflect.Map:
   512  		rv = input.bindMap(&input.Context.Request.Form, key, typ)
   513  	}
   514  	return rv
   515  }
   516  
   517  func (input *BeegoInput) bindValue(val string, typ reflect.Type) reflect.Value {
   518  	rv := reflect.Zero(typ)
   519  	switch typ.Kind() {
   520  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   521  		rv = input.bindInt(val, typ)
   522  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   523  		rv = input.bindUint(val, typ)
   524  	case reflect.Float32, reflect.Float64:
   525  		rv = input.bindFloat(val, typ)
   526  	case reflect.String:
   527  		rv = input.bindString(val, typ)
   528  	case reflect.Bool:
   529  		rv = input.bindBool(val, typ)
   530  	case reflect.Slice:
   531  		rv = input.bindSlice(&url.Values{"": {val}}, "", typ)
   532  	case reflect.Struct:
   533  		rv = input.bindStruct(&url.Values{"": {val}}, "", typ)
   534  	case reflect.Ptr:
   535  		rv = input.bindPoint(val, typ)
   536  	case reflect.Map:
   537  		rv = input.bindMap(&url.Values{"": {val}}, "", typ)
   538  	}
   539  	return rv
   540  }
   541  
   542  func (input *BeegoInput) bindInt(val string, typ reflect.Type) reflect.Value {
   543  	intValue, err := strconv.ParseInt(val, 10, 64)
   544  	if err != nil {
   545  		return reflect.Zero(typ)
   546  	}
   547  	pValue := reflect.New(typ)
   548  	pValue.Elem().SetInt(intValue)
   549  	return pValue.Elem()
   550  }
   551  
   552  func (input *BeegoInput) bindUint(val string, typ reflect.Type) reflect.Value {
   553  	uintValue, err := strconv.ParseUint(val, 10, 64)
   554  	if err != nil {
   555  		return reflect.Zero(typ)
   556  	}
   557  	pValue := reflect.New(typ)
   558  	pValue.Elem().SetUint(uintValue)
   559  	return pValue.Elem()
   560  }
   561  
   562  func (input *BeegoInput) bindFloat(val string, typ reflect.Type) reflect.Value {
   563  	floatValue, err := strconv.ParseFloat(val, 64)
   564  	if err != nil {
   565  		return reflect.Zero(typ)
   566  	}
   567  	pValue := reflect.New(typ)
   568  	pValue.Elem().SetFloat(floatValue)
   569  	return pValue.Elem()
   570  }
   571  
   572  func (input *BeegoInput) bindString(val string, typ reflect.Type) reflect.Value {
   573  	return reflect.ValueOf(val)
   574  }
   575  
   576  func (input *BeegoInput) bindBool(val string, typ reflect.Type) reflect.Value {
   577  	val = strings.TrimSpace(strings.ToLower(val))
   578  	switch val {
   579  	case "true", "on", "1":
   580  		return reflect.ValueOf(true)
   581  	}
   582  	return reflect.ValueOf(false)
   583  }
   584  
   585  type sliceValue struct {
   586  	index int           // Index extracted from brackets.  If -1, no index was provided.
   587  	value reflect.Value // the bound value for this slice element.
   588  }
   589  
   590  func (input *BeegoInput) bindSlice(params *url.Values, key string, typ reflect.Type) reflect.Value {
   591  	maxIndex := -1
   592  	numNoIndex := 0
   593  	sliceValues := []sliceValue{}
   594  	for reqKey, vals := range *params {
   595  		if !strings.HasPrefix(reqKey, key+"[") {
   596  			continue
   597  		}
   598  		// Extract the index, and the index where a sub-key starts. (e.g. field[0].subkey)
   599  		index := -1
   600  		leftBracket, rightBracket := len(key), strings.Index(reqKey[len(key):], "]")+len(key)
   601  		if rightBracket > leftBracket+1 {
   602  			index, _ = strconv.Atoi(reqKey[leftBracket+1 : rightBracket])
   603  		}
   604  		subKeyIndex := rightBracket + 1
   605  
   606  		// Handle the indexed case.
   607  		if index > -1 {
   608  			if index > maxIndex {
   609  				maxIndex = index
   610  			}
   611  			sliceValues = append(sliceValues, sliceValue{
   612  				index: index,
   613  				value: input.bind(reqKey[:subKeyIndex], typ.Elem()),
   614  			})
   615  			continue
   616  		}
   617  
   618  		// It's an un-indexed element.  (e.g. element[])
   619  		numNoIndex += len(vals)
   620  		for _, val := range vals {
   621  			// Unindexed values can only be direct-bound.
   622  			sliceValues = append(sliceValues, sliceValue{
   623  				index: -1,
   624  				value: input.bindValue(val, typ.Elem()),
   625  			})
   626  		}
   627  	}
   628  	resultArray := reflect.MakeSlice(typ, maxIndex+1, maxIndex+1+numNoIndex)
   629  	for _, sv := range sliceValues {
   630  		if sv.index != -1 {
   631  			resultArray.Index(sv.index).Set(sv.value)
   632  		} else {
   633  			resultArray = reflect.Append(resultArray, sv.value)
   634  		}
   635  	}
   636  	return resultArray
   637  }
   638  
   639  func (input *BeegoInput) bindStruct(params *url.Values, key string, typ reflect.Type) reflect.Value {
   640  	result := reflect.New(typ).Elem()
   641  	fieldValues := make(map[string]reflect.Value)
   642  	for reqKey, val := range *params {
   643  		var fieldName string
   644  		if strings.HasPrefix(reqKey, key+".") {
   645  			fieldName = reqKey[len(key)+1:]
   646  		} else if strings.HasPrefix(reqKey, key+"[") && reqKey[len(reqKey)-1] == ']' {
   647  			fieldName = reqKey[len(key)+1 : len(reqKey)-1]
   648  		} else {
   649  			continue
   650  		}
   651  
   652  		if _, ok := fieldValues[fieldName]; !ok {
   653  			// Time to bind this field.  Get it and make sure we can set it.
   654  			fieldValue := result.FieldByName(fieldName)
   655  			if !fieldValue.IsValid() {
   656  				continue
   657  			}
   658  			if !fieldValue.CanSet() {
   659  				continue
   660  			}
   661  			boundVal := input.bindValue(val[0], fieldValue.Type())
   662  			fieldValue.Set(boundVal)
   663  			fieldValues[fieldName] = boundVal
   664  		}
   665  	}
   666  
   667  	return result
   668  }
   669  
   670  func (input *BeegoInput) bindPoint(key string, typ reflect.Type) reflect.Value {
   671  	return input.bind(key, typ.Elem()).Addr()
   672  }
   673  
   674  func (input *BeegoInput) bindMap(params *url.Values, key string, typ reflect.Type) reflect.Value {
   675  	var (
   676  		result    = reflect.MakeMap(typ)
   677  		keyType   = typ.Key()
   678  		valueType = typ.Elem()
   679  	)
   680  	for paramName, values := range *params {
   681  		if !strings.HasPrefix(paramName, key+"[") || paramName[len(paramName)-1] != ']' {
   682  			continue
   683  		}
   684  
   685  		key := paramName[len(key)+1 : len(paramName)-1]
   686  		result.SetMapIndex(input.bindValue(key, keyType), input.bindValue(values[0], valueType))
   687  	}
   688  	return result
   689  }