当ASP.NET MVC邂逅jQuery.Ajax提交数组

作者:   出处:互联网   2015-07-05 23:00:46   阅读:4

硬编码味道太重,换个类型又得重写,工作量跟之前比还视乎增加了,只是Controller变得优雅了。这种浪费青春又耗电的做法还是不符合要求。

当ASP.NET MVC 通过JQuery的Ajax 提交数组时,MVC的model binder机制就失效了。们不得不在Controller里面编写自定义代码,将Request提交的数据转换成需要的数据类型。这个过程往往枯燥乏味。下面以某项目的实际例子来演示如何解决这个问题,提供一个通用的解决方案。 


 

需求描述


 

当用户更改了配置,需要Ajax提交到服务器。


 


 

前端代码:


 

var items = [];  $( input:checked ).each(function () {  items.push($(this).val());  });  $.ajax({  type: 'post',  url: 'Configure/Status',  data: { answers: items }  }); 


 

后端代码:


 

public enum AnswerStatus  {  Correct = 1,  Incorrect = 2,  Unanswered = 3 }  [HttpPost]  public ActionResult Status(IList AnswerStatus  answers)  {   .  } 


 

这里的answers始终为null. 神器fiddler出场,发现用JQuery.Ajax 提交Array的数据,提交的时候始终会在名称后面加上 [] , 问题就出在这里。


 


 

根据发现的结果修改代码:


 

[HttpPost]  public ActionResult Status(IList AnswerStatus  answers)  {  answers = Request.Form.GetValues( answers[] ).Select(d =  d.ToEnum AnswerStatus (AnswerStatus.Unanswered).ToList();  } 


 

虽然这样能够通过解决问题,但每次提交Array都要这样手工解析request,视乎一夜回到石器时代了。其实们马上会想到MVC 的Mode Binder。


 

尝试进行第一次重构:


 

public class AnswerModelBinder : IModelBinder  {  public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)  {  return controllerContext.RequestContext.HttpContext.Request.Form.GetValues( answers[] ).Select(d =  d.ToEnum AnswerStatus (AnswerStatus.Unanswered).ToList();   }  } 


 

硬编码味道太重,换个类型又得重写,工作量跟之前比还视乎增加了,只是Controller变得优雅了。这种浪费青春又耗电的做法还是不符合要求。


 

进行第二次重构 : DefaultModelBinder 出场


 

万能的DefaultModelBinder,能够绑定任何类型,可惜就是client传过来的name后面多加了 [] ,导致DefaultModelBinder无法准确解析。那们能不能欺骗DefaultModelBInder呢?


 

查看ModelBindingContext发现有一个ModelName属性,感觉有点像要绑定的参数的名称,调试跟踪发现ModelName确实就是参数的名称,那们修改ModelName让他跟client传过来的name保持一致是否就能够充分发挥DefaultModelBinder。于是动手创建一个JQAjaxModelBinder


 

并继承自DefaultModelBinder:


 

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)  {  if(bindingContext.ModelType.IsEnumerable())  {  var key = bindingContext.ModelName +  [] ;  var valueResult = bindingContext.ValueProvider.GetValue(key);  if(valueResult != null   !string.IsNullOrEmpty(valueResult.AttemptedValue))  {  bindingContext.ModelName = key;  }  }  return base.BindModel(controllerContext, bindingContext);  }//如何使用自定义ModelBinder。该方法是Controller里面的Action  public ActionResult Status([ModelBinder(typeof(ModelBinder.JQAjaxModelBinder))] IList AnswerStatus  answers)  {      } 


 

这时,Controller里面的Status (Action)方法已经能够正确得到前端传来的数据。并且还是强类型的。当然很多程序员都是懒惰的,笔者也是这其中一份子。笔者连Parameter前面的参数([ModelBinder(typeof(ModelBinder.JQAjaxModelBinder))])都不想写,那们直接在ModelBinders里面注册吧。其实注册的时候也有点麻烦,必须设定Type,那能提前知道有那些类型啊。干脆将JQAjaxModelBinder设置成默认的ModerBinder,一劳永逸,再也没有烦心事情了。


 

ModelBinder不同注册方法


 

通过在Action方法的参数前面添加ModelBinder标签,上文则是采用的这种方法。


 

数据类型上面添加ModelBinder标签


 

[ModelBinder(typeof(ModelBinder.JQAjaxModelBinder))]  Public class User  {  } 


 

通过ModelBinders注册


 

ModelBinders.Binders.Add(typeof(User), new ModelBinder.JQAjaxModelBinder()); 


 

设置默认的ModerBinder


 

ModelBinders.Binders.DefaultBinder = new ModelBinder.JQAjaxModelBinder(); 


 

后记: 当们在开发的时候,经常做重复的事情,当一件事情重复多次后,们就需要停下来认真思考,能不能将这些事情抽象出来,做一个通用的解决方案呢?一劳永逸的解决这些问题。

Copyright© 2006-2015 ComponentCN.com all rights reserved.重庆磐岩科技有限公司(控件中国网) 版权所有 渝ICP备12000264号 法律顾问:元炳律师事务所
客服软件
live chat