一、奇葩的问题

之前自己造轮子的时候,遇到一个很奇怪的问题,虽然需求很奇葩,但是还是尝试解决了一下

当提交的表单里包含多个重复名称的字段的时候,例如

<form action="/Test/save" method="post">
<!--省略其他字段-->
<input type="text" name="value" />
<input type="text" name="value" />
<input type="text" name="value" />
<!--有可能有更多的name为value的input-->
<input type="submit" value="submit" />
</form>

Html

如果需要模型在Action进行接收,那么通常的解决方案是用一个 IEnumerable<T> 类型或其派生类型来接收数据,以保证数据的完成性,例如这样一个模型

   public class Test
{
//省略其他字段
public string value { get; set; }
}

Model

一般来讲这么做没啥问题,可是问题来了

如果我需要将结果以逗号(,)分割并输出,那么我就需要写这样一行代码 string.Join(",", model.Test); ,无论是在哪里。

如果不在模型字段上做字符串拼接操作的,还会导致这个模型无法复用。而且代码看起来很不优雅。

二、思考问题

我把这个问题也发到了博问上.Net MVC 模型接收参数问题

普遍得到的答案都是我上面说到的解决方式,似乎不是我想要的。

尝试思考一下。

标准的Http的Request接收到的时候,对与MVC来说他只是一个Form或者QueryString(如果理解有误,欢迎指正),那么MVC框架是怎么做到将一个表单绑定到一个模型上呢?

为什么 HttpContext.Request.Form 中对应的字段倒是完成了字符串拼接的?

为什么表单和模型的赋值不一样?表单里的值我能不能用?

带着问题猛戳了一番度娘后,发现了一个惊喜的东西,模型绑定器(ModelBinder

概念我就不贴了,见这个大神的博客 ASP.NET MVC5 ModelBinder

这一篇是Core的, ASP.NET Core MVC Model Binding: Custom Binders

三、解决方案

1、建立一个自己的模型绑定器

Framework:

    public class TestModelBinder : DefaultModelBinder, IModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var model = base.BindModel(controllerContext, bindingContext);
//do something for model to format from Form or other place
return model;
} }

Core:

    public class TestModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var model = bindingContext.Model; //do something for model to format from Form or other place bindingContext.Result = ModelBindingResult.Success(model); return Task.CompletedTask;
}
}

只要在注释的地方,对模型需要赋值的字段进行操作就可以了

PS:具体操作就仁者见仁智者见智了,简单说一下我自己的做法,反射遍历模型类型为string的字段,如果模型字段值与表单同名的值不一致,由表单从新给字段赋值。

这个做法的效率有待提升,不过目前先这么解决。

好了模型绑定器有了,不过现在还不能用。

2、定义模型绑定器的Provider

这个方法在上面那个大神的博客里有写,我在这里再啰嗦一下

Framework:

public class TestModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(Type modelType)
{
if (modelType == typeof(Test))
{
return new TestModelBinder();
}
return null;
}
}

这里传入的Type是模型本身的Type,用来过滤对拿一些模型生效,一般会使用默认绑定器 DefaultModelBinder 的。

Core:

public class TestModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(DateTime))
{
return new TestModelBinder();
}
return null;
}
}

除了参数不太一样,两个版本基本长得一毛一样。

3、全局注册

定义为全局的模型绑定器就可以在所有模型绑定的时候启动。无需再加任何其他东西,但是同时面临一个问题就是使用原生的绑定器需要在参数上单独声明。

Framework是在Global.asax里面

public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
ModelBinderProviders.BinderProviders.Insert(, new TestModelBinderProvider());
   //或者下面这个也可以,二选一
//ModelBinders.Binders.Add(typeof(Test),new TestModelBinder());
}
}

Core是在Startup.cs里

public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(option => {
option.ModelBinderProviders.Insert(, new TestModelBinderProvider());
})

4、定义模型绑定属性

在特殊的绑定器不多的情况下我们可以选择将绑定器定义为属性

Framework:

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class FormFormatAttribute : CustomModelBinderAttribute
{
public override IModelBinder GetBinder()
{
return new TestModelBinder();
}
}

Core:

    [AttributeUsage(AttributeTargets.Parameter,AllowMultiple = false, Inherited = false)]
public class TestModelBinderAttribute : ModelBinderAttribute
{
public TestModelBinderAttribute() : base(typeof(TestModelBinder))
{
}
}

看起来基本也是一毛一样

属性的调用方法两个是一致的,在参数前面加上 [TestModelBinder] 就可以了。

总结:

经过一番折腾,模型绑定器搞定了,也能更加优雅绑定变量了。也不需要那么一堆什么拼接字符串啊,额外属性啊,之类的。

但是需要注意一点,Core的绑定器没有默认基类(也可能是我没找到,如果找到了,如果有找到的麻烦欢迎分享),所以不能像Framework一样先让默认的处理,处理完再处理。而是需要全部都由开发人员手动处理。

[MVC]自定义模型绑定器,从表单对模型进行赋值的更多相关文章

  1. 值提供器 AND 模型绑定器

    本章介绍了值提供器的作用,ASP MVC自带的5中值提供器.以及模型绑定器的作用,自定义模型绑定器并使用自定义的模型绑定器(类型上加上[ModelBinder(typeof(xx))]或者在全局模型绑 ...

  2. 玩转Django2.0---Django笔记建站基础七(表单与模型)

    第七章 表单与模型 表单是搜集用户数据信息的各种表单元素的集合,作用是实现网页上的数据交互,用户在网站输入信息,然后提交到网站服务器端进行处理(如数据录入和用户登录.注册等). 用户表单是web开发的 ...

  3. JavaScript:表单验证模型

    之前做的验证提示以弹框的形式出现太丑陋了,不符合标准的验证提示.如果要想进行更好的数据验证操作,那么必须进行一些模块化设计,通过表单样式的改变来提示.其实,一般的数据验证无非就是那么几种,例如: 大多 ...

  4. Atitit vue.js 把ajax数据 绑定到form表单

    Atitit vue.js 把ajax数据 绑定到form表单 1.1. 使用场景:主要应用在编辑与提交场合..1 1.2. 绑定数据到form控件,可以使用jquery,不过vue.js更加简单1 ...

  5. `<jsp:getProperty>`动作和`<jsp:setProperty>`动作的使用在一个静态页面填写图书的基本信息,页面信息提交给其他页面,并且在其页面显示。要去将表单元素的值赋值给Java

    <jsp:getProperty>动作和<jsp:setProperty>动作的使用 1.<jsp:getProperty>动作 语法格式: <jsp:get ...

  6. asp.net mvc 模型验证注解,表单提交

    一.添加模型 public class Account { public int ID { get; set; } [Display(Name = "姓名")] //设置要显示的字 ...

  7. activiti自定义流程之整合(三):整合自定义表单创建模型

    本来在创建了表单之后应该是表单列表和预览功能,但是我看了看整合的代码,和之前没有用angularjs的基本没有什么变化,一些极小的变动也只是基于angularjs的语法,因此完全可以参考之前说些的表单 ...

  8. [Django]下拉表单与模型查询

    前言:本文主要针对自定义下拉表单制作,下拉表单的内容是取至于数据库,即动态实现下拉表单 正文: 动态实现下拉表单有两种方法: 一.自己手动写 html 模板中的 <form ...> &l ...

  9. struts表单域模型注入

    表单使用struts标签,表单中每一个字段都可以这样来赋值 类(action).成员变量 这个叫域模型注入 <s:form action="orders" method=&q ...

随机推荐

  1. AOE工程实践-NCNN组件

    作者:杨科 NCNN是腾讯开源的一个为手机端极致优化的高性能神经网络前向计算框架.在AOE开源工程里,我们提供了NCNN组件,下面我们以SqueezeNet物体识别这个Sample为例,来讲一讲NCN ...

  2. JS-特效 ~ 03. 楼层跳跃、事件对象event的获取与使用、event的主要内容、screenX、pageX、clientX的区别、放大镜、模拟滚动条

    楼层跳跃 100%子盒子会继承父盒子的宽高.父盒子继承body宽高.Body继承html的宽高. 盒子属性:auto:适应盒子自身的宽度或者高度.(对自己负责) 盒子属性:100%:适应盒子父盒子的宽 ...

  3. 2019沈阳网络赛B.Dudu's maze

    https://www.cnblogs.com/31415926535x/p/11520088.html 啊,,不在状态啊,,自闭一下午,,都错题,,然后背锅,,,明明这个简单的题,,, 这题题面不容 ...

  4. FreeSql (三十二)Aop

    FreeSql AOP 已有的功能介绍,未来为会根据用户需求不断增强. 审计 CRUD 马云说过,996是修福报.对于多数程序员来说,加班是好事...起码不是闲人,不会下岗. 当如果因为某个 sql ...

  5. Mars视频笔记——Animation

    Animations的使用(1) 什么是Animations 提供了一系列的动画效果,可以应用在绝大多数控件中 Animations的分类 1 Tweened Animations 渐变动画 提供了旋 ...

  6. 初学FPGA图像处理,开发板选择建议

    我用的是ZYNQ7010的开发板,纯粹是入门学习,对于初学者,使用较多的xlinx入门级的开发板一般是zynq7000系列,淘宝上买的较好的是黑金和米联科,我买的就是黑金的,个人觉得教程很少,学习资料 ...

  7. Java第三次作业第五题

    5. [问题描述]Fibonacci序列处理 从文件in.txt中读取第一个数作为Fibonacci数列的开始数n,读取第二个数,作为需要写入的数的个数m. 将从n开始的m个Fibonacci数列写入 ...

  8. 链表实现比较高效的删除倒数第k项

    最近写链表不太顺,无限的段错误.今天中午写的链表删除倒数第k项,用的带尾节点的双向链表,感觉已经把效率提到最高了,还是超时,改了很多方法都不行,最 终决定看博客,发现原来是审题错了,阳历给的是以-1结 ...

  9. 【Jenkins持续集成(二)】Windows上安装Jenkins教程

    一.前言 Jenkins是一款开源 CI&CD 软件,用于自动化各种任务,包括构建.测试和部署软件. Jenkins 支持各种运行方式,可通过系统包.Docker 或者通过一个独立的 Java ...

  10. [LeetCode]sum合集

    LeetCode很喜欢sum,里面sum题一堆. 1.Two Sum Given an array of integers, return indices of the two numbers suc ...