Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# ASP.NET核心MVC-运行时绑定到模型_C#_Asp.net Core_Asp.net Core Mvc - Fatal编程技术网

C# ASP.NET核心MVC-运行时绑定到模型

C# ASP.NET核心MVC-运行时绑定到模型,c#,asp.net-core,asp.net-core-mvc,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) _p

我有以下视图模型:

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"
“Name”属性设置正确,但“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);
    }
}
如果有人能帮忙,我将不胜感激


谢谢

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

您需要知道目标类型(通过名称或其他类型属性),然后可以创建如下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; }
}
注意:对类型执行自定义反序列化时,限制要反序列化的允许类型列表非常重要。如果您接受类型的全名并使用反射来激活,这可能会导致RCE漏洞,因为.NET中有一些在设置属性时执行代码


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

您需要知道目标类型(通过名称或其他类型属性),然后可以创建如下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; }
}
注意:对类型执行自定义反序列化时,限制要反序列化的允许类型列表非常重要。如果您接受类型的全名并使用反射来激活,这可能会导致RCE漏洞,因为.NET中有一些在设置属性时执行代码


我看不出有这个必要?是
CustomParameters
通用,还是您事先不知道传入字段?我在上面的示例中硬编码了类型。但是参数类型可以更改。例如,假设我正在发回一个小部件的参数,其参数根据小部件的类型而变化。在传入的请求中是否有任何元数据可以告诉您它是哪个模型?意思是,你是怎么想出来的?反射?我实际上在名称和参数旁边发布了parameters类型。当用户选择适当的小部件类型时,会发送此消息。它还执行ajax调用,用适当的参数类填充视图。另外值得注意的是,在ASP.NET MVC中,我使parameters类实现了接口IRuntimeBindable(在上面的示例中,这将是“CustomParameters”类,它没有成员)。但是,我愿意将其更改为对属性使用属性或其他想法。我认为您可以通过实现自定义模型绑定器来实现这一点。。我正在做一些测试。。让我们看看:)我看不出这有什么必要?是
CustomParameters
通用,还是您事先不知道传入字段?我在上面的示例中硬编码了类型。但是参数类型可以更改。例如,假设我正在发回一个小部件的参数,其参数根据小部件的类型而变化。在传入的请求中是否有任何元数据可以告诉您它是哪个模型?意思是,你是怎么想出来的?反射?我实际上在名称和参数旁边发布了parameters类型。当用户选择适当的小部件类型时,会发送此消息。它还执行ajax调用,用适当的参数类填充视图。另外值得注意的是,在ASP.NET MVC中,我使parameters类实现了接口IRuntimeBindable(在上面的示例中,这将是“CustomParameters”类,它没有成员)。但是,我愿意将其更改为对属性使用属性或其他想法。我认为您可以通过实现自定义模型绑定器来实现这一点。。我正在做一些测试。。让我想想:)谢谢,我明天早上第一件事就查一下。请注意,安全性不是问题,因为它托管在专用服务器上,只有管理员才能访问它。如果安全性不是问题,请您提供另一种选择。谢谢,我不推荐。如果出现这样的错误,那么每个人都有可能访问该服务器,该服务器有权使用ModelBinder访问您的控制器。-但是,这里有一个使用反射激活类型的示例:在这个exmaple中有一个限制,即它只查找基于基类型的类型,但是如果您确实需要它,您可以轻松地更改它。(注意:仅适用于具有无参数构造函数的类)我刚刚有机会测试了这个,它工作得非常好,谢谢。我正在查看其他一些绑定器的源代码,我感觉bindingContext.EnterNestedScope将发挥作用。再次感谢。谢谢,我明天早上第一件事就查一下。请注意,安全性不是问题,因为它托管在专用服务器上,只有管理员才能访问它。如果安全性不是问题,请您提供另一种选择。谢谢,我不推荐。如果出现这样的错误,那么每个人都有可能访问该服务器,该服务器有权使用ModelBinder访问您的控制器。-但是,这里有一个使用反射激活类型的示例:在这个exmaple中有一个限制,即它只查找基于基类型的类型,但是如果您确实需要它,您可以轻松地更改它。(注意:仅适用于具有无参数构造函数的类)我刚刚有机会测试了这个,它工作得非常好,谢谢。我正在查看其他一些绑定器的源代码,我感觉bindingContext.EnterNestedScope将发挥作用。再次感谢。