一、奇葩的问题

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

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

<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. 个推TechDay参会感悟

    上周六去参加了个推和FCC联合在梦想小镇举办的TechDay,当然是作为台下听讲选手参与的,想上去讲可惜实力他不允许啊,吹牛逼我在行,讲技术可就有点虚了,老老实实的坐在台下听大佬们的分享,当然由于买了 ...

  2. Oracle 11g Rac 用rman实现把本地数据文件迁移到ASM共享存储中

    在Oracle Rac环境中,数据文件都是要存放在ASM共享存储上的,这样两个节点才能同时访问.而当你在某一节点下把数据文件创建在本地磁盘的时候,那么在另一节点上要访问该数据文件的时候就会报错,因为找 ...

  3. Vert.x Core 文档手册

    Vert.x Core 文档手册 中英对照表 Client:客户端 Server:服务器 Primitive:基本(描述类型) Writing:编写(有些地方译为开发) Fluent:流式的 Reac ...

  4. android View的测量和绘制

    本篇内容来源于android 群英传(徐易生著) 我写到这里,是觉得徐易生讲的确实很好, 另外加入了一些自己的理解,便于自己基础的提高. 另外参考:http://www.gcssloop.com/cu ...

  5. 手把手教程: CentOS 6.5 LVS + KeepAlived 搭建 负载均衡 高可用 集群

    为了实现服务的高可用和可扩展,在网上找了几天的资料,现在终于配置完毕,现将心得公布处理,希望对和我一样刚入门的菜鸟能有一些帮助. 一.理论知识(原理) 我们不仅要知其然,而且要知其所以然,所以先给大家 ...

  6. 为什么Java进程使用的RAM比Heap Size大?

    Java进程使用的虚拟内存确实比Java Heap要大很多.JVM包括很多子系统:垃圾收集器.类加载系统.JIT编译器等等,这些子系统各自都需要一定数量的RAM才能正常工作. 当一个Java进程运行时 ...

  7. [LeetCode]singleNumber

    题目:singleNumber Given an array of integers, every element appears twice except for one. Find that si ...

  8. 【linux】【jenkins】自动化运维七 整合sonarqube代码审查

    1.安装插件:SonarQube Scanner for Jenkins 插件安装教程参考:https://www.cnblogs.com/jxd283465/p/11542680.html 2.So ...

  9. 浅谈Linux进程管理

    一  查看系统进程 在linux中,查看系统进程的命令为ps,常用格式为如下两个: (1)ps aux:unix格式查看系统进程 (2)ps -le:linux格式查看系统进程 一般地,ps aux更 ...

  10. jenkins自动化部署项目6 --STMP服务发送邮件配置

    [系统管理]-[系统设置]里 1.系统管理员邮件地址:和后面SMTP配置认证的邮箱要一致,即发件箱   --------切记 2.配置SMTP认证的前提是需要在sohu搜狐邮箱开通SMTP/POP3, ...