动态MVC模型服务器端验证

时间:2017-03-23 15:26:17

标签: c# asp.net-mvc dynamic

我有以下观点:

@model dynamic
<form class="form-horizontal" id="formDynamicItem" action="/DynamicItem/SaveItem" method="post">

    @Html.AntiForgeryToken()


    <div class="col-xs-12 buttonBar">
        <button type="submit" value="Save" name="submitButton" class="btn">Save</button>

        <button type="submit" value="Cancel" name="submitButton" class="btn">Cancel</button>
    </div>

    <div class="col-lg-6">
        <div class="ibox ">
            <div class="ibox-content">
                @{
                    foreach (var obj in Model)
                    {
                        var kvpObj = (KeyValuePair<string, object>)obj;
                        var entityProp = (EntityAttributeProperties)kvpObj.Value;
                        <div class="form-group">
                            @if (entityProp.IsHiddenField)
                            {
                                <input type="hidden" class="form-control" data-val="true" id="@kvpObj.Key" name="@kvpObj.Key" value="@entityProp.Value" />
                            }
                            else if (entityProp.IsFormField)
                            {
                                var isReadOnly = entityProp.IsReadonly ? "readonly" : "";
                                IHtmlString validationRules = Html.Raw(string.Empty);
                                if (entityProp.ValidationRules != null)
                                {
                                    validationRules = entityProp.ValidationRules;
                                }

                                @Html.Label(entityProp.Name, new { @class = labelClass })
                                <div class="@controlClass">
                                    @switch (@entityProp.Type)
                                    {
                                        //... many cases


                                        default:
                                            <input type="text" class="form-control" id="@kvpObj.Key" name="@kvpObj.Key" value="@entityProp.Value" @isReadOnly @validationRules />
                                            break;
                                    }
                                </div>
                            }
                        </div>
                    }
                }
            </div>
        </div>
    </div>
</form>




@section Scripts {
    <script>
        $("#formDynamicItem").validate();
    </script>
}

在控制器中,我使用FormCollection获取我的值:

public ActionResult SaveItem(FormCollection form)
        {
         ...
         newValue = typeConverter.ConvertFromString(form[entityAttribute.Name]);
         ...                                                   
         }    
   }

我的问题如下:

如何在此类动态模型上建立服务器端验证?我能以某种方式使用FormCollection吗?可能以某种方式构建动态视图模型?如果有人有这方面的经验,请考虑给出一个建议(答案)。

更新:使用ViewModel制作详细信息页面,其中包含动态模型

所以,经过多次重构后,我似乎再次陷入服务器端验证:

所以现在我有了这个ViewModel:

public class DynamicItemViewModel
{
    public Guid Id { get; set; }
    public List<EntityAttributeProperties> Properties { get; set; }
}

此详细信息页面:

@model ExactDistillation.Models.DynamicItem.DynamicItemViewModel
<div class="wrapper wrapper-content animated fadeInRight">
    <div class="row">
        @using (Html.BeginForm("SaveItem", "DynamicItem", FormMethod.Post, new { @class = "form-horizontal", @id = "formDynamicItem" }))
        {
            @Html.AntiForgeryToken()

            @Html.HiddenFor(x => x.Id)
            <div class="col-xs-12 buttonBar">
                <button type="submit" value="Save" name="submitButton" class="btn btn-primary pull-right">Save</button>

                <button type="submit" value="Cancel" name="submitButton" class="btn btn-default pull-right cancel">Cancel</button>
            </div>

            <div class="col-lg-6">
                <div class="ibox float-e-margins">
                    <div class="ibox-title text-webwonders">
                        <h5>Item</h5>
                    </div>
                    <div class="ibox-content">
                        @{
                            for (int i = 0; i < Model.Properties.Count; i++)
                            {
                                @Html.EditorFor(m => Model.Properties[i], "EntityAttributeProperties", "Properties[" + i + "]")
                            }
                        }
                    </div>
                </div>
            </div>
          }
    </div>
</div>

这就是我定义EntityAttributeProperties页面的方式:

@model EntityAttributeProperties

<div class="form-group">
    @if (Model.IsHiddenField)
    {
        @Html.HiddenFor(x => x.Value)
    }
    else if (Model.IsFormField)
    {
        @Html.Label(Model.Name, new { @class = "col-sm-5 col-md-4 col-lg-3" })

        <div class="col-sm-7 col-md-8 col-lg-9">
            @switch (Model.Type)
            {
                --- Many cases

                default:
                    @Html.DynamicTextBoxFor(m => m.Value, null, Model.IsReadonly, Model.ValidationRules)
                    break;

            }
        </div>
    }
</div>

EntityAttributesProperties看起来如下:

public class EntityAttributeProperties
{
    public string Name { get; set; }
    public string DisplayName { get; set; }
    public object Value { get; set; }
    public EntityAttributeDataTypeEnum Type { get; set; }
    public short Order { get; set; }
    public bool IsFormField { get; set; }
    public bool IsReadonly { get; set; }
    public bool IsHiddenField { get; set; }
    public Dictionary<string,object> ValidationRules { get; set; }
}

所以,我正在尝试为Model进行服务器端验证,但是我被困住了,因为我找不到任何优雅的解决方案来解决我的问题,只是我需要进行大量硬编码的解决方案(我不喜欢)。

以下是我收到表格提交的方式:

 public ActionResult SaveItem(DynamicItemViewModel model)
        {
            List<EntityAttributeExtendedView> entityAttributes = GetItemEntityAttributes();
            DataObject dataObject = _dbContext.DataObjectCollection.FirstOrDefault(x => x.Id == model.Id);

            if (dataObject != null)
            {
                JObject json = JObject.Parse(dataObject.Value);
                dynamic dynJson = JsonConvert.DeserializeObject(dataObject.Value);

                // Possibly loop through all entity attributes and separately make a custom validation ?
                // Or somehow create custom Model State for validation ?

            }
            return View("Detail", model);


        }

对于如何通过服务器端验证解决问题,我将不胜感激。

感谢。

2 个答案:

答案 0 :(得分:2)

FormCollection是一种非常原始的数据形式,无法轻松验证。我建议您重构视图以便能够使用ViewModel,否则您将很难处理数据。

我无法向您展示完整的方式,但会给您一些提示:

  1. 为View创建一个View Model类,其中包含Items(类型为EntityAttributeProperties)的列表。我们称之为MainViewModel
  2. 将此视图模型传递给视图,而不是字典
  3. 在您的视图中,使用@Html.EditoFor(x => x.Items)生成正确的HTML。 ASP.NET MVC将使用EntityAttributeProperties类型
  4. 的编辑器模板
  5. 这是在View文件夹EntityAttributeProperties.cshtml子文件夹中创建新视图EditorTemplates的好时机
  6. 在此项目视图中,您可以执行所有entityProp.Type次切换,但请注意ID生成,始终使用@Html.IdFor(...)等,而不是生成自己的ID以保持视图模型的类型安全< / LI>
  7. 经过一些调整后,您的帖子操作应该能够接收类型MainViewModel的视图模型。如果一切顺利,即使您使用不同的控件(隐藏字段,文本字段,下拉菜单......)来填充值
  8. ,项目也将被填充。

    从我的角度来看,只有这种MVC安全方法才能在这种情况下取​​得成功

答案 1 :(得分:1)

我正在添加另一个答案,因为问题发生了重大变化。

一个好方法是使用IValidatableObject接口。因此,您将此界面添加到EntityAttributeProperties类,并且必须覆盖方法Validate。对于像必填字段这样的简单验证,您可以使用所谓的验证属性。

您的EntityAttributeProperties课程将按照以下方式进行装饰:

public class EntityAttributeProperties : IValidatableObject
{
    public string Name { get; set; }

    [Required]
    public object Value { get; set; }

    ...

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var results = new List<ValidationResult>();
        if (... /* some condition, e.g. specific EntityAttributeDataTypeEnum */)
        {
            // Do some validation

            // some other random test
            if (.../* something not right... */)
            {
                results.Add(new ValidationResult("your input was not valid!"));
            }
        }

        return results;
    }
}

可能还需要让你的DynamicItemViewModelIValidatableObject循环遍历这些项目,但有时MVC足够智能自动验证子项目,所以你< em>可能需要这个:

 public class DynamicItemViewModel : IValidatableObject
{
    ...

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        return Items.SelectMany(x => x.Validate(validationContext));
    }
}

现在在你的控制器中,你基本上需要检查你的ModelState。自动生成的ModelState属性包含所有错误。