MVC3 / 4安全&模型绑定动态对象

时间:2011-03-09 20:49:03

标签: asp.net-mvc asp.net-mvc-3 asp.net-mvc-4 modelbinders modelmetadata

2 个答案:

答案 0 :(得分:1)

首先,我建议您使用字典作为viewModel。这允许您添加您喜欢的任何属性(名称/值)。

其次,为了满足安全性要求,我将a)对用户进行身份验证(Forms / Windows),并b)创建一些查询数据库的功能,以查看用户被允许提交/编辑/查看的对象如何。查询的结果可能只是一个包含允许字段名称的字符串数组 - 这是您的元数据。使用此数据,您可以轻松删除未经授权的值。 ModelBinder就是这样做的地方。

第三,对于验证,您可以通过将字符串数组替换为Touple(Of string,bool)列表来扩展METADATA,其中存储一个布尔值,指示该值是否为必需的用户输入。您仍然可以通过实现A MetaDataProvider来依赖ASP.NET MVC默认值。这可能是一个启动者:http://buildstarted.com/2010/09/14/creating-your-own-modelmetadataprovider-to-handle-custom-attributes/

最后,DisplayTemplates和EditorTemplates可以轻松驱动动态UI。为常用数据类型创建模板,为Dictionary创建模板。最后一个只是迭代它的KeyValuePairs写标签并调用具体的数据类型模板。 同样,METADATA可以使用MetaDataProvider扩展并提供给ASP.NET MVC。

- 丹尼尔

答案 1 :(得分:-1)

发布一个接受的答案,因为我找到了我自己需要的解决方案,虽然我还剩下一部分,如果我需要额外的帮助而不是留下这个问题,我会将其作为一个单独的问题发布。有关详细信息,请参阅我的原始帖子。

向下投票是一个很好的提醒,回到这里,所以你在这里: - )

好的,所以这就是我最后的实施方式。希望这可以帮助你们中的一些人了解自己的情况。我必须透露我正在给予 Works on my Machine 批准印章,您应该自己测试一下,看它是否符合您的需求。做出某些决定是为了遵守现有的数据/做法。如果您遇到任何有关此代码的问题,请随时回复此帖子,以便其他人可以受益。为了简洁,我会尝试缩短代码,因为某些细节属于我的雇主。这是它的主旨,因为它与MVC有关。

注意:我目前正在使用MVC4,但这也适用于MVC3。虚拟修饰符适用于nHibernate

<强>波苏斯

public class PseudoObject
{
    // Other properties and such...
    public virtual IList<PseudoObjectAttribute> Attributes { get; set; }
    // Other methods, etc...
}

public class PseudoObjectAttribute
{
    // Other properties and such...
    public virtual string Value { get; set; }

    //This holds all of the info I need for determine metadata & validation
    public virtual PseudoObjectStructure Structure { get; set; }
    // Other methods, etc...
}

public class PseudoObjectStructure
{ 
    public virtual bool IsRequired { get; set; }
    public virtual string RegularExpression { get; set; }
    public virtual string RegularExpressionErrorMessage { get; set; }
}

<强> MetadataProvider

protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
    //We only care about providing custom model metadata to PseudoObjectAttribute objects
    if ((containerType != typeof(PseudoObjectAttribute) && modelType != typeof(PseudoObjectAttribute)) || modelAccessor == null)
        return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

    ModelMetadata metadata = null;
    PseudoObjectAttribute attributeViewModel = null;
    System.Reflection.FieldInfo container = null;

    //The contents of this if statement allows me to get the PseudoObjectAttribute instance I need to work with.

    //This happens when we want metadata for the PseudoObjectAttribute type as a whole, not a specific attribute
    if (modelType == typeof(PseudoObjectAttribute) && containerType == null)
    {
        //
        if (modelAccessor.Target is ViewDataDictionary)
            attributeViewModel = (PseudoObjectAttribute)((ViewDataDictionary)modelAccessor.Target).Model;
        else
        {
            container = modelAccessor.Target.GetType().GetField("item");
            if (container != null)
            {
                attributeViewModel = (PseudoObjectAttribute)container.GetValue(modelAccessor.Target);
            }
            container = modelAccessor.Target.GetType().GetField("model");
            if (container != null)
                attributeViewModel = (PseudoObjectAttribute)container.GetValue(modelAccessor.Target);
        }
    }
    else if(!string.IsNullOrEmpty(propertyName))
    {
        if (modelAccessor.Method.Name.Contains("FromStringExpression"))
        {
            //This happens when we want metadata for a specific property on the PseudoObjectAttribute
            container = modelAccessor.Target.GetType().GetField("vdi");
            attributeViewModel = (PseudoObjectAttribute)((System.Web.Mvc.ViewDataInfo)container.GetValue(modelAccessor.Target)).Container;
        }
            //GetPropertyValueAccessor is used when you bind the posted back form
        else if (modelAccessor.Method.Name.Contains("FromLambdaExpression") || modelAccessor.Method.Name.Contains("GetPropertyValueAccessor"))
        {
            //Accessed property via lambda
            container = modelAccessor.Target.GetType().GetField("container");
            var accessor = container.GetValue(modelAccessor.Target);
            //Sometimes the property is access straight from the parent object for display purposes in the view ex. someRegistration["ProductId"].GuidValue
            //In these situations the access is the Registration object and is not something we can use to derive the attribute.
            if (accessor is PseudoObjectAttribute)
                attributeViewModel = (PseudoObjectAttribute)accessor;
        }
    }

    // At this point I have an instance of the actual PseudoObjectAttribute object I'm trying to derive Metadata for and can build my Metadata easily using it.
    // I'm using typeof (String) as a starting point to build my custom metadata from but it could be any value type if you wanted to befit from the defaults
    metadata = new ModelMetadata(this, typeof (PseudoObjectAttribute), modelAccessor, typeof (String), propertyName);

    // Be sure to store any of the information you've obtained here that is needed to derive validation rules in the AdditionalValues
    metadata.AdditionalValues.Add("Structure", attributeViewModel.Structure);

    //TODO: Populate the rest of the Metadata here....

    return metadata;
}

<强> ValidatorProvider

public class PseudoObjectAttributeValidatorProvider : ModelValidatorProvider
{
    public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
    {

        if (metadata.ContainerType == typeof(PseudoObjectAttribute) && metadata.PropertyName == "StringValue")
        {
            PseudoObjectStructure  structure = null;
            try
            {
                if (metadata.AdditionalValues.Any())
                structure = (PseudoObjectStructure)metadata.AdditionalValues["Structure"];
            }
            catch (KeyNotFoundException) { }

            if (structure != null)
            {
                if (structure.IsRequired)
                    yield return new RequiredAttributeAdapter(metadata, context, new RequiredAttribute());

                if (structure.RegularExpression != null)
                    yield return new RegularExpressionAttributeAdapter(metadata, context, new RegularExpressionAttribute(structure.RegularExpression) { ErrorMessage = structure.RegularExpressionErrorMessage });
            }
        }
        else
            yield break;        
   }
}

认为这就是一切。如果我错过了什么让我知道。