2.2.4基于Area的路由映射
对于一个较大规模的Web应用,我们可以从功能上通过Area将其划分为较小的单元。每个Area相当于一个独立的子系统,它们具有一套包含Models、Views和Controller在内的目录结构和配置文件。一般来说,每个Area具有各自的路由规则(路由模板上一般会包含Area的名称),而基于Area的路由映射通过AreaRegistration类型进行注册。
1.AreaRegistration与AreaRegistrationContext
针对Area的路由通过AreaRegistration来注册。如下面的代码所示,AreaRegistration是一个抽象类,它的抽象只读属性AreaName返回当前Area的名称,而抽象方法RegisterArea用于实现基于当前Area的路由注册。
public abstract class AreaRegistration { public static void RegisterAllAreas(); public static void RegisterAllAreas(object state); public abstract string AreaName { get; }; public abstract void RegisterArea(System.Web.Mvc.AreaRegistrationContext context); }
AreaRegistration定义了两个抽象的静态RegisterAllAreas方法重载,参数state表示传递给具体AreaRegistration的数据。当RegisterAllArea方法执行的时候,所有被当前Web应用直接或间接引用的程序集会被加载(如果尚未被加载),ASP.NET MVC会从这些程序集中解析出所有继承自AreaRegistration的类型,并通过反射创建相应的AreaRegistration对象。针对每个被创建出来的AreaRegistration对象,一个作为Area注册上下文的AreaRegistrationContext对象会被创建出来,它被作为参数调用这些AreaRegistration对象的RegisterArea方法进行针对相应Area的路由注册。
如下面的代码片段所示,AreaRegistrationContext的只读属性AreaName表示Area的名称,属性Routes是一个代表路由表的RouteCollection对象,而State是一个用户自定义对象,他们均通过构造函数进行初始化。具体来说,AreaRegistrationContext对象是在调用AreaRegistration的静态方法RegisterAllAreas时针对创建出来的AreaRegistration对象构建的,其AreaName来源于当前AreaRegistration对象的同名属性,Routes则对应着RouteTable的静态属性Routes所表示的全局路由表。调用RegisterAllAreas方法指定的参数state将被作为调用AreaRegistrationContext构造函数的同名参数。
public class AreaRegistrationContext { public AreaRegistrationContext(string areaName,RouteCollection routes, object state); public AreaRegistrationContext(string areaName,RouteCollection routes); public Route MapRoute(string name, string url, object defaults, object constraints, string[] namespaces); public Route MapRoute(string name, string url, object defaults, string[] namespaces); public Route MapRoute(string name, string url, string[] namespaces); public Route MapRoute(string name, string url, object defaults, object constraints); public Route MapRoute(string name, string url, object defaults); public Route MapRoute(string name, string url); public string AreaName { get; } public ICollection<string> Namespaces { get; } public RouteCollection Routes { get; } public object State { get; } }
AreaRegistrationContext的只读属性Namespaces表示一组需要优先匹配的命名空间(当多个同名的Controller类型定义在不同的命名空间的时候,定义在这些命名空间的Controller类型会被优先选用)。当针对某个具体AreaRegistration的AreaRegistrationContext对象被创建的时候,如果AreaRegistration类型定义在某个命名空间(比如“Artech.Controllers”),则在这个命名空间基础上添加“.*”后缀生成的字符串(比如“Artech.Controllers.*”)会被添加到Namespaces集合中。换言之,对于多个定义在不同命名空间中的同名Controller类型,会优先选择包含在当前AreaRegistration所在命名空间下的Controller。
AreaRegistrationContext定义了一系列的MapRoute方法进行路由注册,方法的使用及参数的含义与RouteCollection类的同名扩展方法一致。在这里需要特别指出的是,如果MapRoute方法没有指定命名空间,通过属性Namespaces表示的命名空间列表会被使用;反之,该属性中包含的命名空间会被直接忽略。
当我们通过Visual Stdio的ASP.NET MVC项目模板创建一个Web应用的时候,在Global.asax文件中会生成类似如下的代码,在这里通过调用AreaRegistration的静态方法RegisterAllAreas实现对所有Area的注册。也就是说,针对所有Area的注册发生在Web应用启动的时候。
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); //其他操作 }