一、奇葩的问题

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

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

<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. Tomcat性能调优参数简介

    近期,我们的一个项目进入了试运营的阶段,在系统部署至阿里云之后,我们发现整个系统跑起来还是比较慢的,而且,由于代码的各种不规范,以及一期进度十分赶的原因,缺少文档和完整的测试,整个的上线过程一波三折. ...

  2. 解决homebrew下install时出现的问题

    在homebrew下install   influxdb时出现error: Error: Failure while executing: git config --local --replace-a ...

  3. Python集训营45天—Day02

    目录 变量和运算符 1.1 初步介绍 1.2 使用案例 1.3 知识点梳理 1.4 练习 序言:这一章我们将学习变量以及常见的类型,我们将以案例和代码相结合的方式进行梳理,但是其中所有的案例和知识点 ...

  4. Mybatis使用入门,这一篇就够了

    mybatis中,封装了一个sqlsession 对象(里面封装有connection对象),由此对象来对数据库进行CRUD操作. 运行流程 mybatis有一个配置的xml,用于配置数据源.映射Ma ...

  5. LeetCode第七题

    Reverse digits of an integer. Example1: x = 123, return 321Example2: x = -123, return -321 Have you ...

  6. DNA sequence(映射+BFS)

    Problem Description The twenty-first century is a biology-technology developing century. We know tha ...

  7. GDB 调试指南

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,欢迎大家关注,二维码文末可以扫. 00 介绍 ...

  8. 网站启动,报编译错误:类型“ASP.global_asax”同时存在两个文件夹的问题

    CS0433: The type 'ASP.global_asax' exists in both 'c:\Windows\Microsoft.NET\Framework64\v4.0.30319\T ...

  9. 复习0824js

    编程思想: 面向过程:凡事亲力亲为,所有事情的过程都要清楚,注重的是过程. 面向对象:提出需求,找到对象,对象解决这个问题,我们要结果,注重的是结果. 面向对象的特性:封装,继承,多态: JS: 是一 ...

  10. Vue学习之vue中的v-if,v-show,v-for

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...