[MVC]自定义模型绑定器,从表单对模型进行赋值
一、奇葩的问题
之前自己造轮子的时候,遇到一个很奇怪的问题,虽然需求很奇葩,但是还是尝试解决了一下
当提交的表单里包含多个重复名称的字段的时候,例如
<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]自定义模型绑定器,从表单对模型进行赋值的更多相关文章
- 值提供器 AND 模型绑定器
本章介绍了值提供器的作用,ASP MVC自带的5中值提供器.以及模型绑定器的作用,自定义模型绑定器并使用自定义的模型绑定器(类型上加上[ModelBinder(typeof(xx))]或者在全局模型绑 ...
- 玩转Django2.0---Django笔记建站基础七(表单与模型)
第七章 表单与模型 表单是搜集用户数据信息的各种表单元素的集合,作用是实现网页上的数据交互,用户在网站输入信息,然后提交到网站服务器端进行处理(如数据录入和用户登录.注册等). 用户表单是web开发的 ...
- JavaScript:表单验证模型
之前做的验证提示以弹框的形式出现太丑陋了,不符合标准的验证提示.如果要想进行更好的数据验证操作,那么必须进行一些模块化设计,通过表单样式的改变来提示.其实,一般的数据验证无非就是那么几种,例如: 大多 ...
- Atitit vue.js 把ajax数据 绑定到form表单
Atitit vue.js 把ajax数据 绑定到form表单 1.1. 使用场景:主要应用在编辑与提交场合..1 1.2. 绑定数据到form控件,可以使用jquery,不过vue.js更加简单1 ...
- `<jsp:getProperty>`动作和`<jsp:setProperty>`动作的使用在一个静态页面填写图书的基本信息,页面信息提交给其他页面,并且在其页面显示。要去将表单元素的值赋值给Java
<jsp:getProperty>动作和<jsp:setProperty>动作的使用 1.<jsp:getProperty>动作 语法格式: <jsp:get ...
- asp.net mvc 模型验证注解,表单提交
一.添加模型 public class Account { public int ID { get; set; } [Display(Name = "姓名")] //设置要显示的字 ...
- activiti自定义流程之整合(三):整合自定义表单创建模型
本来在创建了表单之后应该是表单列表和预览功能,但是我看了看整合的代码,和之前没有用angularjs的基本没有什么变化,一些极小的变动也只是基于angularjs的语法,因此完全可以参考之前说些的表单 ...
- [Django]下拉表单与模型查询
前言:本文主要针对自定义下拉表单制作,下拉表单的内容是取至于数据库,即动态实现下拉表单 正文: 动态实现下拉表单有两种方法: 一.自己手动写 html 模板中的 <form ...> &l ...
- struts表单域模型注入
表单使用struts标签,表单中每一个字段都可以这样来赋值 类(action).成员变量 这个叫域模型注入 <s:form action="orders" method=&q ...
随机推荐
- redis数据库学习
0 使用理由 0.1 高性能 纯内存操作,比在硬盘操作数据的速度有极大提升 0.2 高并发 承受请求比直接操作数据库大得多 0.3 单线程 至于redis单线程的原因.有点意思.CPU不是Redis的 ...
- NetCore下的HTTP请求IHttpClientFactory
使用方式 IHttpClientFactory有四种模式: 基本用法 命名客户端 类型化客户端 生成的客户端 基本用法 在 Startup.ConfigureServices 方法中,通过在 ISer ...
- Unity - 2D中的物理关节
本文概述: 分析Unity中几个2D物理关节组件的基本功能.使用方法.运用场景等 开发环境:Unity2019.3.0a2 / VS2017 项目资源包: 2D Joints Starter 说明: ...
- Vue之render渲染函数和JSX的应用
一.模板缺陷 模板的最大特点是扩展难度大,不易扩展.可能会造成逻辑冗余 <Level :type="1">哈哈</Level> <Level :typ ...
- centos7 kubernetes单机安装
单机版的kubernetes 适合初学者,对kuber有个很好的入门. 因为centos系统内置了安装源.我们可以直接安装 1.yum install -y etco kubernetes 2.whe ...
- springboot统一ajax返回数据格式,并且jquery ajax success函数修改
服务端代码: package com.zhqn.sc.cfg; import org.springframework.core.MethodParameter; import org.springfr ...
- Nginx实现高可用(了解)
使用nginx实现反向代理和负载均衡时,nginx就是整个网站的入口了,所以需要保证nginx的高可用 主要资料包:链接:https://pan.baidu.com/s/1z_-xEM3uUICtZi ...
- 前端-HTML-web服务本质-HTTP协议-请求-标签-01(待完善)
目录 前端 什么是前端 什么是后端 学习流程 前端三剑客的形容 web服务的本质 测试--浏览器作为客户端向服务器发起请求 浏览器输入网址回车发生了几件事 ***** HTTP协议(超文本传输协议) ...
- 2019年全国新课标I卷文理科数学LaTeX排版试题与解析
整体分析,没有偏怪难题之分,中等题偏多,题目较往年有题型改动变化,但难度还称不上很难.具体内容贴上链接! https://mp.weixin.qq.com/s/WKXhCKI_-z3UT-zUwI23 ...
- Jenkins教程(五)构建Java服务Docker镜像
本文主旨 主要记录下如何使用Jenkins构建Java服务的Docker镜像,以及手动部署测试下 前期准备 已安装Jenkins 为jenkins用户添加到docker组内 本地装有maven,配置或 ...