Orchard源码分析(7.2):Controller相关

概述

默认情况下,ASP.NET MVC内置的DefaultControllerFactory负责Controller实例的创建。Orchard定义了一个继承自DefaultControllerFactory类的Orchard.Mvc.OrchardControllerFactory类并在OrchardStarter类中进行注册:

// 以下代码来在Orchard.Environment.OrchardStarter类

ControllerBuilder.Current.SetControllerFactory(new OrchardControllerFactory());

OrchardControllerFactory作用是能从Shell生命周期的Autofac"子容器"中解析(Resolver)Controller类型,再根据类型解析Controller实例。这里就引出这些问题:Controller类型在什么时候被注册到Autofac容器中,Controller的类型如何被解析出来,Controller的实例如何被解析出来。

一、Controller注册

在激活Shell之前,会创建Shell的上下文对象ShellContext,在这个过程中会将Shell需要用到的Controller注册到Shell作用域的Autofac容器中:

// 以下代码来在Orchard.Environment.ShellBuilders.ShellContainerFactory类的CreateContainer方法

foreach (var item in blueprint.Controllers) {

var serviceKeyName = (item.AreaName + "/" + item.ControllerName).ToLowerInvariant();

var serviceKeyType = item.Type;

RegisterType(builder, item)

.EnableDynamicProxy(dynamicProxyContext)

.Keyed< IController>(serviceKeyName)

.Keyed< IController>(serviceKeyType)

.WithMetadata( "ControllerType", item.Type)

.InstancePerDependency()

.OnActivating(e =>s {

// necessary to inject custom filters dynamically

// see FilterResolvingActionInvoker

var controller = e.Instance as Controller;

if (controller != null )

controller.ActionInvoker = ( IActionInvoker)e.Context.ResolveService(new TypedService(typeof (IActionInvoker)));

});

}

foreach (var item in blueprint.HttpControllers) {

var serviceKeyName = (item.AreaName + "/" + item.ControllerName).ToLowerInvariant();

var serviceKeyType = item.Type;

RegisterType(builder, item)

.EnableDynamicProxy(dynamicProxyContext)

.Keyed< IHttpController>(serviceKeyName)

.Keyed< IHttpController>(serviceKeyType)

.WithMetadata( "ControllerType", item.Type)

.InstancePerDependency();

}

// 以下代码来在Orchard.Environment.ShellBuilders.ShellContainerFactory类

private IRegistrationBuilder<object , ConcreteReflectionActivatorData, SingleRegistrationStyle> RegisterType(ContainerBuilder builder, ShellBlueprintItem item) {

return builder.RegisterType(item.Type)

.WithProperty( "Feature", item.Feature)

.WithMetadata( "Feature", item.Feature);

}

二、ControllerFactory

既然Controller已经被注册到了Autofac容器中,则很容易提取出来:

// 以下代码来在Orchard.Mvc.ControllerFactory类

/// <summary>

/// Overrides the default controller factory to resolve controllers using LoC, based their areas and names.

/// </summary>

public class OrchardControllerFactory : DefaultControllerFactory {

/// <summary>

/// Tries to resolve an instance for the controller associated with a given service key for the work context scope.

/// </summary>

/// <typeparam name="T"> The type of the controller.</typeparam>

/// <param name="workContext"> The work context.</param>

/// <param name="serviceKey"> The service key for the controller. </param>

/// <param name="instance"> The controller instance.</param>

/// <returns> True if the controller was resolved; false otherwise. </returns>

protected bool TryResolve<T>(WorkContext workContext, object serviceKey, out T instance) {

if (workContext != null && serviceKey != null) {

var key = new KeyedService(serviceKey, typeof (T));

object value;

if (workContext.Resolve<ILifetimeScope >().TryResolveService(key, out value)) {

instance = (T) value;

return true ;

}

}

instance = default(T);

return false ;

}

/// <summary>

/// Returns the controller type based on the name of both the controller and area.

/// </summary>

/// <param name="requestContext"> The request context from where to fetch the route data containing the area.</param>

/// <param name="controllerName"> The controller name.</param>

/// <returns> The controller type.</returns>

/// <example> ControllerName: Item, Area: Containers would return the type for the ItemController class.</example>

protected override Type GetControllerType( RequestContext requestContext, string controllerName) {

var routeData = requestContext.RouteData;

// Determine the area name for the request, and fall back to stock orchard controllers

var areaName = routeData.GetAreaName();

// Service name pattern matches the identification strategy

var serviceKey = (areaName + "/" + controllerName).ToLowerInvariant();

// Now that the request container is known - try to resolve the controller information

Meta<Lazy <IController>> info;

var workContext = requestContext.GetWorkContext();

if (TryResolve(workContext, serviceKey, out info)) {

return (Type ) info.Metadata["ControllerType"];

}

return null ;

}

/// <summary>

/// Returns an instance of the controller.

/// </summary>

/// <param name="requestContext"> The request context from where to fetch the route data containing the area.</param>

/// <param name="controllerType"> The controller type.</param>

/// <returns> An instance of the controller if it‘s type is registered; null if otherwise. </returns>

protected override IController GetControllerInstance( RequestContext requestContext, Type controllerType) {

IController controller;

var workContext = requestContext.GetWorkContext();

if (TryResolve(workContext, controllerType, out controller)) {

return controller;

}

// fail as appropriate for MVC‘s expectations

return base .GetControllerInstance(requestContext, controllerType);

}

}

三、ActionInvoker

在成功创建Controller实例后,会将其ActionInvoker设置成Orchard.Mvc.FilterResolvingActionInvoker。

我们知道,ASP.NET MVC中,我们可以将Filter以Attribute的形式定义在Controller或Action上。Orchard提供了一个Filter Provider机制,即可以将一些Filter定义在其他地方,在使用Filter之前再提取出来。这方面比较简单,直接看FilterResolvingActionInvoker的定义:

// 以下代码来在Orchard.Mvc.Filters.FilterResolvingActionInvoker类

public class FilterResolvingActionInvoker : ControllerActionInvoker {

private readonly IEnumerable< IFilterProvider> _filterProviders;

public FilterResolvingActionInvoker(IEnumerable <IFilterProvider> filterProviders) {

_filterProviders = filterProviders;

}

protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {

var filters= base .GetFilters(controllerContext, actionDescriptor);

foreach(var provider in _filterProviders) {

provider.AddFilters(filters);

}

return filters;

}

}

只要一个Filter实现了Orchard.Mvc.Filters.FilterProvider:FilterProvider抽象类,就能非常方便的将Filter应用到所有Action之上。另外,高级的应用可以直接实现IFilterProvider接口,不过一般实现FilterProvider抽象类就行了。

相关类型:

Orchard.Mvc.OrchardControllerFactory:System.Web.Mvc.DefaultActionInvoker

Orchard.Mvc.Filters.FilterResolvingActionInvoker:System.Web.Mvc.ControllerActionInvoker

Orchard.Mvc.Filters.IFilterProvider

Orchard.Environment.ShellBuilders.ShellContainerFactory

Orchard.Environment.AutofacUtil.DynamicProxy2.DynamicProxyContext

时间: 2024-10-10 13:49:33

Orchard源码分析(7.2):Controller相关的相关文章

Orchard源码分析(5):Host相关(Orchard.Environment.DefaultOrchardHost类)

概述 Host 是应用程序域级的单例,代表了Orchard应用程序.其处理应用程序生命周期中的初始化.BeginRequest事件.EndRequest事件等. 可以简单理解为HttpApplication的功能转移到了Host身上.从源码角度上看,Host对应的是实现了IOrchardHost接口的 DefaultOrchardHost类. 回顾一下之前对Orchard.Web.MvcApplication类的分析.在Orchard启动时,会创建一个DefaultOrchardHost对象:

springmvc源码分析之找到Controller

概述 MVC大家比较熟悉, M即model,是业务处理层,与我们开发中的(service.dao.model)等对应起来: V即view,是视图层,以前jsp.freemarker.velocity等,现在都是前后端分离了.使用@ResponseBody注解把Controller方法返回的对象通过转换器转换成指定的格式(如json/xml/protobuf)后,再写入到Response对象的body区,不再走视图解析器,把渲染到工作交给前端去做. C,即controller,控制器,可以分为前端

《k8s 源码分析》- Custom Controller 之 Informer

Custom Controller 之 Informer 概述 架构概览 reflector - List & Watch API Server Reflector 对象 ListAndWatch watchHandler - add obj to delta fifo Informer (controller) - pop obj from delta fifo Controller processLoop Add obj to Indexer (Thread safe store) shar

Orchard源码分析(1):Orchard架构

本文主要参考官方文档"How Orchard works"以及Orchardch上的翻译. 源码分析应该做到庖丁解牛,而不是以管窥豹或瞎子摸象.所以先对Orchard架构有个整体的了解,以及对一些基本概念有所认识. 创建一个基于Web的CMS(内容管理系统)不同于创建一个普通的Web应用程序:它更像是建立一个应用程序容器. 这样一个系统,必须拥有优良的开放性.可扩展性.但是作为一个可扩展系统,它可能会面临应用程序"可用性"的挑战:在系统中的核心模块与未知的未来模块的

Orchard源码分析(1):Orchard架构 (转)

源码分析应该做到庖丁解牛,而不是以管窥豹或瞎子摸象.所以先对Orchard架构有个整体的了解,以及对一些基本概念有所认识. 创建一个基于Web的CMS(内容管理系统)不同于创建一个普通的Web应用程序:它更像是建立一个应用程序容器. 这样一个系统,必须拥有优秀的开放性.可扩展性.但是作为一个可扩展系统,它可能会面临应用程序"可用性"的挑战:在系统中的核心模块与未知的未来模块的组合,包括用户界面级别的整合.编排所有这些小零件,让互不知道的彼此的模块成一个连贯的整体,是Orchard是关键

Orchard源码分析(7.1):Routing(路由)相关

概述 关于ASP.NET MVC中路由有两个基本核心作用,一是通过Http请求中的Url参数等信息获取路由数据(RouteData),路由数据包含了area.controller.action的名称等信息.只有获取了匹配的路由数据,才有可能转入ASP.NET MVC管道:二是根据由规则生成Url,比如要根据某些数据生成View上显示的链接. Orchard对路由进行扩展主要基于如下原因: (1).路由定义在各个模块中.在Orchard应用程序初始化时将分散在各个模块的路由定义收集起来统一注册.

Orchard源码分析(7):ASP.NET MVC相关

概述 Orchard归根结底是一个ASP.NET MVC(以后都简称为MVC)应用,但在前面的分析中,与MVC相关内容的涉及得很少.MVC提供了非常多的扩展点,本文主要关注Orchard所做的扩展.主要集中在如下几个方面: 1.Routing(路由)相关 2.Controller相关 3.ViewEngines相关 4.Model绑定(Binding)相关 5.Model验证器(Validator)相关 我们将分几个篇幅来分别分析. 参考资料: How ASP.NET MVC Works(强烈推

Orchard源码分析(4.4):Orchard.Caching.CacheModule类

概述 CacheModule也是一个Autofac模块. 一.CacheModule类 CacheModule将DefaultCacheManager注册为ICacheManager: public class CacheModule : Module { protected override void Load( ContainerBuilder builder) { builder.RegisterType<DefaultCacheManager>() .As< ICacheMana

Orchard源码分析(2):Orchard.Web.MvcApplication类(Global)

概述 分析一个的ASP.NET项目源码,首先可以浏览其项目结构,大致一窥项目其全貌,了解项目之间的依赖关系.其次可以浏览Web.config和Global.asax文件,找到应用程序的入口点. 本 文主要分析Orchard项目的Global.asax文件,而真正的分析入口点在Global.asax的CodeBehind文件 Global.asax.cs中,即Orchard.Web.MvcApplication类(以下简称MvcApplication类). MvcApplication类处理了三个

Orchard源码分析(4.3):Orchard.Events.EventsModule类(Event Bus)

概述 采用Event Bus模式(事件总线),可以使观察者模式中的观察者和被观察者实现解耦. 在.Net 中使用观察者模式,可以使用事件(委托)和接口(类).Orchard Event  Bus使用的是接口的形式,这样方便将“观察者”注册到Autofac容器中.EventsModule模块是构成Orchard Event  Bus的一部分.这里先分开分析Orchard Event Bus涉及的类型和知识点,然后在将他们组合起来分析Orchard Event  Bus的机制. 一.Registra