ASP.NET Core MVC-在运行时绑定到模型

时间:2019-04-19 17:17:11

标签: c# asp.net-core asp.net-core-mvc

我有以下视图模型:

public class FormViewModel {
    [Required, StringLength(100)]
    public string Name { get; set; }

    private object _parameters = null;
    public object Parameters {
        get {
            if (_parameters == null)
                _parameters = Activator.CreateInstance(Type.GetType("CustomParameters"));
            return _parameters;
        }
        set {
            _parameters = value;
        }
    }
}

CustomParameters如下所示:

public class CustomParameters {
    [Required]
    public string Text { get; set; }
}

现在,如果我发布以下表单数据:

"Name" => "Foo"
"Parameters.Text" => "Bar"

正确设置了“名称”属性,但是将“ Parameters.Text”属性设置为null。

请注意,以上情况已经简化,并且参数需要支持绑定到多个自定义类型。

编辑-我添加了以下在ASP.NET MVC中使用的代码,但ASP.NET Core的模型绑定似乎已被重写,而我看不到我需要做什么:

public class IRuntimeBindableModelBinder : DefaultModelBinder {
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
        var newBindingContext = new ModelBindingContext() {
            // In the original method you have:
            // ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => bindingContext.Model, typeof(TModel)),
            ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => bindingContext.Model, bindingContext.Model.GetType()),
            ModelName = bindingContext.ModelName,
            ModelState = bindingContext.ModelState,
            PropertyFilter = bindingContext.PropertyFilter,
            ValueProvider = bindingContext.ValueProvider
        };

        return base.BindModel(controllerContext, newBindingContext);
    }
}

如果有人可以提供帮助,我将不胜感激。

谢谢

1 个答案:

答案 0 :(得分:1)

这可以通过自定义ModelBinder完成。这里的问题是.NET不知道哪种类型存储在对象Property中,因此默认情况下为null。

您需要了解目标Type(通过Name或其他Type属性),然后可以像下面那样创建ModelBinder:

public class MyModelBinder : IModelBinder
{
    private readonly IModelMetadataProvider _modelMetadataProvider;
    private readonly IModelBinderFactory _modelBinderFactory;

    public MyModelBinder(IModelMetadataProvider modelMetadataProvider, IModelBinderFactory modelBinderFactory)
    {
        _modelMetadataProvider = modelMetadataProvider;
        _modelBinderFactory = modelBinderFactory;
    }

    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        var typeValue = bindingContext.ValueProvider.GetValue(nameof(ComplexModel.Type)).Values;
        var nameValue = bindingContext.ValueProvider.GetValue(nameof(ComplexModel.Name)).Values;
        var finalModel = new ComplexModel
        {
            Name = nameValue,
            Type = typeValue
        };
        var innerType = LookupType(typeValue);
        if (innerType != null)
        {
            finalModel.Parameters = Activator.CreateInstance(innerType);
            var modelMetadata = _modelMetadataProvider.GetMetadataForType(innerType);
            var modelBinder = _modelBinderFactory.CreateBinder(new ModelBinderFactoryContext
            {
                Metadata = modelMetadata,
                CacheToken = modelMetadata
            });

            var modelName = bindingContext.BinderModelName == null ? "Parameters" : $"{bindingContext.BinderModelName}.Parameters";

            using (var scope = bindingContext.EnterNestedScope(modelMetadata, modelName, modelName, finalModel.Parameters))
            {
                await modelBinder.BindModelAsync(bindingContext);
            }
        }

        bindingContext.Result = ModelBindingResult.Success(finalModel);
        return;
    }

    //NOTE: this maps a type string to a Type.  
    //DO NOT transmit a type FullName and use reflection to activate, this could cause a RCE vulnerability.
    private Type LookupType(string type)
    {
        switch (type)
        {
            case "text":
                return typeof(TextParam);

            case "int":
                return typeof(IntParam);
        }
        return null;
    }
}

//Sample of ComplexModel classes
[ModelBinder(typeof(MyModelBinder))]
public class ComplexModel
{
    public string Name { get; set; }

    public string Type { get; set; }

    public object Parameters { get; set; }
}

public class TextParam
{
    public string Text { get; set; }
}

public class IntParam
{
    public int Number { get; set; }
}
  

注意:使用类型进行自定义反序列化时,限制要反序列化的允许类型的列表很重要。如果您接受类型的FullName并使用反射激活,则可能会导致RCE漏洞,因为.NET中有一些types会在设置属性时执行代码。