[转载]ASP.NET Web API的核心对象:HttpController - Artech - 博客园

[转载]ASP.NET Web API的核心对象:HttpController – Artech – 博客园.

对于ASP.NET Web API来说,所谓的Web API定义在继承自ApiController的类中,可能ApiController是大部分读者最为熟悉的类型了。但是我们将ASP.NET Web API下的Controller称为HttpController,它是对所有实现了接口IHttpController的所以Controller类型 的统称,而ApiController仅仅视为IHttpController接口的一个实现而已,所以我们会更多地强调HttpController的 概念。[本文已经同步到《How ASP.NET Web API Works?》]

目录
HttpController
HttpControllerContext
HttpControllerDescriptor
ApiController
实例演示:证明针对每次请求创建的HttpController都是“全新的”

HttpController

既然HttpController指的是所有实现了IHttpController接口的类型,我们自然得下来了解一下这个接口的定义。如下面的代码片断所示,在IHttpController接口中仅仅定义了唯一的方法ExecuteAsync方法,该方法以异步的方式执行HttpController。

public interface IHttpController
{
    Task<HttpResponseMessage> ExecuteAsync( HttpControllerContext controllerContext,  CancellationToken cancellationToken);
}

与我们在“消息处理管道” 中介绍的HttpMessageHandler的SendAsync方法一样,该ExecuteAsync方法返回的依然是一个 Task<HttpResponseMessage>对象,代表一个用于处理响应消息的Task。实际上HttpController可以视 为对ASP.NET Web API的消息处理管道的延续。如右图所示,作为消息处理管道最后一个HttpMessageHandler的HttpRoutingDispatcher 利用自身的HttpControllerDispatcher激活并调用ExecuteAsync方法执行目标HttpController。方法调用返 回的Task<HttpResponseMessage>对象正式HttpControllerDispatcher的SendAsync方法的返回值。

与HttpMessageHandler的SendAsync方法不同之处在于:ExecuteAsync方法的参数不再是表示请求的 HttpRequestMessage对象,而是一个HttpControllerContext对象。顾名思 义,HttpControllerContext表示当前基于HttpController的上下文,HttpController可以认为是在该上下文 中执行。

HttpControllerContext

如下面的代码片断所示,通过定义在HttpControllerContext中的属性我们可以得到用于配置消息处理管道的 HttpConfiguration,封装路由数据的HttpRouteData,以及表示当前请求的HttpRequestMessage。这三个属性 可以在构建HttpControllerContext的时候直接通过构造函数的参数指定,我们也可以先创建一个空的 HttpControllerContext对象之后直接对这些属性赋值。

public class HttpControllerContext
{
    public HttpControllerContext();
    public HttpControllerContext(HttpConfiguration configuration,  IHttpRouteData routeData,HttpRequestMessage request);   

    public HttpConfiguration      Configuration { get; set; }
    public IHttpRouteData         RouteData { get; set; }
    public HttpRequestMessage     Request { get; set; }

    public IHttpController              Controller { get; set; }
    public HttpControllerDescriptor     ControllerDescriptor { get; set; }
}

既然HttpControllerContext是针对HttpController的上下文,我们自然可以从中得到这个 HttpController。我们除了可以通过属性Controller获取或者设置对应的HttpController之外,还可以利用另一个属性 ControllerDescriptor获取或者设置用于描述HttpController的 System.Web.Http.Controllers.HttpControllerDescriptor对象。

HttpControllerDescriptor

HttpControllerDescriptor封装了某个HttpController类型的元数据,可以视为对应HttpController类型的描述对象。除此之外,HttpControllerDescriptor 还具有根据元数据创建对应HttpController的能力,实际上ASP.NET Web API的HttpController激活系统就是根据HttpControllerDescriptor来创建目标HttpController的。

如下面的代码片断所示,我们可以通过HttpControllerDescriptor的属性Confiruation、 ControllerName和ControllerType获取当前的HttpConfiguration和被描述HttpController的名称 和类型。这三个属性可以在构建HttpControllerDescriptor时通过构造函数的参数显式指定,我们可以可以先 构建一个空的HttpControllerDescriptor对象,然后手工设置这些属性。

public class HttpControllerDescriptor
{   
    public HttpControllerDescriptor();
    public HttpControllerDescriptor(HttpConfiguration configuration,   string controllerName, Type controllerType);

    public virtual IHttpController CreateController(HttpRequestMessage request);

    public virtual Collection<T> GetCustomAttributes<T>() where T: class;
    public virtual Collection<T> GetCustomAttributes<T>(bool inherit)   where T: class;
    public virtual Collection<IFilter> GetFilters();

    public HttpConfiguration     Configuration { get; set; }
    public string                ControllerName { get; set; }
    public Type                  ControllerType { get; set; }

    public virtual ConcurrentDictionary<object, object> Properties { get; }
}

我们说HttpControllerDescriptor具有根据元数据信息创建目标HttpController的能力主要体现在其 CreateController方法上。实际上本章的核心内容“HttpController的激活”就体现在 HttpControllerDescriptor的这个CreateController方法上,所有后续的内容基本上都是围绕着这个方法展开的。

HttpControllerDescriptor还具有一个字典类型的只读属性Properties,它使我们可以将通过指定一个字符串类型的 Key将任何一个对象附加到某个HttpControllerDescriptor,然后通过这个Key人任何情况下将这个附加的对象提取出来。我们在 HttpRequestMessage类型中已经看到过了类似的设计。

ApiController

我们现在来介绍一下我们创建HttpController类型默认采用的基类ApiController。如下面的代码片断所示,除了实现接口 IHttpController外,HttpController还实现了接口IDisposable。如果在自定义HttpController需要实 现一些资源回收的工作,可以将它们定义在重写的Dispose方法中(受保护的虚方法Dispose)。

public abstract class ApiController : IHttpController, IDisposable
{
    public virtual Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken);
    protected virtual void Initialize(HttpControllerContext controllerContext);

    public void Dispose();
    protected virtual void Dispose(bool disposing);

    public HttpControllerContext     ControllerContext { get; set; }
    public HttpConfiguration         Configuration { get; set; }
    public HttpRequestMessage        Request { get; set; }
    public IHttpRouteData            RouteData { get; set; }

    public ModelStateDictionary     ModelState { get; }
    public UrlHelper                Url { get; set; }
    public IPrincipal               User { get; }
}

我们可以通过属性ControllerContext从ApiController对象中获取或者为它设置一个 HttpControllerContext对象作为其上下文,另外三个属性Configuration、Request和RouteData与此 HttpControllerContext的同名属性引用这同一个对象。

ApiController的只读属性ModelState返回一个具有字典数据结构的ModelStateDictionary对象,包含其中的 数据会被以“Model绑定”的形式绑定到目标Action方法的某个参数。除此之外,ApiController的ModelState属性还用于保存 参数验证失败后的 错误消息。另一个参数Url返回一个类型为UrlHelper的对象,该对象用于根据注册的HttpRoute和提供的路由变量生成相应的URL。

ApiController的User返回当前线程的Principal。相信读者还会记得在“消息处理管道”中介绍HttpServer时我们谈 到:如果当前线程的Principal为Null,作为消息处理管道“龙头”的HttpServer会在SendAync方法执行过程创建一个空的 GenericPrincipal对象作为当前线程的“匿名”Principal。所以对于匿名请求来说,ApiController的User属性会返 回这个通过HttpServer设置的空GenericPrincipal对象。

从上面给出的代码片断我们还会看到ApiController包含一个受保护的Initialize方法,该方法根据指定的 HttpControllerContext对自身作相应的初始化处理。一旦执行了Initialize方法,当前ApiController对象将处于 初始化状态。在默认情况下,此Initialize会在实现的ExecuteAsync方法中被调用。

在默认情况下,ASP.NET Web API的HttpController激活系统总是创建一个新的HttpController来处理每一个请求。对于其类型继承自 ApiController的HttpController来说,如果在执行ExecuteAsync方法的时候发现当前的ApiController已 经处于“初始化”的状态,会直接抛出一个InvalidOperationException异常。

实例演示:证明针对每次请求创建的HttpController都是“全新的”

举个简单的例子,我们定义了如下一个继承自ApiController的类MyApiController,并通过如下的方式将原本为受保护的Initialize方法转换成一个公有方法,以方便我们后续的调用。

public class MyApiController : ApiController
{
    public new void Initialize(HttpControllerContext controllerContext)
    {
        base.Initialize(controllerContext);
    }
}

然后我们执行如下一段代码,它的特别指出在于:我们在调用MyApiController对象的ExecuteAsync方法之前调用了Initialize方法对其作了初始化处理。

MyApiController controller = new MyApiController();
HttpControllerContext controllerContext = new HttpControllerContext(new HttpConfiguration(), new HttpRouteData(new HttpRoute()), new HttpRequestMessage());
controller.ControllerContext = controllerContext;
controller.Initialize(controllerContext);
controller.ExecuteAsync(controllerContext, new CancellationToken(false));

 

当执行ApiController的ExecuteAsync方法的时候会抛出如图4-2所示的InvalidOperation,并提 示:“Cannot reuse an ‘ApiController’ instance. ‘ApiController’ has to be constructed per incoming message. Check your custom ‘IHttpControllerActivator’ and make sure that it will not manufacture the same instance.”错误消息已经表明了ApiController是不能“重用”的,用于处理每一个请求的ApiController都应该是“全新” 的。

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏