• MVC 5 + EF6 完整教程16 -- 控制器详解


    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类型,掌握数据的传递的几种方式。

  • 相关阅读:
    ServletDemo
    day12
    [转载]推荐不伤眼睛的文字背景色 VS背景色
    day11
    63.队列:1.    寻找经过结点最少的路径
    62.COUNT(递归算法)--数的划分变式题型
    62.COUNT(递归算法)--数的划分变式题型
    62.COUNT(递归算法)--数的划分变式题型
    60.(递推练习)黑白棋子
    60.(递推练习)黑白棋子
  • 原文地址:https://www.cnblogs.com/zhang1f/p/11119111.html
Copyright © 2020-2023  润新知