【翻译】ModelState - ASP.NET MVC 大揭秘

        科学上网找到的一篇关于ModelState的好文,翻译出来留作学习用,原文在此:The ModelState - ASP.NET MVC Demystified


ModelState - ASP.NET MVC 大揭秘

        你是否曾想过在你的 ASP .NET MVC 控制器中时常出现的ModelState是什么呢?和你一样,我也对它充满了好奇心。现在,就让我们通过这篇文章来分析一下究竟什么是ModelState,以及人们使用它的理由吧。

一、什么是ModelState?

        ModelState是Controller的一个属性,可以被继承自System.Web.Mvc.Controller的那些类访问。它表示在一次POST提交中被提交到服务器的 (1)键值对集合(a collection of name and value pairs),每个记录到ModelState内的值都有一个错误信息集。尽管ModelState的名字中含有“Model”,但它只有名称、值和错误集,与任何Model类都没有关系。
        ModelState有两个作用:存储提交到服务器的值,以及存储与之相关联的验证错误集。但这些都是生硬枯燥的解释,现在,让我们通过一些实例来看看它的效果吧。

二、准备一组Model、View与Controller

        在讲解实例之前我们需要先做一些准备工作。首先,我们先写一个AddUserVM视图模型:

// ViewModels/Home/AddUserVM.cs
public class AddUserVM
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string EmailAddress { get; set; }
}

        接下来,我们再设计一个简单的视图用于显示信息和收集数据:

<!-- Views/Home/Add.cshtml -->
@model ModelStateDemo.ViewModels.Home.AddUserVM
<h2>Add</h2>

@using(Html.BeginForm())
{
    <div>
        <div>
            @Html.TextBoxFor(x => x.FirstName)
        </div>
        <div>
            @Html.TextBoxFor(x => x.LastName)
        </div>
        <div>
            @Html.TextBoxFor(x => x.EmailAddress)
        </div>
        <div>
            <input type="submit" value="Save" />
        </div>
    </div>
}

        最后,我们还需要在控制器中添加新的Action(Controllers/HomeController.cs):

[HttpGet]
public ActionResult Add()
{
    AddUserVM model = new AddUserVM();
    return View(model);
}

[HttpPost]
public ActionResult Add(AddUserVM model)
{
    if(!ModelState.IsValid)
    {
        return View(model);
    }
    return RedirectToAction("Index");
}

        这样,当我们将表单提交给POST操作时,我们输入的所有值都将显示在AddUserVM实例中。 但是现在问题来了,这些数值是被怎么提交上去的呢?

三、ModelStateDictionary类

        让我们看一下Add页面的呈现HTML表单:

<form action="/Home/Add" method="post">    
    <div>
        <div>
            <label for="FirstName">First Name:</label>
            <input id="FirstName" name="FirstName" type="text" value="">
        </div>
        <div>
            <label for="LastName">Last Name:</label>
            <input id="LastName" name="LastName" type="text" value="">
        </div>
        <div>
            <label for="EmailAddress">Email Address:</label>
            <input id="EmailAddress" name="EmailAddress" type="text" value="">
        </div>
        <div>
            <input type="submit" value="Save">
        </div>
    </div>
</form>

        我们可以看到,在POST中,<input>标签中的所有值都将会作为键值对提交给服务器。 当MVC收到POST时,它会获取所有POST参数并将它们添加到ModelStateDictionary实例中。 在Visual Studio中进行调试时,我们可以通过“局部变量”窗口来查看此实例(打开“局部变量”的方法:开启调试后点击菜单栏的“调试” -> 选择“窗口” -> 点击“局部变量”):
modelstatedictionary        从上图中我们可以看出,ModelStateDictionary的Values属性中含有System.Web.Mvc.ModelState的实例。那么ModelState里究竟包含着什么呢?

四、ModelState里究竟有什么

        同样是刚才那个窗口,让我们展开Value里的属性看看:
ModelStateDictionary        我们可以发现,每一个属性里都包含有一个ValueProviderResult的实例,并且每个实例中都有一个提交给服务器的实际值。当我们通过POST提交数据的时候,(2) MVC会自动帮我们生成这些实例,并且在POST操作中输入会与提交上来的值一一映射形成键值对。 从本质上讲,MVC将用户的输入包装到对服务器而言更加友好的类(ModelState和ValueProviderResult)中以便于使用。
        但这些还不是ModelState的全部,还有两个重要属性我们没有讨论:ModelState.Errors属性和ModelStateDictionary.IsValid属性。我们之前说过,ModelState有两个用途,而这两个属性则正是用于ModelState的第二个用途:存储在提交的值中找到的错误。

五、ModelState中的错误信息集

        我们稍微修改下AddUserVM中的内容(ViewModels/Home/AddUserVM.cs):

public class AddUserVM
{
    [Required(ErrorMessage = "Please enter the user's first name.")]
    [StringLength(50, ErrorMessage = "The First Name must be less than {1} characters.")]
    [Display(Name = "First Name:")]
    public string FirstName { get; set; }

    [Required(ErrorMessage = "Please enter the user's last name.")]
    [StringLength(50, ErrorMessage = "The Last Name must be less than {1} characters.")]
    [Display(Name = "Last Name:")]
    public string LastName { get; set; }

    [EmailAddress(ErrorMessage = "The Email Address is not valid")]
    [Required(ErrorMessage = "Please enter an email address.")]
    [Display(Name = "Email Address:")]
    public string EmailAddress { get; set; }
}

        我们在原先的基础上添加了一些验证属性,在这个例子中,我先加入了 (3)Requiered、StringLength以及Display三个属性作为示范。同时,我还为它们设置了相应的错误信息,这样当验证错误出现时,这些提示信息将会被显示在页面上。
        完成上述更改后,让我们修改“添加”视图以显示错误消息(Views/Home/Add.cshtml):

@model ModelStateDemo.ViewModels.Home.AddUserVM

<h2>Add</h2>

@using(Html.BeginForm())
{
    @Html.ValidationSummary()
    <div>
        <div>
            @Html.LabelFor(x => x.FirstName)
            @Html.TextBoxFor(x => x.FirstName)
            @Html.ValidationMessageFor(x => x.FirstName)
        </div>
        <div>
            @Html.LabelFor(x => x.LastName)
            @Html.TextBoxFor(x => x.LastName)
            @Html.ValidationMessageFor(x => x.LastName)
        </div>
        <div>
            @Html.LabelFor(x => x.EmailAddress)
            @Html.TextBoxFor(x => x.EmailAddress)
            @Html.ValidationMessageFor(x => x.EmailAddress)
        </div>
        <div>
            <input type="submit" value="Save" />
        </div>
    </div>
}

        请注意我们现在使用的两个帮助信息控件 ValidationSummary 和 ValidationMessageFor。ValidationSummary控件将会读取模型中所有属性的错误信息摘要并显示在一个项目符号列表中;而ValidationMessageFor则只显示其指定属性的错误信息摘要。
        现在让我们看看当我们尝试提交缺少电子邮件地址的无效POST时会发生什么。当我们在调试中进入POST操作时,我们的ModelStateDictionary中出现了以下内容:
ErrorsCollection        注意,电子邮件属性的ModelState实例中的Errors集合现在记录了一个错误。当MVC为提交上来的属性创建ModelState时,它会遍历视图模型中的每一个属性及其相关联验证属性。如果有任何错误被发现,MVC就会将错误添加到ModelState的错误信息集合中。同时,我们还能注意到的是:isVaild这一属性现在也是false了。这是因为现在有错误出现了,而对于isVaild而言,只要有任何提交上来的属性存在错误,它就会变为false。
        以上种种设置验证的手段,都是为了让我们设计的MVC可以以其设计的方式正常运作。ModelState存储提交的值,允许它们映射到类属性(或作为操作的参数),并为每个属性保留一组错误消息。在一些简单的环境下,这些能在后台自行处理的验证手段正是我们所需要的。

六、自定义验证

        那么现在问题来了,当我们需要采用更加复杂的验证手段时,我们该怎么办呢?举个例子,假设我们需要验证名字和姓氏是否相同,并在发生这种情况时显示特定的错误消息,此时我们应该如何设计验证方式呢?方法还是有的,我们实际上可以通过ModelStateDictionary上的AddModelError方法向模型状态添加错误:

[HttpPost]
public ActionResult Add(AddUserVM model)
{
    if(model.FirstName == model.LastName)
    {
        ModelState.AddModelError("LastName", "The last name cannot be the same as the first name.");
    }
    if(!ModelState.IsValid)
    {
        return View(model);
    }
    return RedirectToAction("Index");
}

        AddModelError方法的第一个参数是需要显示错误信息的属性的名称。还是用刚才那个例子,我们将第一个参数设置为“LastName”。 如果你只希望它出现在ValidationSummary而不是ValidationMessage中,你可以将其设置为空(或用一个假名)。
        现在错误将直接显示在页面上:
AddModelError

七、总结

        ModelState代表着POST提交中上传上来的值和错误信息,其验证过程必须遵守开发者指定的验证规则(像[Required]和[EmailAddress]这样的)。只要我们想,我们也可以自己定义错误信息。使用ValidationSummary和ValidationMessageFor则可以直接从ModelState读取错误信息内容并展示给用户看。更多的相关信息,我推荐你们查阅 Professional ASP.NET MVC 5,特别是第6章,里面详细介绍了使用ModelState的验证方法。我还在Github上有一个非常简单的示例项目,它演示了ModelState的工作原理,并提供了这篇文章中的所有代码和标记,推荐你们看一看!
        希望各位看的开心!Happy Coding!


(1) 原文里按直译应当写作“名称与提交值对”,但从ModelState的继承形式来看,ModelState应当是一个集合。它同时继承了IConnection、IDictionary、IEnumerable等多个接口,内部含有一个键值对集合和一个与之相对应的错误信息集合,所以我将它翻译成了键值对。


(2) 这里的原句是 “MVC creates all of these instances automatically for us when we submit a POST with data, and the POST action has inputs that map to the submitted values.”。ModelState里value与name是根据model一一对应的,value即为提交上来的数值,name表示model的属性名,在view中体现为<input>标签的id。因为ModelState本身就有个键值对的缘故所以我认为作者在这里想表达的应该是说:将与input的id对应的“键”和用户输入的“值”一一对应起来,这也符合ModelState键值对集合的本质。


(3) 这三者应该都是MVC的验证特性,[Required]表示该参数或属性为必填项,即该项的输入不能为 NULL;[StringLength]用来验证字符串属性值是否未超过指定长度限制;[Display]我没有找到官方描述,但从效果上看可能是以文本的形式显示属性说明的,如果有dalao找到了官方描述的,请务必在评论区中指点一二。

推荐链接:ModelState Class (System.Web.Mvc)

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ASP.NET MVC 5是一种基于MVC设计模式的Web应用程序框架,它能够帮助开发人员快速地构建高性能、可扩展、易于维护的Web应用程序。ASP.NET MVC 5的源代码是开放的,可以供开发人员学习和使用。 ASP.NET MVC 5的源代码包括了许多组件,如控制器、视图、路由、模型绑定、过滤器等,每个组件为开发人员提供了丰富的功能和灵活的扩展性。例如,控制器是ASP.NET MVC 5应用程序的核心组件之一,它包含了Action(处理客户端请求的方法)和ActionResult(请求的结果)等。视图是另一个重要的组件,它负责呈现数据并与用户交互。路由是整个应用的核心,负责将请求路由到对应的控制器和Action。 ASP.NET MVC 5的源代码还包含了许多其他的实用工具和库,如ASP.NET Identity(用户认证和授权)、Microsoft.AspNet.Web.Optimization(前端资源优化)、Web API(RESTful服务)等,这些工具和库能够帮助开发人员构建更加丰富和复杂的Web应用程序。 总之,ASP.NET MVC 5的源码是开放的,它提供了丰富的组件、实用的工具和库,可以帮助开发人员快速构建高性能、可扩展和易于维护的Web应用程序。开发人员可以通过学习源代码来了解框架的内部实现和功能特性,并逐步提升自己的开发技能。 ### 回答2: ASP.NET MVC 5是一种使用MVC模式(Mode-View-Controller)的Web应用程序框架。它是基于.NET Framework的,可以用于创建高性能、可扩展、安全的Web应用程序。 以下是ASP.NET MVC 5框架的主要源码揭秘: 1. Routing:路由是ASP.NET MVC应用程序中的一个重要概念。它用于将URL映射到特定的控制器和操作方法。MVC 5框架中的路由引擎根据请求的URL和路由模板来确定请求应该由哪个控制器和操作方法处理。 2. Controller:控制器是MVC模式中的一个组成部分,它负责处理用户请求并生成响应。在MVC 5框架中,控制器有许多基础类和接口,如Controller类、IController接口等。 3. View:视图是控制器生成响应的一部分,它将模型数据呈现为HTML页面。在MVC 5框架中,使用各种视图引擎来呈现视图,如Razor视图引擎、ASPX视图引擎等。 4. Model:模型是MVC模式中的最后一个组成部分,它在控制器和视图之间传递数据。在MVC 5框架中,使用各种模型绑定器来将请求数据绑定到模型。 5. Bundling and Minification:Bundling和Minification是ASP.NET MVC中常用的技术,用于减少页面加载时间和资源使用。在MVC 5框架中,通过使用System.Web.Optimization命名空间中的类来添加和管理JS、CSS和其他资源的捆绑和缩小。 总之,ASP.NET MVC 5是一种功能强大的Web应用程序框架,它实现了MVC模式并提供了许多特性和工具来帮助开发人员构建高性能、可扩展、安全的Web应用程序。通过深入了解和分析MVC 5框架的源代码,开发人员可以更好地理解其内部工作原理,从而更有效地使用和扩展它。 ### 回答3: ASP.NET MVC 5是一种用于构建Web应用的框架,它使用模型-视图-控制器(MVC)的设计模式。本文将介绍ASP.NET MVC 5的源代码。 ASP.NET MVC 5的源代码主要分为几个部分:框架核心、路由、控制器、视图和模型。 框架核心是ASP.NET MVC 5的核心部分,提供了MVC框架的基本功能。其中包括建立请求管道、控制器工厂和视图引擎等关键组件。框架核心的代码是相当庞大的,包含了许多类、接口和扩展方法。在了解整个框架的实现细节时,框架核心是一个重要的起点。 路由是控制请求流程的重要组成部分。ASP.NET MVC 5使用的是基于URL的路由,它将请求URL映射到相应的控制器和操作上。路由的源代码中,关键部分是路由表的维护和查询机制。在了解路由的实现细节时,了解路由表的数据结构和查询算法是必不可少的。 控制器是MVC框架中的核心组件,它负责处理请求并返回响应。控制器的实现代码通常非常简单,主要是一些Action方法的定义和调用。控制器的源代码中,需要关注的是Action的参数绑定、Model验证和ActionResult的实现细节等。 视图是MVC框架中的UI组件,它通过生成HTML代码来渲染页面。视图的源代码中,关键部分是视图引擎的实现,它根据视图的文件名和模板来生成HTML代码。在了解视图的实现细节时,视图引擎是一个重要的组成部分。 模型是应用程序中的数据模型,通常与数据库中的数据结构相关。模型的源代码中,重点是数据访问层和实体类型的定义。在了解数据模型的实现细节时,了解数据访问层的实现和实体类型的定义是必要的。 总之,ASP.NET MVC 5的源代码非常庞大,也非常复杂。要彻底掌握它,需要对框架的核心、路由、控制器、视图和模型等组成部分进行深入研究。同时,我们也需要了解C#编程语言和.NET平台的底层机制。只有这样,才能真正揭秘ASP.NET MVC 5的源代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值