POST json dictionary

后端 未结 10 871
猫巷女王i
猫巷女王i 2020-11-27 05:52

I\'m trying the following : A model with a dictionary inside send it on the first ajax request then take the result serialize it again and send it back to the controller.

10条回答
  •  孤城傲影
    2020-11-27 06:27

    I came across the same issue today and came up with a solution which doesn't require anything but registering a new model binder. It's a bit hacky but hopefully it helps someone.

        public class DictionaryModelBinder : IModelBinder
        {
            /// 
            /// Binds the model to a value by using the specified controller context and binding context.
            /// 
            /// 
            /// The bound value.
            /// 
            /// The controller context.The binding context.
            public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
                if (bindingContext == null)
                    throw new ArgumentNullException("bindingContext");
    
                string modelName = bindingContext.ModelName;
                // Create a dictionary to hold the results
                IDictionary result = new Dictionary();
    
                // The ValueProvider property is of type IValueProvider, but it typically holds an object of type ValueProviderCollect
                // which is a collection of all the registered value providers.
                var providers = bindingContext.ValueProvider as ValueProviderCollection;
                if (providers != null)
                {
                    // The DictionaryValueProvider is the once which contains the json values; unfortunately the ChildActionValueProvider and
                    // RouteDataValueProvider extend DictionaryValueProvider too, so we have to get the provider which contains the 
                    // modelName as a key. 
                    var dictionaryValueProvider = providers
                        .OfType>()
                        .FirstOrDefault(vp => vp.ContainsPrefix(modelName));
                    if (dictionaryValueProvider != null)
                    {
                        // There's no public property for getting the collection of keys in a value provider. There is however
                        // a private field we can access with a bit of reflection.
                        var prefixsFieldInfo = dictionaryValueProvider.GetType().GetField("_prefixes",
                                                                                          BindingFlags.Instance |
                                                                                          BindingFlags.NonPublic);
                        if (prefixsFieldInfo != null)
                        {
                            var prefixes = prefixsFieldInfo.GetValue(dictionaryValueProvider) as HashSet;
                            if (prefixes != null)
                            {
                                // Find all the keys which start with the model name. If the model name is model.DictionaryProperty; 
                                // the keys we're looking for are model.DictionaryProperty.KeyName.
                                var keys = prefixes.Where(p => p.StartsWith(modelName + "."));
                                foreach (var key in keys)
                                {
                                    // With each key, we can extract the value from the value provider. When adding to the dictionary we want to strip
                                    // out the modelName prefix. (+1 for the extra '.')
                                    result.Add(key.Substring(modelName.Length + 1), bindingContext.ValueProvider.GetValue(key).AttemptedValue);
                                }
                                return result;
                            }
                        }
                    }
                }
                return null;
            }
        }
    

    The binder is registered in the Global.asax file under application_start

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
    
            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
    
            ModelBinders.Binders.Add(typeof(Dictionary), new DictionaryModelBinder());
        }
    

提交回复
热议问题