mirror of
				https://github.com/labstack/echo.git
				synced 2025-10-30 23:57:38 +02:00 
			
		
		
		
	binder: make binding to Map work better with string destinations (#2554)
This commit is contained in:
		
							
								
								
									
										22
									
								
								bind.go
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								bind.go
									
									
									
									
									
								
							| @@ -131,10 +131,26 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri | |||||||
| 	typ := reflect.TypeOf(destination).Elem() | 	typ := reflect.TypeOf(destination).Elem() | ||||||
| 	val := reflect.ValueOf(destination).Elem() | 	val := reflect.ValueOf(destination).Elem() | ||||||
|  |  | ||||||
| 	// Map | 	// Support binding to limited Map destinations: | ||||||
| 	if typ.Kind() == reflect.Map { | 	// - map[string][]string, | ||||||
|  | 	// - map[string]string <-- (binds first value from data slice) | ||||||
|  | 	// - map[string]interface{} | ||||||
|  | 	// You are better off binding to struct but there are user who want this map feature. Source of data for these cases are: | ||||||
|  | 	// params,query,header,form as these sources produce string values, most of the time slice of strings, actually. | ||||||
|  | 	if typ.Kind() == reflect.Map && typ.Key().Kind() == reflect.String { | ||||||
|  | 		k := typ.Elem().Kind() | ||||||
|  | 		isElemInterface := k == reflect.Interface | ||||||
|  | 		isElemString := k == reflect.String | ||||||
|  | 		isElemSliceOfStrings := k == reflect.Slice && typ.Elem().Elem().Kind() == reflect.String | ||||||
|  | 		if !(isElemSliceOfStrings || isElemString || isElemInterface) { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
| 		for k, v := range data { | 		for k, v := range data { | ||||||
| 			val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0])) | 			if isElemString { | ||||||
|  | 				val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0])) | ||||||
|  | 			} else { | ||||||
|  | 				val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v)) | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										56
									
								
								bind_test.go
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								bind_test.go
									
									
									
									
									
								
							| @@ -429,6 +429,62 @@ func TestBindUnsupportedMediaType(t *testing.T) { | |||||||
| 	testBindError(t, strings.NewReader(invalidContent), MIMEApplicationJSON, &json.SyntaxError{}) | 	testBindError(t, strings.NewReader(invalidContent), MIMEApplicationJSON, &json.SyntaxError{}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestDefaultBinder_bindDataToMap(t *testing.T) { | ||||||
|  | 	exampleData := map[string][]string{ | ||||||
|  | 		"multiple": {"1", "2"}, | ||||||
|  | 		"single":   {"3"}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	t.Run("ok, bind to map[string]string", func(t *testing.T) { | ||||||
|  | 		dest := map[string]string{} | ||||||
|  | 		assert.NoError(t, new(DefaultBinder).bindData(&dest, exampleData, "param")) | ||||||
|  | 		assert.Equal(t, | ||||||
|  | 			map[string]string{ | ||||||
|  | 				"multiple": "1", | ||||||
|  | 				"single":   "3", | ||||||
|  | 			}, | ||||||
|  | 			dest, | ||||||
|  | 		) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("ok, bind to map[string][]string", func(t *testing.T) { | ||||||
|  | 		dest := map[string][]string{} | ||||||
|  | 		assert.NoError(t, new(DefaultBinder).bindData(&dest, exampleData, "param")) | ||||||
|  | 		assert.Equal(t, | ||||||
|  | 			map[string][]string{ | ||||||
|  | 				"multiple": {"1", "2"}, | ||||||
|  | 				"single":   {"3"}, | ||||||
|  | 			}, | ||||||
|  | 			dest, | ||||||
|  | 		) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("ok, bind to map[string]interface", func(t *testing.T) { | ||||||
|  | 		dest := map[string]interface{}{} | ||||||
|  | 		assert.NoError(t, new(DefaultBinder).bindData(&dest, exampleData, "param")) | ||||||
|  | 		assert.Equal(t, | ||||||
|  | 			map[string]interface{}{ | ||||||
|  | 				"multiple": []string{"1", "2"}, | ||||||
|  | 				"single":   []string{"3"}, | ||||||
|  | 			}, | ||||||
|  | 			dest, | ||||||
|  | 		) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("ok, bind to map[string]int skips", func(t *testing.T) { | ||||||
|  | 		dest := map[string]int{} | ||||||
|  | 		assert.NoError(t, new(DefaultBinder).bindData(&dest, exampleData, "param")) | ||||||
|  | 		assert.Equal(t, map[string]int{}, dest) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("ok, bind to map[string][]int skips", func(t *testing.T) { | ||||||
|  | 		dest := map[string][]int{} | ||||||
|  | 		assert.NoError(t, new(DefaultBinder).bindData(&dest, exampleData, "param")) | ||||||
|  | 		assert.Equal(t, map[string][]int{}, dest) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestBindbindData(t *testing.T) { | func TestBindbindData(t *testing.T) { | ||||||
| 	ts := new(bindTestStruct) | 	ts := new(bindTestStruct) | ||||||
| 	b := new(DefaultBinder) | 	b := new(DefaultBinder) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user