天道酬勤,学无止境

How to test custom Model Binders in ASP.NET MVC?

I've written some custom model binders (implementing IModelBinder) in our ASP.NET MVC application. I'm wondering what is a good approach to unittest them (binders)?

评论

I did it this way:

var formElements = new NameValueCollection() { {"FirstName","Bubba"}, {"MiddleName", ""}, {"LastName", "Gump"} };         
var fakeController = GetControllerContext(formElements);
var valueProvider = new Mock<IValueProvider>();           

var bindingContext = new ModelBindingContext(fakeController, valueProvider.Object, typeof(Guid), null, null, null, null);



private static ControllerContext GetControllerContext(NameValueCollection form) {
    Mock<HttpRequestBase> mockRequest = new Mock<HttpRequestBase>();
    mockRequest.Expect(r => r.Form).Returns(form);

    Mock<HttpContextBase> mockHttpContext = new Mock<HttpContextBase>();
    mockHttpContext.Expect(c => c.Request).Returns(mockRequest.Object);

    return new ControllerContext(mockHttpContext.Object, new RouteData(), new Mock<ControllerBase>().Object);
}

And then I just passed in the bindingContext variable to the BindModel method of the object that implements the IModelBinder interface.

Here's a simple no-mocks way I wrote for you on my blog assuming you use the ValueProvider and not the HttpContext: http://www.hanselman.com/blog/SplittingDateTimeUnitTestingASPNETMVCCustomModelBinders.aspx

[TestMethod]  
public void DateTime_Can_Be_Pulled_Via_Provided_Month_Day_Year_Hour_Minute_Second_Alternate_Names()  
{  
    var dict = new ValueProviderDictionary(null) {   
            { "foo.month1", new ValueProviderResult("2","2",null) },  
            { "foo.day1", new ValueProviderResult("12", "12", null) },  
            { "foo.year1", new ValueProviderResult("1964", "1964", null) },  
            { "foo.hour1", new ValueProviderResult("13","13",null) },  
            { "foo.minute1", new ValueProviderResult("44", "44", null) },  
            { "foo.second1", new ValueProviderResult("01", "01", null) }  
        };  

    var bindingContext = new ModelBindingContext() { ModelName = "foo", ValueProvider = dict };  

    DateAndTimeModelBinder b = new DateAndTimeModelBinder() { Month = "month1", Day = "day1", Year = "year1", Hour = "hour1", Minute = "minute1", Second = "second1" };  

    DateTime result = (DateTime)b.BindModel(null, bindingContext);  
    Assert.AreEqual(DateTime.Parse("1964-02-12 13:44:01"), result);  
}  

dict could be refactored like this

            FormCollection form = new FormCollection
                                  {
                                      { "month1", "2" },
                                      { "day1", "12" },
                                      { "year1", "1964" },
                                      { "hour1", "13" },
                                      { "minute1", "44" },
                                      { "second1", "01" }
                                  };

            var bindingContext = new ModelBindingContext() { ModelName = "foo", ValueProvider = form.ToValueProvider() };  

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • Model binder for abstract class in asp.net core mvc 2
    I've been trying to implement a model binder for an abstract class in ASP.NET Core 2 without success. I've studied two articles in particular which look very good: http://www.dotnetcurry.com/aspnet-mvc/1368/aspnet-core-mvc-custom-model-binding Asp net core rc2. Abstract class model binding There are two goals I'm trying to reach, Automatically create as many nested editors as needed from model (child nesting). Map the form values back into the model correctly. Here's my code based on the articles mentioned above. public class Trigger { public ActionBase Action { get; set; } } [ModelBinder
  • Asp.net MVC中的自定义DateTime模型绑定程序(Custom DateTime model binder in Asp.net MVC)
    问题 我想为DateTime类型编写我自己的模型活页夹。 首先,我想编写一个可以附加到模型属性的新属性,例如: [DateTimeFormat("d.M.yyyy")] public DateTime Birth { get; set,} 这是简单的部分。 但是活页夹部分要困难一些。 我想为DateTime类型添加一个新的模型活页夹。 我可以 实现IModelBinder接口并编写我自己的BindModel()方法从DefaultModelBinder继承并重写BindModel()方法 我的模型具有上述属性( Birth )。 因此,当模型尝试将请求数据绑定到此属性时,将调用我的模型绑定器的BindModel(controllerContext, bindingContext) 。 一切正常,但是。 如何从controller / bindingContext获取属性属性,以正确解析我的日期? 如何到达属性Birth的PropertyDesciptor ? 编辑 由于关注点分离,我的模型类是在不(也不应该)引用System.Web.MVC程序集的程序集中定义的。 设置自定义绑定(类似于Scott Hanselman的示例)属性在这里是不行的。 回答1 我认为您不应该在模型上放置特定于语言环境的属性。 此问题的两个其他可能的解决方案是:
  • 接受逗号和点作为小数点分隔符(Accept comma and dot as decimal separator [duplicate])
    问题 这个问题已经在这里有了答案: 如何在ASP.NET MVC控制器中设置小数点分隔符? (4个答案) 3年前关闭。 ASP.NET MVC中的模型绑定很棒,但是它遵循语言环境设置。 在我的语言环境中,小数点分隔符是逗号(','),但用户也使用点('。'),因为他们不愿意切换布局。 我希望在模型中所有decimal字段的一处实现这一点。 我应该为decimal类型实现自己的Value Provider(或事件Model Binder),还是错过了一些简单的方法? 回答1 最干净的方法是实现自己的模型活页夹 public class DecimalModelBinder : DefaultModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); return valueProviderResult == null ? base.BindModel(controllerContext, bindingContext)
  • 数据输入后修剪字符串的最佳方法。 我应该创建自定义模型活页夹吗?(Best way to trim strings after data entry. Should I create a custom model binder?)
    问题 我正在使用ASP.NET MVC,我希望在将所有用户输入的字符串字段插入数据库之前先对其进行修剪。 并且由于我有许多数据输入表单,因此我正在寻找一种修整所有字符串的方法,而不是显式修整每个用户提供的字符串值。 我很想知道人们如何以及何时修剪琴弦。 我考虑过可能要创建一个自定义模型联编程序并在那里修剪任何字符串值...那样,我所有的修剪逻辑都包含在一个地方。 这是一个好方法吗? 是否有任何代码示例可以做到这一点? 回答1 public class TrimModelBinder : DefaultModelBinder { protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value) { if (propertyDescriptor.PropertyType == typeof(string)) { var stringValue = (string)value; if (!string.IsNullOrWhiteSpace(stringValue)) { value =
  • 在asp.net mvc 4中格式化datetime(Format datetime in asp.net mvc 4)
    问题 如何在asp.net mvc 4中强制使用datetime格式? 在显示模式下,它显示为我想要的,但在编辑模型中,它没有显示。 我正在使用displayfor和editorfor,并使用dataformatstring =“ {0:dd / MM / yyyy}”的applyformatineditmode = true进行了尝试: 我的文化和uiculture在web.config(两者)中都实现了全球化。 在application_start()中修改文化和uiculture 用于日期时间的自定义modelbinder 我不知道如何强制使用,我需要输入日期作为dd / MM / yyyy而不是默认值。 更多信息:我的视图模型是这样的 [DisplayName("date of birth")] [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)] public DateTime? Birth { get; set; } 在视图中,我使用@Html.DisplayFor(m=>m.Birth)但这按预期工作(我看到格式),并输入我使用@Html.EditorFor(m=>m.Birth)但是如果尝试并输入类似13
  • 如何从jQuery / Ajax将Dictionary作为参数传递给ActionResult方法?(How do I pass a Dictionary as a parameter to an ActionResult method from jQuery/Ajax?)
    问题 我正在使用jQuery在ASP.NET MVC中使用Http Post进行Ajax调用。 我希望能够传递值字典。 我能想到的最接近的东西是传递多维的字符串数组,但是实际上传递给ActionResult方法的结果是一个包含“键/值”对的字符串连接的一维字符串数组。 例如,下面的“值”数组中的第一项包含以下值: "id,200" 这是我的ActionResult方法的示例: public ActionResult AddItems(string[] values) { // do something } 这是我如何从jQuery调用方法的示例: $.post("/Controller/AddItems", { values: [ ["id", "200"], ["FirstName", "Chris"], ["DynamicItem1", "Some Value"], ["DynamicItem2", "Some Other Value"] ] }, function(data) { }, "json"); 有谁知道如何将字典对象从jQuery传递到ActionResult方法而不是数组? 我真的很想这样定义我的ActionResult: public ActionResult AddItems(Dictionary<string, object> values) { // do
  • 复杂模型和局部视图-ASP.NET MVC 3中的模型绑定问题(Complex models and partial views - model binding issue in ASP.NET MVC 3)
    问题 我的示例MVC 3应用程序中有2个模型, SimpleModel和ComplexModel ,如下所示: public class SimpleModel { public string Status { get; set; } } public class ComplexModel { public ComplexModel() { Simple = new SimpleModel(); } public SimpleModel Simple{ get; set; } } 我已经为此模型定义了视图: _SimplePartial.cshtml : @model SimpleModel @Html.LabelFor(model => model.Status) @Html.EditorFor(model => model.Status) 和Complex.cshtml : @model ComplexModel @using (Html.BeginForm()) { @Html.Partial("_SimplePartial", Model.Simple) <input type="submit" value="Save" /> } 提交表单后,在“ Status字段中输入随机值,该值不会绑定到我的模型。 当我在控制器操作中检查模型时,“ Status字段为NULL :
  • 如何使ASP.Net MVC模型绑定程序将传入日期视为UTC?(How to make ASP.Net MVC model binder treat incoming date as UTC?)
    问题 我正在将对象发布到MVC控制器。 该对象包含一个名为StartDt的字段,并且在客户端上是本地时间的javascript Date对象。 当我在对象上调用JSON.stringify并使用jQuery的ajax方法将其发布到服务器时,我在Firebug中看到发送到服务器的内容是一个ISO字符串,例如“ 1900-12-31T13:00:00.000Z”,认为应该是UTC格式的当地时间。 但是,当我在控制器中查看DateTime字段时,它看起来像是返回本地时间而不是UTC。 我怎样才能解决这个问题? 我想存储来自客户端的日期的UTC版本。 回答1 您可能必须使用DateTime.ToUniversalTime()方法来获取UTC时间。 回答2 此问题在ASP.NET Core 2.0中仍然存在。 以下代码将解决该问题,支持ISO 8601基本和扩展格式,正确保留该值并正确设置DateTimeKind 。 这与JSON.Net解析的默认行为保持一致,因此可以使模型绑定行为与系统的其余部分保持一致。 首先,添加以下模型活页夹: public class DateTimeModelBinder : IModelBinder { private static readonly string[] DateTimeFormats = { "yyyyMMdd'T'HHmmss
  • 通用类型的ASP.NET MVC模型绑定器(ASP.NET MVC Model Binder for Generic Type)
    问题 是否可以为泛型类型创建模型绑定程序? 例如,如果我有一个类型 public class MyType<T> 有什么方法可以创建适用于任何类型的MyType的自定义模型联编程序? 谢谢,内森 回答1 创建一个模型绑定器,重写BindModel,检查类型并执行所需的操作 public class MyModelBinder : DefaultModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (HasGenericTypeBase(bindingContext.ModelType, typeof(MyType<>)) { // do your thing } return base.BindModel(controllerContext, bindingContext); } } 将您的模型资料夹设置为global.asax中的默认值 protected void Application_Start() { // Model Binder for My Type ModelBinders.Binders.DefaultBinder = new MyModelBinder(); }
  • 具有错误日期格式的MVC DateTime绑定(MVC DateTime binding with incorrect date format)
    问题 Asp.net-MVC现在允许DateTime对象的隐式绑定。 我采取了以下行动 public ActionResult DoSomething(DateTime startDate) { ... } 这成功地将一个字符串从ajax调用转换为DateTime。 但是,我们使用日期格式dd / MM / yyyy; MVC正在转换为MM / dd / yyyy。 例如,使用字符串'09 / 02/2009'提交对操作的调用会导致DateTime为'02 / 09/2009 00:00:00',或者在我们的本地设置中为9月2日。 我不想为了日期格式而滚动自己的模型活页夹。 但是似乎不需要更改操作以接受字符串,然后如果MVC能够为我执行此操作,则可以使用DateTime.Parse。 有什么方法可以更改DateTime的默认模型联编程序中使用的日期格式? 默认模型联编程序是否不应该使用您的本地化设置? 回答1 我刚刚通过更详尽的谷歌搜索找到了答案: 梅尔文·哈伯(Melvyn Harbor)全面解释了为什么MVC会以这种方式使用日期,以及在必要时如何覆盖日期: http://weblogs.asp.net/melvynharbour/archive/2008/11/21/mvc-modelbinder-and-localization.aspx 在寻找要解析的值时
  • ASP.NET MVC3-DateTime格式(ASP.NET MVC3 - DateTime format)
    问题 我正在使用ASP.NET MVC 3。 我的ViewModel看起来像这样: public class Foo { [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}", ApplyFormatInEditMode = true)] public DateTime StartDate { get; set; } ... } 看来,我有这样的事情: <div class="editor-field"> @Html.EditorFor(model => model.StartDate) <br /> @Html.ValidationMessageFor(model => model.StartDate) </div> StartDate以正确的格式显示,但是当我将其值更改为19.11.2011并提交表单时,出现以下错误消息:“值'19 .11.2011'对StartDate无效。” 任何帮助将不胜感激! 回答1 您需要在dd.MM.yyyy是有效日期时间格式的web.config文件的globalization元素中设置适当的区域性: <globalization culture="...." uiCulture="...." /> 例如,这是德语的默认格式: de-DE 。 更新
  • ASP.NET MVC使用自定义Modelbinder时从客户端检测到潜在危险的Request.Form值(ASP.NET MVC A potentially dangerous Request.Form value was detected from the client when using a custom modelbinder)
    问题 在这里得到错误: ValueProviderResult value = bindingContext.ValueProvider.GetValue("ConfirmationMessage"); 如何只允许选择值? IE [ValidateInput(false)] public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { ValueProviderResult value = bindingContext.ValueProvider.GetValue("ConfirmationMessage"); ValueProviderResult value2 = bindingContext.ValueProvider.GetValue("ConfirmationMessage2"); } 回答1 您有几种选择。 在模型上,将此属性添加到允许HTML所需的每个属性中-最佳选择 using System.Web.Mvc; [AllowHtml] public string SomeProperty { get; set; } 在控制器操作上,添加此属性以允许所有HTML [ValidateInput(false)] public
  • POST json字典(POST json dictionary)
    问题 我正在尝试以下操作:内部带有字典的模型在第一个ajax请求上发送该模型,然后将结果再次序列化,然后将其发送回控制器。 这应该测试我可以在模型中找回字典。 没用 这是我的简单测试: public class HomeController : Controller { public ActionResult Index (T a) { return View(); } public JsonResult A(T t) { if (t.Name.IsEmpty()) { t = new T(); t.Name = "myname"; t.D = new Dictionary<string, string>(); t.D.Add("a", "a"); t.D.Add("b", "b"); t.D.Add("c", "c"); } return Json(t); } } //model public class T { public string Name { get; set; } public IDictionary<string,string> D { get; set; } } JavaScript: $(function () { var o = { Name: 'somename', "D": { "a": "b", "b": "c", "c": "d" } }; $
  • 如何在MVC5项目中将Json.NET用于JSON模型绑定?(How to use Json.NET for JSON modelbinding in an MVC5 project?)
    问题 我一直在互联网上寻找答案或示例,但找不到。 我只是想更改默认的JSON序列化程序,该序列化程序用于在模型绑定到JSON.NET库时反序列化JSON。 我已经找到了这篇SO帖子,但是到目前为止还无法实现,我什至看不到System.Net.Http.Formatters命名空间,也看不到GlobalConfiguration 。 我想念什么? 更新 我有一个ASP.NET MVC项目,它基本上是一个MVC3项目。 目前,我的目标是.NET 4.5,并使用ASP.NET MVC 5和相关的NuGet程序包。 我没有看到System.Web.Http程序集,也没有看到任何类似的名称空间。 在这种情况下,我想注入JSON.NET用作JSON类型的请求的默认模型绑定器。 回答1 我终于找到了答案。 基本上,我不需要MediaTypeFormatter东西,不是设计用于MVC环境,而是在ASP.NET Web API中,这就是为什么我看不到那些引用和名称空间(顺便说一下,这些都包含在Microsoft.AspNet.WeApi NuGet包)。 解决方案是使用自定义值提供程序工厂。 这是所需的代码。 public class JsonNetValueProviderFactory : ValueProviderFactory { public override IValueProvider
  • 将JSON数据发布到ASP.NET MVC(Posting JSON Data to ASP.NET MVC)
    问题 我试图使用JSON获取网页的订单项列表,然后将使用到达的相同JSON结构(通过更改字段值除外)通过ajax请求对其进行处理,并将其发送回服务器。 从服务器接收数据很容易,甚至更容易操作! 但是将JSON数据发送回服务器以节省...自杀时间! 请有人帮忙! Java脚本 var lineitems; // get data from server $.ajax({ url: '/Controller/GetData/', success: function(data){ lineitems = data; } }); // post data to server $.ajax({ url: '/Controller/SaveData/', data: { incoming: lineitems } }); C#-对象 public class LineItem{ public string reference; public int quantity; public decimal amount; } C#-控制器 public JsonResult GetData() { IEnumerable<LineItem> lineItems = ... ; // a whole bunch of line items return Json(lineItems); } public
  • ASP.NET MVC 3和4之间的区别? [关闭](Difference between ASP.NET MVC 3 and 4? [closed])
    问题 关门了。 这个问题需要更加集中。 它当前不接受答案。 想要改善这个问题吗? 更新问题,使其仅通过编辑此帖子即可将重点放在一个问题上。 6年前关闭。 改善这个问题 是否有详尽的清单来说明MVC4的所有新功能以及MVC3的所有变化? (发行说明没有太大帮助) 回答1 从MVC4发行说明中复制和粘贴: •现代的HTTP编程模型:使用新的强类型HTTP对象模型直接访问和操纵Web API中的HTTP请求和响应。 通过新的HttpClient类型,客户端上可以对称使用相同的编程模型和HTTP管道。 •完全支持路由:ASP.NET Web API支持ASP.NET Routing的全套路由功能,包括路由参数和约束。 此外,使用简单的约定将操作映射到HTTP方法。 •内容协商:客户端和服务器可以一起确定从Web API返回的数据的正确格式。 ASP.NET Web API为XML,JSON和Form URL编码格式提供了默认支持,您可以通过添加自己的格式化程序来扩展此支持,甚至可以替换默认的内容协商策略。 •模型绑定和验证:模型绑定器提供了一种简便的方法,可以从HTTP请求的各个部分提取数据并将这些消息部分转换为Web API操作可以使用的.NET对象。 还基于数据注释对动作参数执行验证。 •过滤器:ASP.NET Web API支持过滤器,包括众所周知的过滤器,例如[Authorize
  • Inject a dependency into a custom model binder and using InRequestScope using Ninject
    I'm using NInject with NInject.Web.Mvc. To start with, I've created a simple test project in which I want an instance of IPostRepository to be shared between a controller and a custom model binder during the same web request. In my real project, I need this because I'm getting IEntityChangeTracker problems where I effectively have two repositories accessing the same object graph. So to keep my test project simple, I'm just trying to share a dummy repository. The problem I'm having is that it works on the first request and that's it. The relevant code is below. NInjectModule: public class
  • 属性的自定义模型活页夹(Custom model binder for a property)
    问题 我有以下控制器操作: [HttpPost] public ViewResult DoSomething(MyModel model) { // do something return View(); } MyModel如下所示: public class MyModel { public string PropertyA {get; set;} public IList<int> PropertyB {get; set;} } 因此,DefaultModelBinder应该可以毫无问题地绑定此对象。 唯一的事情是我想使用特殊/自定义的绑定器来绑定PropertyB而且我也想重用此绑定器。 因此,我认为解决方案是将ModelBinder属性放在PropertyB之前,这当然是行不通的(属性上不允许使用ModelBinder属性)。 我看到两个解决方案: 要在每个单个属性而不是整个模型上使用动作参数(我不希望这样做,因为该模型具有很多属性),如下所示: public ViewResult DoSomething(string propertyA, [ModelBinder(typeof(MyModelBinder))] propertyB) 若要创建新类型,请说MyCustomType: List<int>并为此类型注册模型绑定程序(这是一个选项)
  • mvc3中有小数的错误-该值对于字段无效(error with decimal in mvc3 - the value is not valid for field)
    问题 我正在关注[ASP.NET MVC 3入门] [1]。 而且我无法添加/编辑Price = 9.99或9,99的值。 它说:“值9.99对价格无效。” 和“字段价格必须是数字。” 如何解决这个问题? 模型: public class Movie { public int ID { get; set; } public string Title { get; set; } public DateTime ReleaseDate { get; set; } public string Genre { get; set; } public decimal Price { get; set; } } public class MovieDbContext : DbContext { public DbSet<Movie> Movies { get; set; } } 控制器: public class MovieController : Controller { private MovieDbContext db = new MovieDbContext(); // // GET: /Movie/ public ViewResult Index() { var movie = from m in db.Movies where m.ReleaseDate > new DateTime
  • How to set culture for date binding in Asp.Net Core?
    I have an Asp.Net Core application with MVC. I'm submitting a form with a date on the form. Form looks (roughly) like this: @model EditCustomerViewModel <form asp-action="Edit"> <input asp-for="ServiceStartDate" class="form-control" type="date" /> <input type="submit" value="Update" class="btn btn-success" /> </form> Controller action is: [HttpPost] public async Task<IActionResult> Edit(EditCustomerViewModel viewModel) { // do stuff return RedirectToAction("Index"); } View model is: public class EditCustomerViewModel { public Guid Id { get; set; } [DataType(DataType.Date)] public DateTime