Asp.Net Mvc 控制器详解
理解控制器
控制器的角色
(1)中转作用:控制器通过前面的学习大家应该知道它是一个承上启下的作用,根据用户输入,执行响应行为(动 作方法),同时在行为中调用模型的业务逻辑,返回给用户结果(视图)。
(2)中介角色:分离视图和模型,让视图和模型各司其职,控制器赋值二者交互。只负责数据传送,不负责处理。 控制器的两种角色可以用下图来表示:
深入理解控制器类
(1)观察控制器类的元数据。前面我们一直用控制器,在最初我们也简单说过控制器必须继承 Controller 类,那么 Controller 类究竟还有哪些密码需要我们进一步研究呢。其实我们通过查看 Controller 类的元数据,就能够发现, 如下图所示:
(2)ControllerBase 类需要理解的内容:
Controller 类位于 System.Web.Mvc 命名空间,继承自 ControllerBase 类。
ControllerBase 类实现了 Icontroller 接口的 Execute 方法,在路由器搜索到相应的控制器后,就会调用 Execute 方法进入控制器的处理。
(3)ControllerBase 的属性:
- ControllerContext:获取或设置控制器上下文。
- ValidateRequest:获取或设置一个值,该值指示伺服为此请求启用请求验证。
- ValueProvider:获取或设置控制器的值。
- ViewData:获取或设置数据的字典。
- ViewBag:获取动态视图数据字典。
- TempData:获取或设置可以在不同控制器之间传递数据的字典。
(4)ControllerBase 的任务:
- 定位:找到对应的动作方法。
- 获取参数:获取动作方法的参数。
- 处理错误:在执行动作方法中可能出现的错误。
- 渲染视图:提供默认的 WebFormViewFactory 类来渲染 ASP.NET 视图。
动作方法的参数
获取表单的常规方法
回顾我们以前在动作方法中获取 URL 和表单数据的方法,代码如下:
请大家思考,除了上面的方法外,还有没有更好的方法呢?答案是有的,请大家继续看下面的新方法。
动作方法参数的映射:
(1):)参数映射的基本使用: 其实,在 ASP.NET MVC 框架中,给我们提供了非常好的获取参数的方法,那就是可以 通过参数映射的形式直接获取参数值,而无需再像上面那样单独获取,只要我们提交的参数名称(比如文本框的 name)和我们定义的动作方法参数一致基本就可以,下面我们先看一个例子:
(2)参数映射的好处: 通过上面可以看出,我们省去了参数获取的步骤,这样在参数较多的时候开发效率是非常明 显的。
(3)动作方法参数映射来源,如下表所示:
(4)动作方法参数映射要求: 第一,参数名要和目标数据参数名一致(不区分大小写)。
值类型参数映射问题
(1)值类型参数有时候会失败,我们来做一个测试:比如,我们要求用户输入用户名和密码后登录,但是用户只是 输入了密码,没有输入用户名,那么提交后会有什么结果呢?
(2)原因分析:是因为 int 类型的参数,必须有数据才行,因为用户从视图中传递的数据是 null,而参数映射的时候 是无法将 null 转换成 int 类型。所以,大家明白这个道理,我们只需要将参数 int 类型定义成可为空类型 int?即可。
提示:虽然我们定义了可为空类型,但是并不是说我们开发中以后可以随便的使用带?的可为空类型,比如实际开 发中,我们肯定会有验证,也就是说用户登录 ID 是不会为 null 的,刚才我们只是这样一个测试,如果您在写项目 时,确实前端不需要验证,也允许为空,而后台是值类型,这时候就用刚才我们给出的方法解决是最完美的。
映射模型
(1)问题思考:前面的参数映射虽然解决了获取参数的麻烦,但是请思考,如果参数还是很多怎么办?如下图所示:
(2)解决思路:按照我们面向对象编程的规范,一个方法的参数是不能过多的,3-4 个就已经不少了,再多就不方便了, 于是我们在基础课程阶段告诉大家了解决方法,那就是使用实体类来解决!非常好,有了实体类,在动作方法中,我 们照样能够使用!
(3)使用模型映射的基本步骤:第一,获取表单数据;第二,自动构造实体模型,第三,调用业务逻辑方法并传递 模型。具体代码实现如下:
(4)模型映射的总结。通过上面的例子,您应该感觉出来了,模型自动映射最关键的就是参数的 name 一定要和实 体类的属性一致,因为在自动构造模型的时候,就是根据这个参数 name 对应到实体类的属性,这个非常关键。
动作方法特性与 ActionResult
动作方法同名问题
我们知道普通的方法可以同名,也就是使用重载的方法,那么一个控制器的动作方法能不能也重名呢,我们接下来 做一个测试,在一个控制器中添加两个同名的动作方法,然后运行程序,代码如下:
结果发现,路由不鞥呢根据方法参数去定位动作方法!那这个问题如何解决呢?我们继续往下研究、
动作方法的特性应用
(1)Http 请求谓词特性:针对请求类型定位方法,这样就能解决同名冲突问题,那我们先看特性的类型和具体使用 方法,如下图所示:
按照以上方法,再次运行程序就不会出现前面的问题了,因为两个不同的动作方法,会针对不同的请求来响应,这 样就不会争夺同一个请求。
(2)关于 Http 谓词:特点是经常使用,如果不加上该特性,默认动作方法接收所有谓词的请求,一般开发中都会加 上谓词,限定请求谓词类型。
(3)NonAction 特性:特点是可以将控制器中的方法声明为“非动作方法”,对于控制器内部使用的方法非常有用, 加上该特性后,外部不能访问,即可解决同名冲突问题。示例如下:
(4)ActionName 特性:特点是为动作方法“重新命名”,解决同名方法冲突问题,视图中使用时,需要修改成“重 新命名”后的方法。示例如下:
ActionResult 详解
(1)ActionResult 与 View()方法的关系:前面我们一直用 View()方法返回动作方法的值,返回值类型也是 ActionResult。那么究竟两者是何种关系呢?我们通过下图来看一下:
通过上面的分析,我们发现,View()方法重载了很多次,而且返回类型都是 ViewResult 对象,而不是 ActionResult 对象,那么我们继续研究两者个关系通过查看 MSDN 我们得出如下结论:那就是 ViewResultBase 继承 ActionResult,而 ViewResult 又继承 ViewResultBase,所以 ViewResult 还是 ActionResult 的子类,关系如下:
以上关系很清楚的能够用我们前面讲解的“里氏替换原则”来解释,父类作为方法的返回值类型,实际返回的是 子类对象,这正是面向对象三大特性之多态的体现。
(2)常用的内容输出类型。以下表格都我们开发中经常用到的,请大家先参考,后面我们用到的时候慢慢巩固:
(3)隐式动作类型。所谓隐式类型就是说动作方法的返回类型可以是 ActionResult 以外的类,示例如下:
提示:看似是其他数据类型,其实本质上 int 类型会自动转换成 ContentResult 类型,void 转换成 EmptyResult 类型。这点请大家务必记住。