ASP.NET MVC的Model Binding


参考:https://github.com/MVCAppDesignAndDevelop/MVC5Book.git

当View传递一个参数给Action的时候,ASP.NET MVC使如何获取该值的呢?

1.Request对象

如ASP或Web Forms的Request对象,ASP.NET MVC使用Request.QueryString[“name”]或Request.Form[“name”]的方式来接受数据,如下:

//Action
public ActionResult DemoQueryString()
{
	//参数名称与参数值通过URL方式传递,如:/VtoC/DemoQueryString?id=5
    ViewBag.id = int.Parse(Request.QueryString["id"]);
    //通过窗体方式且<form>的method属性为post,就改用Request.Form["id"]
    //ViewBag.id = Request.Form["id"];
    return View();
}

//View
@{
    ViewBag.Title = "DemoQueryString";
}
<h2>DemoQueryString</h2>
<p>
    ID:@ViewBag.id
</p>

使用Request对象接受数据,在数据字段多时编写代码费时且容易出错

2.使用RouteData

通过路由模板获取参数值,以默认路由“/{Controller}/{Action}/{id}”为例,如下:

//Action
public ActionResult DemoRouteData(int id)
{
	ViewBag.id = id;
    return View();
}

//View
@{
    ViewBag.Title = "DemoRouteData";
}
<h2>DemoRouteData</h2>
<p>
    ID:@ViewBag.id
</p>

使用路由/VtoC/DemoRouteData/5时,可以发现5会被解析成id参数的值,它不像Request对象取值时是不具类型的,假如传递了非整数类型就会报错,如下:
在这里插入图片描述

3.简单Model Binding

路由方式,与用户端互动以窗体居多,Model Binding主要通过DefaultModelBinder类进行自动化数据转换工作,它非常依赖<from>表单域的name属性名称。当View传递进来的字段名与Action方法参数名称不相符时,参数值会设置为null,若参数值不可为null则弹出例外错误,如下:

//Action
public ActionResult BasicModelBindingByModel(string name)
{
	ViewData.Model = name;
    return View();
}

//View
@{
    ViewBag.Title = "BasicModelBindingByModel";
}
<h2>BasicModelBindingByModel</h2>
<div>
    @using (Html.BeginForm())
    {
        <p>
            姓名:<input type="text" name="name" />
               <input type="submit" value="提交" />
        </p>
    }
</div>
<p>
    Your Name:@Model
</p>

在这里插入图片描述ViewData.Model只有单一值,在ViewPage中直接使用@Model读取即可。传递少量字段使用Action参数方式不错,但字段一多在Action方法加多个参数就不合适了,所以可以使用FormCollection类,如下:

//Action
public ActionResult DemoFormCollection(FormCollection form)
{
	ViewBag.Name = form["name"];
    ViewBag.Age = form["age"];
    return View();
}

//View
@{
    ViewBag.Title = "DemoFormCollection";
}
<h2>DemoFormCollection</h2>
<div>
    @using (Html.BeginForm())
    {
        <p>
            姓名:<input type="text" name="name" value="" /><br/>
            年龄:<input type="text" name="age" value="" />
               <input type="submit" value="提交" />
        </p>
    }
</div>
<p>
    Your Name:@ViewBag.Name<br/>
    Your Age:@ViewBag.Age
</p>

在这里插入图片描述
FormCollection解决了需要在Action方法中设置多个参数的问题,不过由于是字典的使用方式,本质上和Request是一样的,数据本身没有类型且错误几率还是较高的

4.复杂的Model Binding

DefaultModelBinder类还能处理更复杂的窗体数据与.NET Framework类型之间对应的处理关系,如下:

//在Models目录下新建一个Person类
namespace WebApplication1.Models
{
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

//Ation
public ActionResult PersonModelBinding(Person person)
{
	//ViewData.Model = person;
    //return View();
    return View(person);
}

//View
@{
    ViewBag.Title = "PersonModelBinding";
}
<h2>PersonModelBinding</h2>
<div>
    @using (Html.BeginForm())
    {
        <p>
            姓名:<input type="text" name="name" value="" /><br />
            年龄:<input type="text" name="age" value="" />
            <input type="submit" value="提交" />
        </p>
    }
</div>
<p>
    Your Name:@Model.Name<br />
    Your Age:@Model.Age
</p>

在这里插入图片描述
由Model和View可知,名称映射时不区分大小写,窗体传过来的数据一律是string类型,DefaultModelBinder类会将string类型进行转换,如Person.Age的定义是int。DefaultModelBinder也具备处理多个Model的情况,如:

//Action
public ActionResult MutiPersonModelBinding(Person man,Person woman)
{
	ViewBag.ManName = man.Name;
    ViewBag.ManAge = man.Age;
    ViewBag.WomanName = woman.Name;
    ViewBag.WomanAge = woman.Age;
    return View();
}

//View
@{
    ViewBag.Title = "MutiPersonModelBinding";
}
<h2>MutiPersonModelBinding</h2>
<div>
    @using (Html.BeginForm())
    {
        <p>男生</p>
        <p>
            姓名:<input type="text" name="man.name" value="" /><br />
            年龄:<input type="text" name="man.age" value="" />
        </p>
        <p>女生</p>
        <p>
            姓名:<input type="text" name="woman.name" value="" /><br />
            年龄:<input type="text" name="woman.age" value="" />
        </p>
        <input type="submit" value="提交" />
    }
</div>
<div>
    Man Information:<br />
    Your Name:@ViewBag.ManName<br />
    Your Age:@ViewBag.ManAge
</div>
<div>
    Woman Information:<br />
    Your Name:@ViewBag.WomanName<br />
    Your Age:@ViewBag.WomanAge
</div>

在这里插入图片描述
注意View中的字段名使用“名称.属性”方式,要和Action方法内访问对象的方法一致,再例如:

//新增一个PersonViewModel类(当然需要在Models目录下新建一个ViewModels)
namespace WebApplication1.Models.ViewModels
{
    public class PersonViewModel
    {
        public Person Man { get; set; }
        public Person Woman { get; set; }
    }
}

//Action方法需要两个,一个给HTTP GET来呈现窗体,一个给HTTP POST来接受PersonViewModel类型数据
public ActionResult ViewModelModelBinding()
{
	return View();
}
[HttpPost]
public ActionResult ViewModelModelBinding(PersonViewModel person)
{
	return View("ShowViewModelModelBinding", person);
}

//View需要引用@model WebApplication1.Models.ViewModels.PersonViewModel,如下图
@model WebApplication1.Models.ViewModels.PersonViewModel
@{
    ViewBag.Title = "ViewModelModelBinding";
}
<h2>ViewModelModelBinding</h2>
<div>
    @using (Html.BeginForm())
    {
        <p>男生</p>
        <p>
            姓名:<input type="text" name="person.man.name" value="" /><br />
            年龄:<input type="text" name="person.man.age" value="" />
        </p>
        <p>女生</p>
        <p>
            姓名:<input type="text" name="person.woman.name" value="" /><br />
            年龄:<input type="text" name="person.woman.age" value="" />
        </p>
        <input type="submit" value="提交" />
    }
</div>
//View需要引用@model WebApplication1.Models.ViewModels.PersonViewModel,如下图
@model WebApplication1.Models.ViewModels.PersonViewModel
@{
    ViewBag.Title = "ShowViewModelModelBinding";
}
<h2>ShowViewModelModelBinding</h2>
<div>
    Man Information:<br />
    Your Name:@Model.Man.Name<br />
    Your Age:@Model.Man.Age
</div>
<div>
    Woman Information:<br />
    Your Name:@Model.Woman.Name<br />
    Your Age:@Model.Woman.Age
</div>

View需要引用@model WebApplication1.Models.ViewModels.PersonViewModel,如下图:
在这里插入图片描述在这里插入图片描述
HttpPost的ViewModelModelBinding接受ViewModel数据传递至ShowViewModelModelBinding呈现,如下:
在这里插入图片描述在这里插入图片描述

5.JSON Model Binding

6.ModelBinder扩展

Model Binding默认来源有以下4种:

  • QueryString(指URL,而非Request.QueryString对象)
  • Form窗体
  • RouteData
  • JSON

如果需要扩展来源,ASP.NET MVC提供了ValueProviderFactory抽象类和IValueProvider接口来自定义Model Binding的来源,就可以将所需要的数据源进行Model Binding,而不用在Action方法中手动设置,例如,Cookie或HTML 5的LocalStorage等:

//Models目录下新建一个CookieValueProviderFactory类
using System.Globalization;
using System.Web.Mvc;

namespace WebApplication1.Models
{
    public class CookieValueProviderFactory : ValueProviderFactory
    {
        public override IValueProvider GetValueProvider(ControllerContext controllerContext)
        {
            // 将其中一个Cookie值加入Model Binding中
            var source = controllerContext.RequestContext.HttpContext.Request.Cookies["Datas"].Values;
            // 因为Cookie也是NameValue格式,所以使用内建的Provider
            return new NameValueCollectionValueProvider(source, CultureInfo.CurrentCulture);
        }
    }
}

//Global.asax中将CookieValueProviderFactory注册进ValueProvierFactories中
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using WebApplication1.Models;

namespace WebApplication1
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            //注册
            ValueProviderFactories.Factories.Add(new CookieValueProviderFactory());
        }
    }
}
版权声明:本文为CGS_______原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/CGS_______/article/details/103218827