Controller作为持久层和展现层的桥梁, 封装了应用程序的逻辑,是MVC中的核心组件之一。

本篇文章我们就来谈谈 Controller, 主要讨论两个方面:

  • Controller运行机制简介
  • Controller数据传递方式

Controller运行机制简介

实现自定义的Controller

我们自己要实现一个控制器有两种方法:

一种是继承IController接口,一种是继承Controller或ControllerBase.

Controller继承了ControllerBase, 另外Controller和ControllerBase本身也继承了IController,总之需要实现IController接口.

首先我们到XEngine中随便打开一个Controller, 例如AccountController,

可以看到新建Controller时,脚手架帮我们继承了Controller类

我们逐级查看,如下几张图,右键 转到Controller定义--> 查看ControllerBase定义 --> 查看IController定义,可以看到Controller需要实现IController中Execute方法。





接下来,我们就新建一个类MyCustomController,继承IController接口,实现Execute方法。

namespace XEngine.Web.Controllers
{
public class MyCustomController:IController
{
public void Execute(RequestContext requestContext)
{
requestContext.HttpContext.Response.Write("Hello world.");
}
}
}

运行http://localhost/XEngine/mycustom,可以看到浏览器输出了Hello world.

MVC框架将实现IController接口的类当作一个控制器,根据路由规则将请求发送给它。

上例中,我们使用到了RequestContext的HttpContext属性,该属性用来获取有关HTTP请求的信息。

RequestContext另外还有一个属性RouteData,用来获取请求路由的信息,例如可以通过如下方式可以获得controller和action的名称:

requestContext.RouteData.Values["controller"].ToString();

requestContext.RouteData.Values["action"].ToString();

实现IController接口的控制器需要负责处理请求的各个方面,包括生成对客户端的响应。

实际应用中我们像脚手架一样直接继承System.Web.Mvc.Controller就可以了,这种方式我们就不需要自己实现Execute方法来输出内容了,可以通过MVC Framework的action results来解决这个问题。

我们先举个例子,看看我们原来一直使用的ActionReslut生成响应的方式,例如

public ActionResult NativeOutput()
{
return Redirect("~/Account/Login");
}

Action 方法不直接使用Response对象,而是返回ActionResult类型的对象。ActionResult类描述了response的类型,比如返回一个view或跳转到另外一个页面。

当MVC Framework从一个action方法接收一个ActionResult对象时,会调用那个对象的ExecuteResult方法。

namespace System.Web.Mvc
{
// 摘要:
// 表示操作方法的结果。
public abstract class ActionResult
{
// 摘要:
// 初始化 System.Web.Mvc.ActionResult 类的新实例。
protected ActionResult(); // 摘要:
// 通过从 System.Web.Mvc.ActionResult 类继承的自定义类型,启用对操作方法结果的处理。
//
// 参数:
// context:
// 用于执行结果的上下文。上下文信息包括控制器、HTTP 内容、请求上下文和路由数据。
public abstract void ExecuteResult(ControllerContext context);
}
}

我们通过一个自定义的ActionResult实现来演示工作机制, 模拟实现这个简单的跳转功能, ExecuteResult实现如下:

namespace XEngine.Web.Utility
{
public class CustomRedirectResult:ActionResult
{
public string Url { get; set; }
public override void ExecuteResult(ControllerContext context)
{
string fullUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext);
context.HttpContext.Response.Redirect(fullUrl);
}
}
}

在Controller中使用

public CustomRedirectResult CustomOutput()
{
return new CustomRedirectResult { Url = "~/Account/Login" };
}

可以看到,实现了同样的效果。

内置的action result 类型

类似于我们实现的CustomRedirectResult,MVC框架包含一些内置的action result类型,所有这些类型都继承于ActionReslut类型。如下列表:

https://msdn.microsoft.com/en-us/library/system.web.mvc.actionresult.aspx

我们具体使用时可以明确指明返回类型,如

public ViewResult xxx()
{ xxx }

或统一返回 ActionResult, 如

public ActionResult xxx()
{ xxx }

一般我都是笼统的返回 ActionResult,这样比较方便。(另外具体实现时,一个Action也可能根据不同情况返回不同种类的ActionResult,没办法明确返回类型)

上面这张表格, HttpStatusCodeResult、 HttpUnauthorizedResult、 EmptyResult这三个Action Result是没有Helper Method的.

类似于我们自定义的CustomRedirectResult,使用时需要使用字面量来明确返回结果。下面看例子:

可以使用HttpStatusCodeResult 类将一个特定的HTTP状态码发送给浏览器。下面看下HttpStatusCodeResult的例子,返回特定的HTTP结果码:

这个类没有具体的控制器辅助方法,因此必须对这个类进行实例化。

public HttpStatusCodeResult StatusCode()
{
return new HttpStatusCodeResult(404, "URL cannot beserviced");
}

401和404是HttpStatusCodeResult的两个特例:

可以使用HttpNotFoundResult类取得上面的404效果

public HttpStatusCodeResult NotFoundStatusCode()
{
return HttpNotFound();
}

发送401结果,通常是把用户重定向到认证页面

public HttpStatusCodeResult UnauthorizedStatusCode()
{
return new HttpUnauthorizedResult();
}

可以看到,运行后跳转到认证页面

数据的传递方式

我们讨论下一话题:MVC中常用的传递数据方式。(传统的Session, Cookie传递方式还可以继续用,就不再做介绍了)

我们使用到的数据传递主要有 view到controller, controller到view, 跨view间的数据传递三种。下面我们分别加以说明。

一、 Controller接收View数据

Controller 经常需要访问来自输入请求的数据,如查询字符串值、表单值,以及路由系统根据输入URL解析所得到的参数。访问这些数据有两个主要途径:

1、通过context(和ASP.NET 之前版本的技术类似,如我们熟悉的Request)

2、通过action方法的参数(包括模型绑定),(MVC框架自动检查上下文给这些参数赋值)

这两个方式都很常用,我们来依次讲解。

通过 Context获取数据

当我们通过继承ControllerBase类创建controller时, 我们可以利用context对象的一组属性来获取请求的相关信息, 如Request, Response, RouteData, HttpContext和Server.

常用的归纳如下表:

这些使用方法有些之前的文章已经介绍过,其他的在用到时再介绍,就不重复说明了。

通过action方法的参数(利用模型绑定)

通过参数的方法可读性更好。

如下的重写例子,我们先用context读取表单值,再改写成参数方式读取。

先定义一个View

@{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>GetDataFromView</title>
</head>
<body>
<h1>@ViewBag.Name</h1>
<form method="post" >
<input name="name" value="Tony"/>
<input type="submit" value="提交表单" />
</form>
</body>
</html>

通过context读取

public ActionResult GetDataFromView()
{
ViewBag.Name = Request.Form["name"];
return View();
}

改成通过参数读取

public ActionResult GetDataFromView(string name)
{
ViewBag.Name = name;
return View();
}

点击按钮后,均返回如下页面:

通过自动检查上下文对象和属性,MVC框架会给action method 参数提供值,这些对象包括Request.QueryString, Request.Form 和 RouteData.Values

模型绑定是MVC推荐的方式,个人感觉可以使代码更加干净。原理是通过Value Provider  Model Binder 两个组件。

有一组内建的 Value Provider,它们会抓取Request.Form, Request.QueryString, Request.Files以及 RouteData.Values的数据项,然后将这些值传递给Model Binder,尝试将这些数据映射为action method参数的数据类型。

当然参数也可以是一个model, 这种方式前面文章已经使用多次,不再重复举例。

二、Controller传递数据到View

直接将对象作为View的参数即可(即传递一个view model object)。

public ActionResult DateOutput()
{
DateTime date = DateTime.Now;
return View(date);
}
在View中使用Model关键字来访问
@{
ViewBag.Title = "Index";
} <h2> DateOutput </h2> The day is: @(((DateTime)Model).DayOfWeek)

这种视图是无类型视图。该视图不知道关于视图模型的任何情况,而把它作为object的一个实例来看待。

可以通过创建强类型视图来明确model类型,在强类型视图中包含视图模型对象类型的详细信息。

@model DateTime

@{
ViewBag.Title = "Index";
} <h2>DateOutput</h2> The day is: @Model.DayOfWeek

注意:指定模型类型是需要小写的 m, 读取时用大写的 M

使用ViewBag

之前也用过多次,不再赘述。

个人认为ViewBag最大的一个优点是它便于将多个对象发送给视图。

三、跨请求传递

前面两种请求方式都在一轮请求应答中。

还有一种跨请求的情况,例如重定向导致浏览器递交新的HTTP请求。

如果需要将一个请求的数据传递到下一个请求,这种情况可以使用TempData.

使用时直接按Session一样的语法就可以了。

TempData和Session的区别是,当读取TempData值时,值就会被标记为待删除,

当请求结束后就会被删除。

有两个小技巧:

1、利用Peek方法,可以得到TempData的值,而不把它标记为删除

DateTime time = (DateTime)TempData.Peek("Date");

2、利用Keep方法,可以保留一个将被删除的值

TempData.Keep("Date");

Keep方法不会永久保护一个值。如果这个值被再次读取,它将被再次标记为删除。

总结

关于Controller需要了解常用的ActionResult类型,掌握数据的传递的几种方式。

欢迎大家多多评论,祝 学习进步:)

MVC 5 + EF6 完整教程16 -- 控制器详解的更多相关文章

  1. MVC+EF Core 完整教程20--tag helper详解

    之前我们有一篇:“动态生成多级菜单”,对使用Html Helper做了详细讲述,并且自定义了一个菜单的 Html Helper: https://www.cnblogs.com/miro/p/5541 ...

  2. MVC 5 + EF6 完整教程15 -- 使用DI进行解耦

    如果大家研究一些开源项目,会发现无处不在的DI(Dependency Injection依赖注入). 本篇文章将会详细讲述如何在MVC中使用Ninject实现DI 文章提纲 场景描述 & 问题 ...

  3. MVC5+EF6 完整教程

    随笔分类 - MVC ASP.NET MVC MVC5+EF6 完整教程17--升级到EFCore2.0 摘要: EF Core 2.0上周已经发布了,我们也升级到core 文章内容基于vs2017, ...

  4. 开源题材征集 + MVC&EF Core 完整教程小结

    到目前为止,我们的MVC+EF Core 完整教程的理论部分就全部结束了,共20篇,覆盖了核心的主要知识点. 下一阶段是实战部分,我们将会把这些知识点串联起来,用10篇(天)来完成一个开源项目. 现向 ...

  5. MVC5+EF6 完整教程17--升级到EFCore2.0(转)

    MVC5+EF6 完整教程17--升级到EFCore2.0 2017年08月22日 14:48:12 linux12a 阅读数:2814   EF Core 2.0上周已经发布了,我们也升级到core ...

  6. SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解

    SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解 博客分类: 跟开涛学SpringMVC   6.6.2.@RequestParam绑定单个请求参数值 @RequestParam用于 ...

  7. Linux下nginx编译安装教程和编译参数详解

    这篇文章主要介绍了Linux下nginx编译安装教程和编译参数详解,需要的朋友可以参考下 一.必要软件准备1.安装pcre 为了支持rewrite功能,我们需要安装pcre 复制代码代码如下: # y ...

  8. Spring4.X + spring MVC + Mybatis3 零配置应用开发框架搭建详解(1) - 基本介绍

    Spring4.X + spring MVC + Mybatis3 零配置应用开发框架搭建详解(1) - 基本介绍 spring集成 mybatis Spring4.x零配置框架搭建 两年前一直在做后 ...

  9. 《挑战30天C++入门极限》入门教程:实例详解C++友元

        入门教程:实例详解C++友元 在说明什么是友元之前,我们先说明一下为什么需要友元与友元的缺点: 通常对于普通函数来说,要访问类的保护成员是不可能的,如果想这么做那么必须把类的成员都生命成为pu ...

随机推荐

  1. php与mysql的常规使用

    <?php header("Content-type:text/html;charset=GBK"); /* 通常,php网页中完成有关数据库的操作,首先,需要如下代码: $ ...

  2. ArcGIS API For JavaScript 加载地图,设置地图中心点

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  3. 【一通百通】c/php的printf用法

    1.先说说PHP printf()函数: printf()函数的调用格式为: printf("<格式化字符串>", <参量表>); %d 十进制有符号整数  ...

  4. ubuntu 软件安装配置使用总结(由xmind:Depends:java8-runtime but is not installed引出)

    ubuntu 软件安装总结(由xmind:Depends:java8-runtime but is not installed引出) 不知道抽什么风,这几天PC上又用起了linux操作系统.其实之前断 ...

  5. 【Java基础 】Java7 NIO Files,Path 操作文件

    从Java1.0到1.3,我们在开发需要I/O支持的应用时,要面临以下问题: 没有数据缓冲区或通道的概念,开发人员要编程处理很多底层细节 I/O操作会被阻塞,扩展能力有限 所支持的字符集编码有限,需要 ...

  6. oracle物理视图(转)

    近期根据项目业务需要对oracle的物化视图有所接触,在网上搜寻关于这方面的资料,便于提高,整理内容如下: 物化视图是一种特殊的物理表,“物化”(Materialized)视图是相对普通视图而言的.普 ...

  7. Unix环境编程基础下

    Unix出错处理 当UNIX系统的函数出错时,通常会返回一个负值.我们判断函数的返回值小于0表示出错了,注意我们并不知道为什么出错.例如我们open一个文件,返回值-1表示打开失败,但是为什么打开失败 ...

  8. 手机自动化测试:appium源码分析之bootstrap十七

    手机自动化测试:appium源码分析之bootstrap十七   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣 ...

  9. 利用php数组函数进行函数式编程

    因为一个BUG, 我在一个摇摇欲坠,几乎碰一下就会散架的项目中某一个角落中发现下面这样一段代码 这段程序与那个BUG有密切的关系. 我来回反复的捉摸这段代码, 发现这段代码实现了两个功能 第一个是在一 ...

  10. Uva 10892 LCM Cardinality (数论/暴力)

    题意:给出数n,求有多少组A,B的最小公约数为n; 思路:3000ms,直接暴力寻找,找到所有能把n整除的数 pi, 枚举所有pi 代码: #include <iostream> #inc ...