惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
V
Vulnerabilities – Threatpost
Google Online Security Blog
Google Online Security Blog
Hugging Face - Blog
Hugging Face - Blog
L
LINUX DO - 热门话题
云风的 BLOG
云风的 BLOG
P
Proofpoint News Feed
D
Docker
C
Cyber Attacks, Cyber Crime and Cyber Security
MyScale Blog
MyScale Blog
P
Palo Alto Networks Blog
T
Tenable Blog
P
Privacy International News Feed
Google DeepMind News
Google DeepMind News
小众软件
小众软件
Cisco Talos Blog
Cisco Talos Blog
aimingoo的专栏
aimingoo的专栏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
Arctic Wolf
C
Cybersecurity and Infrastructure Security Agency CISA
C
Cisco Blogs
T
Threat Research - Cisco Blogs
NISL@THU
NISL@THU
The Hacker News
The Hacker News
Project Zero
Project Zero
AWS News Blog
AWS News Blog
Simon Willison's Weblog
Simon Willison's Weblog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Threatpost
V
Visual Studio Blog
The GitHub Blog
The GitHub Blog
The Cloudflare Blog
Last Week in AI
Last Week in AI
Jina AI
Jina AI
Cyberwarzone
Cyberwarzone
The Register - Security
The Register - Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
Vercel News
Vercel News
D
Darknet – Hacking Tools, Hacker News & Cyber Security
MongoDB | Blog
MongoDB | Blog
U
Unit 42
Scott Helme
Scott Helme
A
About on SuperTechFans
WordPress大学
WordPress大学
F
Fortinet All Blogs
大猫的无限游戏
大猫的无限游戏
G
GRAHAM CLULEY
Latest news
Latest news
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Schneier on Security

博客园 - Terry Sun

Ubuntu安装Java7 SDK Postgres整理 RESTful架构API的设计误区 解决createdb: could not connect to database postgres: FATAL: Peer authentication failed for user &quot;postgres&quot; 如何升级Nginx到最新稳定版 The remote name could not be resolved:&#39;maps.googleapis.com&#39; 的解决办法 关于Retrieving the COM class factory for component with CLSID {000209FF-0000-0000-C000-000000000046} failed due to the following error: 80070005的解决办法 [iOS]黑色状态栏 圆角内容区 No connection could be made because the target machine actively refused it 127.0.0.1:808 的解决办法 Google Map V3--geocode与fitBounds方法的同步操作 ADO.NET Entity Framework--不再查询直接更新数据 安装SSL证书-解决导入证书时的ASN1 bad tag value met错误 解决 The Controls collection cannot be modified because the control contains code blocks (i.e. <% ... %>) - Terry Sun &lt;转&gt;用HTML判断IE版本 关于Master Page的css和js文件引用问题 ASP.NET Mvc 2.0 - 2. 异步Controller执行流程时序图 [转载]ASP.NET 2.0 中的异步页 深入分析 ASP.NET Mvc 1.0 – 4. 使用ModelBinder绑定Action的参数 深入分析 ASP.NET Mvc 1.0 – 3. Controller.Execute(Request)-ActionInvoker.InvokeAction()
ASP.NET Mvc 2.0 - 1. Areas的创建与执行
Terry Sun · 2010-04-13 · via 博客园 - Terry Sun

Areas是ASP.NET Mvc 2.0版本中引入的众多新特性之一,它可以帮你把一个较大型的Web项目分成若干组成部分,即Area。实现Area的功能可以有两个组织形式:

  1. 在1个ASP.NET Mvc 2.0 Project中创建Areas。
  2. 创建多个ASP.NET Mvc 2.0 Project,每个Project就是一个Area。

第2种结构比较复杂,但第1种结构同样可以做到每个Area之间的并行开发,互不影响,所以不推荐第2种方法。

以ASP.NET Mvc 2.0的默认模板为例:

1. 首先要新建一个ASP.NET Mvc 2.0的Project,在Project上点击鼠标右键选择Add->Area,在打开的窗口中输入Area的名子(例如:Profile),点击Add按钮,然后将看到下面的结构。P

名子叫做Profile的Area的结构与根目录下的Controllers,Models和Views的结构是一样的,唯一区别是Profile下面多了一个ProfileAreaRegistration.cs文件。它继承自AreaRegistration类,ProfileAreaRegistration 必须实现AreaRegistration类中的AreaName属性和RegisterArea(AreaRegistrationContext context)方法

using System.Web.Mvc;

namespace ASP.NET_Mvc_2_Test.Areas.Profile
{
    public class ProfileAreaRegistration : AreaRegistration
    {
        public override string AreaName
        {
            get
            {
                return "Profile";
            }
        }

        public override void RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
                "Profile_default",
                "Profile/{controller}/{action}/{id}",
                new { action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

AreaName属性用来定义Area的名子, RegisterArea(AreaRegistrationContext context) 方法中可以看出在浏览器的地址栏中URL的样式为Profile/{controller}/{action}/{id},是4级构结,只要将context.MapRoute(…)改为

public override void RegisterArea(AreaRegistrationContext context)
        {
            context.MapRoute(
                "Profile_default",
                "Profile/{action}/{id}",
                new { controller = "要访问的controller名子", action = "Index", id = UrlParameter.Optional }
            );
        }

URL的样式会再变为三级结构 Profile/{action}/{id}。

2. 修改根目录下Views/shared/Site.Master文件,并添加一个名为o”Your Profile”的菜单项并指定area的名子, 示例中Area的名子为Profile。  

<ul id="menu">              
                    <li><%= Html.ActionLink("Home", "Index", "Home", new { area = ""}, null)%></li>
                    <li><%= Html.ActionLink("Your Profile", "ViewProfile", "Profile", new { area = "Profile" }, null)%></li>
                    <li><%= Html.ActionLink("About", "About", "Home", new { area = ""}, null)%></li>
</ul>

注意:Home和About不属于任何Area,但是也要通过匿名对象的方式声明area,如果没有声明Area,当进入到Profile的某个view时, Home和About的会的URL会变为Profile/Home, Profile/About,点击Home或About时会有异常抛出,所以当页面上的某个链接不属于任何一个Area并且有可能被多个Area可享的话,一定要加上new { area = ""}

3. 只有匹配正确的Route才能显示Area中的View,Area中的Route已经配置好,但它是如何被加入到RouteTable中的?

  3.1 首先在Global.asax.cs的Application_Start()方法中调用了AreaRegistration.RegisterAllAreas()方法,这个方法的目地主是找出所有继承了AreaRegistration的类,并执行RegisterArea(…)方法来完成注册

public static void RegisterAllAreas() {
            RegisterAllAreas(null);
        }

        public static void RegisterAllAreas(object state) {
            RegisterAllAreas(RouteTable.Routes, new BuildManagerWrapper(), state);
        }

        internal static void RegisterAllAreas(RouteCollection routes, IBuildManager buildManager, object state) {
            List<Type> areaRegistrationTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(_typeCacheName, IsAreaRegistrationType, buildManager);
            foreach (Type areaRegistrationType in areaRegistrationTypes) {
                AreaRegistration registration = (AreaRegistration)Activator.CreateInstance(areaRegistrationType);
                registration.CreateContextAndRegister(routes, state);
            }
        }

    3.1.1 TypeCacheUtil.GetFilteredTypesFromAssemblies(…)方法用来找出所有继承了AreaRegistration类的Type对象:

public static List<Type> GetFilteredTypesFromAssemblies(string cacheName, Predicate<Type> predicate, IBuildManager buildManager) {
            TypeCacheSerializer serializer = new TypeCacheSerializer();

            // first, try reading from the cache on disk
            List<Type> matchingTypes = ReadTypesFromCache(cacheName, predicate, buildManager, serializer);
            if (matchingTypes != null) {
                return matchingTypes;
            }

            // if reading from the cache failed, enumerate over every assembly looking for a matching type
            matchingTypes = FilterTypesInAssemblies(buildManager, predicate).ToList();

            // finally, save the cache back to disk
            SaveTypesToCache(cacheName, matchingTypes, buildManager, serializer);

            return matchingTypes;
        }

     在GetFilteredTypesFromAssemblies(…)方法中,先从缓存中读取匹配的类型(缓存用于.Net Framework 4.0中,示例程序用VS2008在.NET 3.5环境下,暂不讨论缓存的应用)。缓存没有数据返回,调用FilterTypesInAssemblies(…)方法返回List<Type>对象, 这时返回的List<Type>才是继承了AreaRegistration类的Type对象, 下面的代码是FilterTypesInAssemblies方法的源码:

private static IEnumerable<Type> FilterTypesInAssemblies(IBuildManager buildManager, Predicate<Type> predicate) {
            // Go through all assemblies referenced by the application and search for types matching a predicate
            IEnumerable<Type> typesSoFar = Type.EmptyTypes;

            ICollection assemblies = buildManager.GetReferencedAssemblies();
            foreach (Assembly assembly in assemblies) {
                Type[] typesInAsm;
                try {
                    typesInAsm = assembly.GetTypes();
                }
                catch (ReflectionTypeLoadException ex) {
                    typesInAsm = ex.Types;
                }
                typesSoFar = typesSoFar.Concat(typesInAsm);
            }
            return typesSoFar.Where(type => TypeIsPublicClass(type) && predicate(type));
        }

    与寻找controll的方法相似,找出所有assembly中的所有Type并通过TypeIsPublicClass和predicate的过滤出继承了AreaRegistration类的Type对象

    3.1.2 在GetFilteredTypesFromAssemblies(…)方法中如果.Net Framework的版本不是4.0, 最后的SaveTypesToCache(…)方法也不会执行

              以后会对.Net Framework 4.0下的ReadTypesFromCache(…)和SaveTypesToCache(…)进行补充

  3.2 遍历返回的List<Type>对象,反射出AreaRegistration 对象并调用它的CreateContextAndRegister(…)方法

internal void CreateContextAndRegister(RouteCollection routes, object state) {
            AreaRegistrationContext context = new AreaRegistrationContext(AreaName, routes, state);

            string thisNamespace = GetType().Namespace;
            if (thisNamespace != null) {
                context.Namespaces.Add(thisNamespace + ".*");
            }

            RegisterArea(context);
        }

在CreateContextAndRegister(…)方法中创建一个AreaRegistrationContext 对象并做为RegisterArea(…)方法的参数,RegisterArea刚好是AreaRegistration 的继承类中重写方法,在RegisterArea方法只要调用AreaRegistrationContext 类的MapRoute(…)方法就可能完成一个新的Route的注册。

示例代码 下载