接收一组json对象并在控制器中进行验证

时间:2014-03-27 13:41:55

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

我发送的是这样的JSON:

[
{col1: 'value', col2: 'value'}, 
{col1: 'value2', col2: 'value2'},
...
]

我的控制器中的操作有一个List参数,需要自定义模型绑定器,如下所示:

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var contentType = controllerContext.HttpContext.Request.ContentType;

            String bodyText;

            Stream stream = null;
            try
            {
                stream = controllerContext.HttpContext.Request.InputStream;
                stream.Seek(0, SeekOrigin.Begin);
                using (var reader = new StreamReader(stream))
                {
                    stream = null;
                    bodyText = reader.ReadToEnd();
                }
            }
            finally
            {
                if (stream != null)
                    stream.Dispose();
            }

            if (string.IsNullOrEmpty(bodyText))
            {
                return null;
            }

            var model = new JavaScriptSerializer().Deserialize<T>(bodyText);
            return model;

            // return base.BindModel(controllerContext, bindingContext);
        }

它正在工作,除了它没有考虑验证的数据注释(必需,范围等)。

如何使用验证?

更新

控制器操作

[HttpPost]
        public ActionResult ActionName([ModelBinder(typeof(JsonArrayValidationModelBinder<List<EntityName>>))]List<EntityName> viewModel)

实体

public class EntityName
{
        [Display(Name = "Data Entrada")]
        [DataType(DataType.Date)]
        [Required]
        public DateTime? DataEntrada { get; set; }
// ....
}

2 个答案:

答案 0 :(得分:0)

DefaultModelBinder派生会给你你想要的东西。在你的覆盖中,调用基本方法,如此

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // base BindModel should validate your model 
        base.BindModel(controllerContext, bindingContext);

        // (optional) Capture validation result
        bool ModelIsValid = bindingContext.ModelState.IsValid;

        var contentType = controllerContext.HttpContext.Request.ContentType;

        [...]
    }

答案 1 :(得分:0)

我修改了我的答案。我试图让这个工作时遇到了一些问题。下面详述的是我使用的问题和解决方案。

json: 您提供的json与您提供的Model不符。所以我假设json字符串应该包含这样的内容:

`DataEntrada: "1/1/2014"`

模型: 您的模型仅描述EntityName。反序列化的json是一个列表。这是两件不同的事情。所以我将json修改为一个定义EntityNamesEntityName列表)的对象,如下所示:

 `data = { EntityNames: [{ DataEntrada: "1/1/2014" }] };`

然后我实现了这个类..这将是反序列化的结果:

public class EntityInfo
{
    public EntityName[] EntityNames { get; set; }
}

最后修改了ActionMethod,如此:

public JsonResult SaveActionName([ModelBinder(typeof(JsonArrayValidationModelBinder<EntityInfo>))]EntityInfo viewModel)

验证 :验证EntityNames并不像我想象的那样容易实现。在模型绑定期间(作为列表的成员),我无法获取EntityName的验证属性。所以,我实现了一个源自&#39; ValidationAttribute&#39;的自定义验证器。像这样:

public class EntityNamesValidation : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        EntityName[] list = (EntityName[])value;
        foreach (EntityName e in list)
        {
            if (string.IsNullOrEmpty(e.DataEntrada.ToString()))
                return false;

            // more checks performed here

        }
        return true;
    }
}

然后我将EntityNamesValidation属性应用于EntityNamesEntityInfo,如下所示:

[EntityNamesValidation]
    public EntityName[] EntityNames { get; set; }

绑定期间模型不正确 JsonArrayValidationModelBinder使用的bindingContext没有任何实例。如果您在BindModel之前调试base.BindModel,您会看到bindingContext.Model为空。所以我做的是在反序列化之后和调用bindingContext.ModelMetadata.Model = model之前设置base.BindModel。我还在代码中移动base.BindModel,以便在返回model之前触发...见下文

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)  
{
    [...]

    bindingContext.ModelMetadata.Model = model;
    base.BindModel(controllerContext, bindingContext);

    return model;
}

验证 :我没有对此进行单元测试,但我确实在ActionMethod中放置了一个断点。然后我使用了以下json:

data = { EntityNames: [{ DataEntrada: "1/1/2014" }, { DataEntrada: null }] };

当代码到达断点时,ModelState.IsValid为false。然后我把json更改为:

data = { EntityNames: [{ DataEntrada: "1/1/2014" }, { DataEntrada: "2/19/2014" }] };

当代码到达断点时,ModelState.IsValid为真。

这种方法有效,但并不理想。我认为您希望在不创建自定义代码的情况下进行验证,并使用MVC来处理此问题。

我希望这会让你更进一步。

所有代码

的javascript

        data = { EntityNames: [{ DataEntrada: "1/1/2014" }, { DataEntrada: null }] };
        var jsonOfLog = JSON.stringify(data);
        $.ajax({
            type: 'POST',
            dataType: 'text',
            url: "/EntityData/SaveActionName",
            data: jsonOfLog,
            success: function (data) {
                alert(data);
            },
            error: function (result) {
                alert(result);
            }
                ,
            async: false
        });

模型

public class EntityInfo
{
    [EntityNamesValidation]
    public EntityName[] EntityNames { get; set; }
}

public class EntityName
{
    [Display(Name = "Data Entrada")]
    [DataType(DataType.Date)]
    [Required]
    public DateTime? DataEntrada { get; set; }
}

自定义验证器

public class EntityNamesValidation : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        EntityName[] list = (EntityName[])value;
        foreach (EntityName e in list)
        {
            if (string.IsNullOrEmpty(e.DataEntrada.ToString()))
                return false;

            // more checks performed here

        }
        return true;
    }
}

BindModel

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var contentType = controllerContext.HttpContext.Request.ContentType;
        String bodyText;
        Stream stream = null;
        try
        {
            stream = controllerContext.HttpContext.Request.InputStream;
            stream.Seek(0, SeekOrigin.Begin);
            using (var reader = new StreamReader(stream))
            {
                stream = null;
                bodyText = reader.ReadToEnd();
            }
        }
        finally
        {
            if (stream != null)
                stream.Dispose();
        }
        if (string.IsNullOrEmpty(bodyText))
        {
            return null;
        }
        var model = new JavaScriptSerializer().Deserialize<T>(bodyText);
        bindingContext.ModelMetadata.Model = model;
        base.BindModel(controllerContext, bindingContext);

        return model;
    }

ActionMethod

[HttpPost]
public JsonResult SaveActionName([ModelBinder(typeof(JsonArrayValidationModelBinder<EntityInfo>))]EntityInfo viewModel)