zoukankan      html  css  js  c++  java
  • MVC简单实现插件Demo-从底层理解MVC路由匹配浏览器请求的URL

         今天实现了在mvc平台下自定义插件,虽然功能比较简单,但是通过对反射的运用,更加明白了为什么我们在浏览器上输入友好的url时,mvc会智能的帮我们找到我们想要查找的页面呢?mvc在底层又是怎样实现的呢?为什么它是如此智能呢?

    原理:

      当我们在浏览器中输入url点击回车键发送请求mvc页面时,请求的url会发送到服务器的iis上,然后提交给mvc.net处理机制来处理用户的请求,在mvc.net处理机制中,会执行HttpRuntime对象的ProcessRequest方法执行两个操作:创建httpContext上下文对象和通过HttpApplicationFactory工厂创建一个HttpApplication对象;

    1. httpContext上下文对象:在创建httpcontext上下文的同时,创建了HttpRequestHttpResponseSessionRemapHandlerServer五大属性,其中的用户请求的请求报文信息存放在httpcontext上下文中的httpRequest属性中,
    2. httpApplication对象:创建httpApplication对象后,httpApplication对象会执行自己的ProcessRequest方法跑19个管道事件,在开始管道事件时,会注册路由、过滤器、webApi和自定义等信息(详细请看mvc根目录中的Global.asax文件),因为Global.asax文件在一运行时就执行,那么我们可以进行面向切面编程(AOP,然后注册到Global.asax文件中。当跑到第七个管道事件时,mvc.net机制会扫描路由集合RouteCollection中的所有路由规则和当前上下文中的Request中的url路径比较,发现存在相同控制器则创建一个mvcHandler对象存入到httpcontext上下文中的RemapHandler中。在第八个事件会判断当前httpcontext中的remapHandler是否为null,如果不为null则说明在第七个管道事件中创建mvcHandler对象成功。如果为null,则表示当前是aspx页面请求,在第1112个管道事件间调用请求url中的action方法,执行action方法中的逻辑操作,然后通过return view转到Razor视图引擎,最终在底层通过Excute方法将前台页面类中的字符串转为html保存到HttpContext上下文中的HttpResponse属性中返回给浏览器

     

    底层实现:两个mvc项目(Mvc.Plugin和MVC.Site)和一个类库(MVC.Plugs.Framework)

    1. Mvc.Plugin:在controllers文件夹中新建一个order控制器,代码如下:
       1   public string Index()//主要是在下面重写控制器验证使用
       2         {
       3             return "不知道怎么选择";
       4         }
       5   //添加一个视图
       6         public ActionResult List()//主要在下面重写Razor视图引擎验证使用
       7         {
       8             return View();
       9             }
      10 要点:将该项目bin目录中的Mvc.Plugin.dll复制到MVC.Site项目文件夹中的Plugin文件夹下的Order文件中,同时也要将该项目中的Views文件夹复制到Plugin文件夹下的Order文件中
    2. 在类库程序MVC.Plugs.Framework中添加两个类PluginControllerFactory.cs(重写控制器)和PluginViewsEngine.cs(重写视图), PluginControllerFactory.cs代码如下(通过反射实现):
       1    using System.Web.Mvc;
       2        using System.Reflection;
       3        public class PluginControllerFactory:DefaultControllerFactory
       4        {
      //重写DefaultControllersFactory工厂中的GetControllerType方法
      5     protected override Type GetControllerType(System.Web.Routing.RequestContext requestContext, string controllerName) 6     { 7     //返回值是一个Type类型的,所以先定义一个空的返回类型 8     Type controllerType = null; 9     10     //1.0获取当前运行网站的根目录 11     string phyPath=AppDomain.CurrentDomain.BaseDirectory; 12     13     //2.0获取当前运行网站中的插件目录 14     string pluginPhyPath = phyPath + "Plugin"; 15     16     //3.0根据请求的controller拼接为完整的带后缀controller控制器名,如homeController 17     string fullName = controllerName + "Controller"; 18     19     //4.0根据插件的物理路径获取插件文件夹中所有带有.dll后缀的程序集 20     string[] dllFiles = System.IO.Directory.GetFiles(pluginPhyPath, "*.dll", System.IO.SearchOption.AllDirectories); 21     22     //5.0判断获取到的程序集是否为空 23     if (dllFiles != null && dllFiles.Length > 0) 24     { 25     //遍历所有的插件程序集 26     foreach (string item in dllFiles) 27     { 28     //把插件文件夹中的所有插件程序集加入到Assembly中 29     Assembly ass = Assembly.LoadFile(item); 30     //然后通过ass的GetType方法检查是否存在Mvc.Plugn.dll,最后赋值给controllerType 31     controllerType = ass.GetType("Mvc.Plugin.Controllers." + fullName); 32     //判断是否存在该插件程序集 33     if (controllerType != null) 34     { 35     //不为空,那么就直接跳出循环,然后controllerType就有值了 36     break; 37     } 38     } 39     } 40     //6.0再判断controllerType是否为空,如果不为空,那么调用父类的GetControllerType方法 41     if (controllerType != null) 42     { 43     return controllerType; 44     } 45     else 46     { 47     return base.GetControllerType(requestContext, controllerName); 48     } 49 }

      3.PluginViewsEngine.cs代码如下:

    3.  1    using System.Web.Mvc;
       2        using System.Web.WebPages.Razor;
       3        using System.Reflection;
       4       public class PluginViewsEngine:RazorViewEngine
       5        {
       6            //1.0定义自己希望razor视图引擎查询的目录
       7           public string[] ViewLocationFormats = {
       8                                                         "~/Plugin/Order/Views/{1}/{0}.cshtml",
       9                                                         "~/Plugin/Order/Views/Shared/{1}/{0}.cshtml",
      10                                                         "~/Views/{1}/{0}.cshtml",
      11                                                         "~/Views/Shared/{1}/{0}.cshtml"
      12                                                            };                                                    
      13    
      14           //2.0重写Razor视图中的FindView方法,查询自己定义的视图目录
      15           public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
      16           {
      17              //3.0将自定义的视图覆盖mvc自带的搜索视图
      18               base.ViewLocationFormats = this.ViewLocationFormats;
      19    
      20               //4.0重写视图引擎,将视图引擎编译成前台页面类的方法
      21               RazorBuildProvider.CodeGenerationStarted += RazorBuildProvider_CodeGenerationStarted;        
      22    
      23               return base.FindView(controllerContext, viewName, masterName, useCache);
      24           }
      25    
      26          //5.0重写视图引擎开始的方法
      27           void RazorBuildProvider_CodeGenerationStarted(object sender, EventArgs e)
      28           {
      29               //将sender强转为RazorBuildProvider
      30               RazorBuildProvider provider=sender as RazorBuildProvider;
      31               //获取当前网站的运行目录
      32               string phyPath = AppDomain.CurrentDomain.BaseDirectory;
      33               //获取当前网站下插件Plugin的物理路径
      34               string pluginPhyPath = phyPath + "Plugin";
      35               //获取插件文件夹中所有带有.dll的程序集
      36              string[] dllPhyPath=System.IO.Directory.GetFiles(pluginPhyPath, "*.dll", System.IO.SearchOption.AllDirectories);
      37               //因为只有一个插件dll,所以可以直接获取
      38              string mvcPlugdllPath = dllPhyPath[0];
      39               //直接加载进程序集中
      40              Assembly ass = Assembly.LoadFile(mvcPlugdllPath);
      41              //6.0将ass添加为视图前台页面类的引用程序集
      42              provider.AssemblyBuilder.AddAssemblyReference(ass);
      43        }
    4. 在MVC.Site主网站中的Global.asax文件中注入重写的控制器和自定义的视图即可,源码如下:
       1    // 注意: 有关启用 IIS6 或 IIS7 经典模式的说明,
       2        // 请访问 http://go.microsoft.com/?LinkId=9394801
       3        using MVC.Plugs.Framework;
       4        public class MvcApplication : System.Web.HttpApplication
       5        {
       6            protected void Application_Start()
       7            {
       8                AreaRegistration.RegisterAllAreas();
       9    
      10                WebApiConfig.Register(GlobalConfiguration.Configuration);
      11                FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
      12                RouteConfig.RegisterRoutes(RouteTable.Routes);
      13                BundleConfig.RegisterBundles(BundleTable.Bundles);
      14    
      15                //将创建控制器的任务由父类工厂转交给重写的工厂
      16                ControllerBuilder.Current.SetControllerFactory(new PluginControllerFactory());
      17    
      18                //将mvc默认的视图查找删除
      19                ViewEngines.Engines.Clear();
      20                //注册自己写的视图查询方案
      21                ViewEngines.Engines.Add(new PluginViewsEngine());
      22            }
      23   }

       

      新手上路,很多东西都不懂简化,只能累赘地写出来,通过该实例明白了mvc在底层是怎样智能识别浏览器的url请求(通过反射去读取根目录下的所有dll,然后遍历查出请求的controller)和如何去遍历Razor视图(通过findView方法去遍历视图集合),不喜勿喷。。。。。

  • 相关阅读:
    网站安全检测
    Centos下Subversion 服务器安装配置
    报错:1130-host ... is not allowed to connect to this MySql server 开放mysql远程连接 不使用localhost
    八个免费在线网站速度测试服务-分析影响网页加载因素提高网站访问速度
    Python处理HTML转义字符
    atitit.TokenService  token服务模块的设计
    Atitit.木马 病毒 免杀 技术 360免杀 杀毒软件免杀 原理与原则 attilax 总结
    atitit.TokenService  token服务模块的设计
    Atitit.atijson 类库的新特性设计与实现 v3 q31
    Atitit.atijson 类库的新特性设计与实现 v3 q31
  • 原文地址:https://www.cnblogs.com/jean69/p/3644186.html
Copyright © 2011-2022 走看看