asp.net mvc模型状态错误键

时间:2016-08-09 20:05:32

标签: asp.net-mvc data-annotations modelstate

所以我发现了一个有趣的问题。 我有这样的模型:

public class ApplicantModel
{
    [Display(Name = "Firstname", ResourceType = typeof(Resources))]
    [MaxLength(50, ErrorMessageResourceName = "FirstName", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
    [Required(ErrorMessageResourceName = "FirstName", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
    public string Firstname { get; set; }

    [Display(Name = "Surname", ResourceType = typeof(Resources))]
    [MaxLength(50, ErrorMessageResourceName = "Surname", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
    [Required(ErrorMessageResourceName = "Surname", ErrorMessageResourceType = typeof(Validations), ErrorMessage = null)]
    public string Surname { get; set; }
}

这一切都很好,当我检查模型状态并且模型上有错误时,我得到这样的结果: 错误:

[{
Key = FirstApplicant.Firstname
Value = ["First name is required field"]
},
{
Key = FirstApplicant.Surname
Value = ["Surname name is required field"]
}].

那也没关系。

修改 这是可视化为JSON对象的c#ModelState对象。真实对象看起来像这样:

ModelState
{System.Web.Mvc.ModelStateDictionary}
    Count: 2
    IsReadOnly: false
    IsValid: false
    Keys: Count = 2
    Values: Count = 2
    Results View: Expanding the Results View will enumerate the IEnumerable

但我的问题是。有可能以某种方式改变密钥吗?我知道密钥是作为对象的名称创建的,然后是该对象上的name属性。 所以它是有道理的,但有没有办法如何改变这种默认行为?或者我是否必须更改对象的名称?

EDIT2:

我想在这里实现的是我有一个c#ViewModel和淘汰ViewModel。当你进行服务器端验证时,你会获得我序列化并发送给客户端的密钥和值字典。 然后我在客户端上调用此函数:

var errors = @Html.Raw(Json.Encode(Model.Errors));
        function showErrors(serializedErrors) {
            var errors = JSON.parse(serializedErrors);
            for (var i = 0; i < errors.length; i++) {
                var error = errors[i];
                var key = error.Key;
                var property = eval("masterModel." + key);
                property.setError(error.Value.ErrorMessage);
                property.isModified(true);
            }
        }
        showErrors(errors);

如果视图模型属性名称在服务器和客户端上匹配,这将正常工作。但是,例如在服务器端,我有一个FirstApplicant.FirstName,在客户端,它是ApplicantOne.firstname。谢谢大家的帮助和评论。我希望这次能更详细地解释我的问题。

1 个答案:

答案 0 :(得分:0)

最后我找到了解决这个问题的方法。它有点复杂但有效。

首先我创建了一个属性。

public class ClientNameAttribute : Attribute, IMetadataAware
{
    public ClientNameAttribute(string name)
    {
        this.Name = name;
    }

    public string Name { get; set; }

    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.AdditionalValues["ClientName"] = this.Name;
    }
}

请注意,此属性还实现了IMetadataAware

下一步是创建Html帮助器,所以我可以在视图中调用它。

public static class HtmlHelperExtensions
{
    public static string CustomModelState<T>(this HtmlHelper<T> helper)
    {
        var errors = helper.ViewData.ModelState.Select(
            m => new { Key = GenerateClientName(m.Key, helper), Value = m.Value.Errors.FirstOrDefault() }).Where(e=> e.Value != null);

        return Json.Encode(errors);
    }

    private static string GenerateClientName<T>(string key, HtmlHelper<T> helper)
    {
        StringBuilder builder = new StringBuilder();
        int periodIndex = -1;
        do
        {
            periodIndex = key.IndexOf('.', periodIndex + 1);
            string part = key.Substring(0, periodIndex==-1 ? key.Length : periodIndex);
            var partMetadata = ModelMetadata.FromStringExpression(part, helper.ViewData);

            object clientName;
            if (builder.Length > 0)
            {
                builder.Append('.');
            }

            if (partMetadata.AdditionalValues.TryGetValue("ClientName", out clientName))
            {
                builder.Append(clientName);
            }
            else
            {
                builder.Append(partMetadata.PropertyName);
            }
        }
        while (periodIndex != -1);

        return builder.ToString();
    }
}

CustomModelState是我在视图中调用的方法。

像这样:

var errors = @Html.Raw(Html.CustomModelState());
if (errors.length > 0) {
    showErrors("masterModel",errors);
}

这将为您提供很好的格式错误,以及您的自定义属性名称。

以下是对它的测试:

public class TestModel
{
    [Required]
    public string Normal { get; set; }
    [ClientName("Other")]
    [Required]
    public string Changed { get; set; }

    [ClientName("Complicated")]
    public TestModelTwo TestModelTwo { get; set; }
}

public class TestModelTwo
{
    public string PropertyOne { get; set; }

    [ClientName("Two")]
    public string PropertyTwo{ get; set; }
}

[TestClass]
public class HtmlHelperExtensionsTests
{
    [TestMethod]
    public void CustomModelStateTests()
    {
        var model = new TestModel();
        var page = new ViewPage();
        page.ViewData.Model = model;
        page.ViewData.ModelState.AddModelError("Normal", "Error1");
        page.ViewData.ModelState.AddModelError("Changed", "Error2");
        HtmlHelper<TestModel> helper = new HtmlHelper<TestModel>(new ViewContext(), page);
        var custom = helper.CustomModelState();
        string expectedResult =
            "[{\"Key\":\"Normal\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error1\"}},{\"Key\":\"Other\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error2\"}}]";
        Assert.AreEqual(expectedResult, custom);
    }


    [TestMethod]
    public void CustomModelStateTests_ObjectProperty_With_ClientName()
    {
        var model = new TestModel();
        model.TestModelTwo = new TestModelTwo();
        var page = new ViewPage();
        page.ViewData.Model = model;
        page.ViewData.ModelState.AddModelError("TestModelTwo.PropertyOne", "Error1");
        page.ViewData.ModelState.AddModelError("TestModelTwo.PropertyTwo", "Error2");
        HtmlHelper<TestModel> helper = new HtmlHelper<TestModel>(new ViewContext(), page);
        var custom = helper.CustomModelState();
        string expectedResult =
            "[{\"Key\":\"Complicated.PropertyOne\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error1\"}},{\"Key\":\"Complicated.Two\",\"Value\":{\"Exception\":null,\"ErrorMessage\":\"Error2\"}}]";
        Assert.AreEqual(expectedResult, custom);
    }
}