模型绑定逗号分隔查询字符串参数

Tx3*_*Tx3 25 model-binding asp.net-mvc-3

如何绑定逗号分隔值的查询字符串参数

http://localhost/Action?ids=4783,5063,5305
Run Code Online (Sandbox Code Playgroud)

一个控制器动作期待一个列表?

public ActionResult Action(List<long> ids)
{
    return View();
}
Run Code Online (Sandbox Code Playgroud)

注意! ids在控制器动作中必须有一个列表(或基于IEnumerable的东西),所以string ids不被接受作为答案,因为这些参数被传递给许多动作并且将字符串解析为数组会增加不需要的噪声.

zvo*_*kov 34

这是我在archil的答案中使用的Nathan Taylor解决方案的改进版本.

  1. Nathan的绑定器只能绑定复杂模型的子属性,而我的绑定器也可以绑定单个控制器参数.
  2. 我的绑定器还通过返回数组或IEnumerable的实际空实例,为您提供正确的空参数处理.

要将其连接起来,您可以将其附加到单个Controller参数:

[ModelBinder(typeof(CommaSeparatedModelBinder))]
Run Code Online (Sandbox Code Playgroud)

...或者将其设置为global.asax.cs中Application_Start中的全局默认绑定器:

ModelBinders.Binders.DefaultBinder = new CommaSeparatedModelBinder();
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,它将尝试处理所有IEnumerables并回退到其他所有的ASP.NET MVC标准实现.

看吧:

public class CommaSeparatedModelBinder : DefaultModelBinder
{
    private static readonly MethodInfo ToArrayMethod = typeof(Enumerable).GetMethod("ToArray");

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        return BindCsv(bindingContext.ModelType, bindingContext.ModelName, bindingContext)
                ?? base.BindModel(controllerContext, bindingContext);
    }

    protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
    {
        return BindCsv(propertyDescriptor.PropertyType, propertyDescriptor.Name, bindingContext)
                ?? base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
    }

    private object BindCsv(Type type, string name, ModelBindingContext bindingContext)
    {
        if (type.GetInterface(typeof(IEnumerable).Name) != null)
        {
            var actualValue = bindingContext.ValueProvider.GetValue(name);

            if (actualValue != null)
            {
                var valueType = type.GetElementType() ?? type.GetGenericArguments().FirstOrDefault();

                if (valueType != null && valueType.GetInterface(typeof(IConvertible).Name) != null)
                {
                    var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(valueType));

                    foreach (var splitValue in actualValue.AttemptedValue.Split(new[] { ',' }))
                    {
                            if(!String.IsNullOrWhiteSpace(splitValue))
                                list.Add(Convert.ChangeType(splitValue, valueType));
                    }

                    if (type.IsArray)
                        return ToArrayMethod.MakeGenericMethod(valueType).Invoke(this, new[] { list });
                    else
                        return list;
                }
            }
        }

        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)


arc*_*hil 22

默认模型绑定器期望简单类型列表采用格式

name=value&name=value2&name=value3
Run Code Online (Sandbox Code Playgroud)

要使用内置绑定,您应该将查询字符串更改为

Action?ids=4783&ids=5063&ids=5305
Run Code Online (Sandbox Code Playgroud)

或创建自定义模型绑定器.您可以查看以下文章(来自那里的代码)

public class CommaSeparatedValuesModelBinder : DefaultModelBinder
{
    private static readonly MethodInfo ToArrayMethod = typeof(Enumerable).GetMethod("ToArray");

    protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
    {
        if (propertyDescriptor.PropertyType.GetInterface(typeof(IEnumerable).Name) != null)
        {
            var actualValue = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);

            if (actualValue != null && !String.IsNullOrWhiteSpace(actualValue.AttemptedValue) && actualValue.AttemptedValue.Contains(","))
            {
                var valueType = propertyDescriptor.PropertyType.GetElementType() ?? propertyDescriptor.PropertyType.GetGenericArguments().FirstOrDefault();

                if (valueType != null && valueType.GetInterface(typeof(IConvertible).Name) != null)
                {
                    var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(valueType));

                    foreach (var splitValue in actualValue.AttemptedValue.Split(new[] { ',' }))
                    {
                        list.Add(Convert.ChangeType(splitValue, valueType));
                    }

                    if (propertyDescriptor.PropertyType.IsArray)
                    {
                        return ToArrayMethod.MakeGenericMethod(valueType).Invoke(this, new[] { list });
                    }
                    else
                    {
                        return list;
                    }
                }
            }
        }

        return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
    }
}
Run Code Online (Sandbox Code Playgroud)


Moh*_*din 9

摘自我的回答

我将在这里向您展示我刚刚编写的一个非常简单的自定义模型绑定器(并在.Net Core 2.0 中测试):

我的模型活页夹:

public class CustomModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        var value = valueProviderResult.FirstValue; // get the value as string

        var model = value?.Split(",");
        bindingContext.Result = ModelBindingResult.Success(model);

        return Task.CompletedTask;
    }
}
Run Code Online (Sandbox Code Playgroud)

我的模型(请注意,只有一个属性具有我的自定义模型绑定器注释):

public class CreatePostViewModel
{
    [Display(Name = nameof(ContentText))]
    [MinLength(10, ErrorMessage = ValidationErrors.MinLength)]
    public string ContentText { get; set; }

    [BindProperty(BinderType = typeof(CustomModelBinder))]
    public IEnumerable<string> Categories { get; set; } // <<<<<< THIS IS WHAT YOU ARE INTERESTER IN

    #region View Data
    public string PageTitle { get; set; }
    public string TitlePlaceHolder { get; set; }
    #endregion
}
Run Code Online (Sandbox Code Playgroud)

它的作用是:它接收一些像“aaa,bbb,ccc”这样的文本,并将其转换为数组,并将其返回给 ViewModel。

我希望这有帮助。

免责声明:我不是模型活页夹写作方面的专家,我在 15 分钟前了解到这一点,我发现了你的问题(没有有用的答案),所以我试图提供帮助。这是一个非常基本的模型绑定器,肯定需要一些改进。我从官方文档页面学习了如何编写它。


Tx3*_*Tx3 2

Archils 的回答给出了一些如何实现我自己的模型绑定器的想法。我能够稍微简化源代码,因为不需要非常通用的 CSV 支持。我没有将接收到的数据设置为,而是List<int>将其放入班级。

模型活页夹

public class FarmModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType == typeof(FarmModel))
        {
            var newBindingContext = new ModelBindingContext()
            {
                ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(
                () => CreateFarmModel(controllerContext, bindingContext),
                typeof(FarmModel)
                ),
                ModelState = bindingContext.ModelState,
                ValueProvider = bindingContext.ValueProvider
            };

            return base.BindModel(controllerContext, newBindingContext);
        }

        return base.BindModel(controllerContext, bindingContext);
    }

    private FarmModel CreateFarmModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var farmsIds = new List<int>();

        var value = bindingContext.ValueProvider.GetValue("farmData");
        if(value != null && value.AttemptedValue != null)
        {
            var array = value.AttemptedValue.Split(new [] {','});
            foreach (var s in array)
            {
                int result;
                if(int.TryParse(s, out result))
                {
                    farmsIds.Add(result);
                }
            }
        }
        return new FarmModel() { FarmIds = farmsIds };
    }
}
Run Code Online (Sandbox Code Playgroud)

模型

public class FarmModel
{
    public IEnumerable<int> FarmIds { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

添加自定义活页夹

System.Web.Mvc.ModelBinders.Binders.Add(typeof(FarmModel), new FarmModelBinder());
Run Code Online (Sandbox Code Playgroud)