理解 ASP.NET MVC 中的 IController 和 ControllerBase





5.00/5 (3投票s)
本文介绍了 IController 接口和 ControllerBase 类。
引言
MVC 中的控制器负责响应用户交互,通常会根据用户输入更改 Model。简而言之,MVC 模式中的控制器负责应用程序的流程,处理传入数据,并提供输出数据到相关的 View。
每当我们向 MVC 项目添加一个控制器时,Visual Studio 就会创建一个类,其名称以“Controller”结尾。这个新创建的类继承自 Controller
类,这使得它表现为控制器。或者,我们也可以通过继承 ControllerBase
类或实现 IController
接口来创建控制器。这是可能的,因为 Controller
类继承了 ControllerBase
类,而 ControllerBase
类又实现了 IController
接口的方法。让我们来了解一下 ControllerBase
类和 IController
接口,看看它们各自提供了哪些方法。
IController 接口
IController 接口的主要目的是在向控制器发出请求时执行一些代码。这是该接口的结构
注意:我们可以从 aspnetwebstack.codeplex.com/ 获得源代码
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// See License.txt in the project root for license information.
using System.Web.Routing;
namespace System.Web.Mvc
{
public interface IController
{
void Execute(RequestContext requestContext);
}
}
IController
接口公开了 Execute()
方法,当对控制器发出请求时,该方法将被执行。它接受一个 RequestContext
类的对象,该对象封装了有关与已定义路由匹配的 HTTP 请求的信息,使用 HttpContext
和 RouteData
属性。
因此,我们可以通过实现 IController
并实现其 Execute()
方法来完成一些工作以创建控制器。例如,下面的 MyCustomController
类实现了 IController
并为 Execute()
方法提供了主体。
public class MyCustomController : IController
{
public void Execute(RequestContext requestContext)
{
string controller = requestContext.RouteData.Values["controller"] as string;
string action = requestContext.RouteData.Values["action"] as string;
requestContext.HttpContext.Response.Write(string.Format("Controller name - {0}", controller));
requestContext.HttpContext.Response.Write(string.Format("Action name - {0}", action));
}
}
在这里,我们从路由值集合中获取控制器和操作名称,为了使示例简单,直接将其写入响应中。
ControllerBase 类
抽象的 ControllerBase
类代表所有 MVC 控制器的基类。它实现了 IController
接口的 Execute()
方法,该方法执行指定的请求上下文。这是该类的结构
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// See License.txt in the project root for license information.
public abstract class ControllerBase : IController
{
protected virtual void Execute(RequestContext requestContext)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (requestContext.HttpContext == null)
{
throw new ArgumentException(
MvcResources.ControllerBase_CannotExecuteWithNullHttpContext,
"requestContext");
}
VerifyExecuteCalledOnce();
Initialize(requestContext);
using (ScopeStorage.CreateTransientScope())
{
ExecuteCore();
}
}
protected abstract void ExecuteCore();
protected virtual void Initialize(RequestContext requestContext)
{
ControllerContext = new ControllerContext(requestContext, this);
}
internal void VerifyExecuteCalledOnce()
{
if (!_executeWasCalledGate.TryEnter())
{
string message = String.Format(CultureInfo.CurrentCulture,
MvcResources.ControllerBase_CannotHandleMultipleRequests, GetType());
throw new InvalidOperationException(message);
}
}
void IController.Execute(RequestContext requestContext)
{
Execute(requestContext);
}
}
ControllerBase
类充当 IController
接口的包装器。ControllerBase
类的 Execute()
方法负责创建 ControllerContext
,它为当前请求提供 MVC 特定的上下文,这与 HttpContext
为 ASP.NET 提供上下文的方式非常相似,提供了请求和响应、URL 和服务器信息以及其他元素。
我们可以看到,ControllerBase
类提供了 Execute()
的实现,但它又有另一个 IController.Execute()
的定义,那么问题是为什么会这样?
默认情况下,通常实现的接口方法是 public 并且不是 virtual 或 abstract 的,因此您无法在派生类中覆盖它。因此,如果我们在 ControllerBase
类中创建一个新的 Execute()
方法,则默认情况下,当要调用其自己的 Execute()
方法时,它将无法通过 IController
接口访问。
但是,通过在 ControllerBase
类中创建一个新的、受保护的 virtual Execute()
方法,我们将从显式实现的接口方法 (IController.Execute()
) 中调用该方法,这允许派生类(Controller
类)覆盖 ControllerBase
类的 Execute()
方法,而不会破坏接口实现。
因此,我们可以通过实现 ControllerBase
并重写其 ExecuteCore()
方法来完成一些工作来创建控制器。例如,下面的 MyCustomController
类继承了 ControllerBase
并为 ExecuteCore()
方法提供了主体。
public class MyCustomController : ControllerBase
{
protected override void ExecuteCore()
{
string controllername = ControllerContext.RouteData.Values["controller"].ToString();
string actionName = ControllerContext.RouteData.Values["action"].ToString();
this.ControllerContext.HttpContext.Response.Write(
string.Format("Controller name - {0}", controllername));
this.ControllerContext.HttpContext.Response.Write(
string.Format("Action name - {0}", actionName));
}
}
在这里,我们从路由值集合中获取控制器和操作名称,为了使示例简单,直接将其写入响应中。
实现 IController
或直接继承 ControllerBase
的控制器必须编写它们自己的实现,用于将请求 URL 映射到适当操作的执行。