• ASP.NET MVC中的Json Binding和Validate


    引子:电子商务网站支付功能页面往往会有很多信息,对于这些信息的保存,往往是分步完成的,那么使用Ajax最合适不过了,比如其中的收货人信息模块。这些信息的新建和编辑保存都是用Ajax来完成的。那么有几种方式完成这个操作呢,我想到如下几种。 
    先来看看该功能的截图: 


    一般情况下这些信息会对应一个实体类,就命名为:ReceiverInfo,简单起见,我定义ReceiverInfo如下: 



    1、将需要的值拼接成json文本,再Action里面处理

    首先您需要将要保存的值拼接成一个json文本,类似: 

    var test = "{ ReceiverId: 5, ReceiverName: 'will', Sex: 'F', CreateDate: '2011-02-21' }"; 

    然后用Jquery保存到数据库,代码如下: 


    $.ajax({ 
        url: "/Home/test1", 
        type: "post", 
        cache: false, 
        data: test 
    }); 

    然后您在Action里面这样操作: 

    StreamReader reader = new StreamReader(Request.InputStream); 
    string bodyText = reader.ReadToEnd(); 
    JavaScriptSerializer js = new JavaScriptSerializer(); 
    ReceiverInfo receiver = js.DeserializeReceiverInfo>(bodyText); 
    //保存。。。 

    2、利用自定义的ModelBinder实现


    JsonBinder 

    1 public class JsonBinderT> : IModelBinder 
    2 { 
    3     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    4     { 
    5         StreamReader reader = new StreamReader(controllerContext.HttpContext.Request.InputStream); 
    6         string json = reader.ReadToEnd(); 

    8         if (string.IsNullOrEmpty(json)) 
    9             return json; 
    10 
    11         JavaScriptSerializer serializer = new JavaScriptSerializer(); 
    12         object jsonData = serializer.DeserializeObject(json); 
    13         return serializer.DeserializeT>(json); 
    14     } 
    15 }
     

    我们继承IModelBinder接口,实现其方法: 

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 

    即可。我们可以在Action里面这样使用: 


    public ActionResult Test1([ModelBinder(typeof(JsonBinderReceiverInfo>))] ReceiverInfo receiverInfo) 

    这样我们自定义的IModelBinder就会取代DefaultModelBinder完成数据绑定。 

    3、直接传递一个Json对象

    上面两种方法并没有利用MVC的System.ComponentModel.DataAnnotations进行有效的数据验证。您可能需要自己手动验证,无疑增加了工作量。 

    我们试试这种方式。 

    前端的写法: 

    var b = { 
        ReceiverId: 5, 
        ReceiverName: "will", 
        Sex: "F", 
        CreateDate: "2011-02-21" 
    }; 
    $.ajax({ 
        url: "/Home/test1", 
        type: "post", 
        cache: false, 
        data: b, 
        success: function(data) { alert(data.message); }, 
        error: function(xhr, a, b) { alert(xhr.responseText); } 
    }); 


    Action的写法: 


    public ActionResult Test1(ReceiverInfo receiverInfo) 

    我们能正常的得到绑定后的数据。而且我们还能利用System.ComponentModel.DataAnnotations进行数据验证。我们为ReceiverInfo做如下改动: 


    [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "收货人必须填写")] 
    public string ReceiverName { get; set; } 

    并在前端为ReceiverName赋值为空字符串,再次执行,得到提示: 


    很好,不过我们有新的要求了,那就是传递更复杂的对象,比如对象套嵌对象,对象有集合属性,这种方式不能胜任了。 


    4、利用MvcFutures的JsonValueProviderFactory


    每一版的MVC都有一个MvcFutures,里面会有一些额外的功能,这些功能有些会加入下一个版本中,而这些功能在某些时候很有用处。我查看了里面的类,发现有一个类JsonValueProviderFactory正是处理复杂对象的提交和数据验证。由于json对象需要特定解析才能使用默认的DefaultModelBinder,而这个解析过程需要在ValueProvider阶段完成,所以需要实现特定的ValueProvider给DefaultModelBinder。我们需要实现一个ValueProviderFactory和IValueProvider,而MVC里面的DictionaryValueProvider(继承了IValueProvider)已经足够使用了,所以只需要继承ValueProviderFactory实现其方法:public override IValueProvider GetValueProvider(ControllerContext controllerContext)即可,具体代码您可以看JsonValueProviderFactory。 

    我们定义另一个类: 

    ReceiverInfoChild 

    public class ReceiverInfoChild 

        [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "ChildId必须填写")] 
        public string ChildId { get; set; } 



    并为类ReceiverInfo增加一个属性public List ReceiverInfoChild { get; set; } 
    我们把JsonValueProviderFactory拿出来放在项目里面,然后在Global.asax里面注册一下,就可以使用了。 


    protected void Application_Start() 

        AreaRegistration.RegisterAllAreas(); 

        RegisterRoutes(RouteTable.Routes); 

        ValueProviderFactories.Factories.Add(new JsonValueProviderFactory()); 



    因为JsonValueProviderFactory中有:if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))来判断进来的请求是不是json对象,所以我们提交数据的时候需要这样写: 


    var ReceiverInfo = [ 
                { 
                    ReceiverInfoChild: [{ ChildId: "1" }, { ChildId: "11"}], 
                    ReceiverId: 5, 
                    ReceiverName: "will", 
                    Sex: "F", 
                    CreateDate: "2011-02-21" 
                }, 
                { 
                    ReceiverInfoChild: [{ ChildId: "2" }, { ChildId: "22"}], 
                    ReceiverId: 5, 
                    ReceiverName: "will", 
                    Sex: "F", 
                    CreateDate: "2011-02-21" 
                } 
                ]; 
    $.ajax({ 
        url: "/Home/test1", 
        type: "post", 
        cache: false, 
        contentType: "application/json;charset=utf-8", 
        data: JSON.stringify(ReceiverInfo), 
        success: function(data) { alert(data.message); }, 
        error: function(xhr, a, b) { alert(xhr.responseText); } 
    }); 


    其中JSON.stringify(ReceiverInfo)是将json对象转换成字符串,您可以到这里下载该类库。 

    在Action里面,我们这样写就可以了: 


    public ActionResult Test1(ListReceiverInfo> receiverInfo) 

    看一下调试的结果: 



    完全正常绑定了值。我们再看看数据验证: 



    至此,我们实验了四种方案: 

    第一种方案,最麻烦,而且容易出错(可能跟我个人不喜欢拼接字符串有关系); 

    第二种方案,有一定的通用性,但是不利于数据验证; 

    第三种方案,通用,可以进行有效的数据验证,应对一般的需求够用了,但是处理更复杂的对象不行; 

    第四种方案,几乎可以处理我们遇到的所有情况 

    另外,这是在ASP.NET MVC2中的使用,到了ASP.NET MVC3,微软已经把JsonValueProviderFactory作为内置的功能了。

  • 相关阅读:
    HTTP缓存总结
    最近公司用到了lombok,感觉很不错的样子,所以上网搜了一些资料,总结了一下用法。
    servlet中获取各种相对地址(服务器、服务器所在本地磁盘、src等)。
    centos 7 lvm 扩容刷新lv 报错Couldn't find valid filesystem superblock.的解决
    自动化运维工具——ansible详解(二)
    自动化运维工具——ansible详解(一)
    k8s中删除pod的操作
    rsync 只同步目录结构 不同步文件
    redis-info命令输出详解
    centos解决“c++: internal compiler error: Killed (program cc1plus)”错误
  • 原文地址:https://www.cnblogs.com/rrxc/p/3939868.html
Copyright © 2020-2023  润新知