• asp.net mvc的Routing,Controller,Filter学习笔记


    1、深入学习Routing

    首先Routing的处于的位置,一个Http请求过来,Web容器接收到以后,将信息交给Routing(路由组件),Routing再进行处理~那么Routing的作用

    确定Controller确定Action确定其他参数根据识别出来的数据, 将请求传递给Controller和Action.

    小提示:asp.net mvc预览版的时候,Routing组件还是作为asp.net mvc的一部分,后续的版本似乎就微软将其编译成一个独立的组件提供System.Web.Routing.dll,也就是说asp.net mvc项目是开源的,但是Routing组件并没有开源。Routing组件不仅在asp.net mvc中可以使用,也可以在WebForm中使用

    首先我们新建一个asp.net mvc2/3的项目,新建好以后,直接运行为什么访问localhost/home/index会传递给 HomeController中名为index的action(即HomeController类中的index方法)?怎么实现的呢?
    在我们新建的项目中,Global.asax文件中有以下方法
    注册路由
           public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
                routes.MapRoute(
                    "Default", // Route name
                    "{controller}/{action}/{id}", // URL with parameters
                    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
                );
    
            }

    Routes是Application级别(全局)的,在Application开始的时候,程序注册路由,新建的项目默认只注册了一条路由,看下代码,

         routes.MapRoute(
         "Default", // Route name
         "{controller}/{action}/{id}", // URL with parameters
         new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
         );

    MapRoute第一个参数是此路由的名称,第二个参数是URL的参数形式,{controller}相当于是一个string.Format方法中的占位符,意思是这里可以是任意一个controller名称,

    同理,action、id也是一样的,那为什么请求的/Home/Index并没有Id这个参数,第三个参数是路由规则默认值,这条路由默认的controller是home,action是index,而id呢,是可选的~~~当我们请求/Home/Index的时候,会被此路由获取,而我们直接请求http://localhost的时候,可以到Home/Index的时候,路由参数有默认值

     Ok,到这里我们学习了如何注册路由,我们来试着自己写一条路由

    自定义路由
           routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
                //自定义路由,
                routes.MapRoute(
                    "myRoute", // Route name
                    "{controller}-{action}", // URL with parameters
                    new { controller = "Home", action = "Index" } // Parameter defaults
                );
                routes.MapRoute(
                    "Default", // Route name
                    "{controller}/{action}/{id}", // URL with parameters
                    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
                );

    然后重新启动程序,因为Routes是Application级别的,在我们修改Application级别信息后,应该退出,否则不会有效果滴,这个是好多初学者容易犯错的地方.如果是生产环境,应该重新启动下Web容器.

    我们运行程序以后,请求域名/home-index一样可以请求页面,说明我们自定义的路由有效.

    这里路由的规则非常灵活,我们可以自定义,以下的路由规则都可以

         routes.MapRoute(
         "myRoute", // Route name
         "{controller}-{action}-{1}-{2}-{3}", // URL with parameters
         new { controller = "Home", action = "Index" } // Parameter defaults
         );

    MapRoute()方法

    MapRoute有以下的重载方法

    MapRoute( string name, string url);

    MapRoute( string name, string url, object defaults);

    MapRoute( string name, string url, string[] namespaces);

    MapRoute( string name, string url, object defaults, object constraints);

    MapRoute( string name, string url, object defaults, string[] namespaces);

    MapRoute( string name, string url, object defaults, object constraints, string[] namespaces);

    name参数: 规则名称, 可以随意起名.不可以重名,否则会发生错误: 路由集合中已经存在名为“Default”的路由。路由名必须是唯一的。

    url参数: url获取数据的规则, 这里不是正则表达式,  将要识别的参数括起来即可, 比如: {controller}/{action} 最少只需要传递name和url参数就可以建立一条Routing(路由)规则.比如实例中的规则完全可以改为: routes.MapRoute( "Default", "{controller}/{action}");

    defaults参数: url参数的默认值.如果一个url只有controller: localhost/home/ 而且我们只建立了一条url获取数据规则: {controller}/{action} 那么这时就会为action参数设置defaults参数中规定的默认值. defaults参数是Object类型,所以可以传递一个匿名类型来初始化默认值: new { controller = "Home", action = "Index" } 实例中使用的是三个参数的MapRoute方法: routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "" } // Parameter defaults );

    constraints参数: 用来限定每个参数的规则或Http请求的类型.constraints属性是一个RouteValueDictionary对象,也就是一个字典表, 但是这个字典表的值可以有两种: 用于定义正则表达式的字符串。正则表达式不区分大小写。 一个用于实现 IRouteConstraint 接口且包含 Match 方法的对象。 通过使用正则表达式可以规定参数格式,比如controller参数只能为4位数字: new { controller = @"\d{4}"}

      通过第IRouteConstraint 接口目前可以限制请求的类型.因为System.Web.Routing中提供了HttpMethodConstraint类, 这个类实现了IRouteConstraint 接口. 我们可以通过为RouteValueDictionary字典对象添加键为"httpMethod", 值为一个HttpMethodConstraint对象来为路由规则添加HTTP 谓词的限制, 比如限制一条路由规则只能处理GET请求: httpMethod = new HttpMethodConstraint( "GET", "POST" )

    View Code
                 routes.MapRoute( 
                     "Default", // Route name 
                     "{controller}/{action}/{id}", // URL with parameters 
                     new { controller = "Home", action = "Index", id = "" }, // Parameter defaults 
                     new { controller = @"\d{4}" , httpMethod = new HttpMethodConstraint( "GET", "POST" ) } 
                     );

    我们注册这样的一条路由

    自定义带过滤的路由
          routes.MapRoute(
                    "test", // Route name
                    "{controller}-{action}-{id}", // URL with parameters
                    new { controller = "Home", action = "Index" }, // Parameter defaults
                    new { controller = @"^\w ", action = @"^\w ", id = @"^\d " }
                );

    编译且运行,当我们请求/home-index-11可以请求到,而我们/home-index-1x这样的是不能匹配的,

    那这样的规则的又有什么用处呢?在这里可以对请求进行过滤,比如我们的id只能是数字类型,防止一些非法检测

    路由组件的调试

    假设我们写了N条路由,当我们发送请求的时候,并没有被我们想要的规则所捕获解析,so..我们需要调试。

    在我们的项目添加RouteDebug.dll的引用,并在Global.asax文件中的Application_Start方法添加以下代码

    启用路由调试
            protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
                RegisterRoutes(RouteTable.Routes);
                //启动路由表调试
                RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
            }

    编译后,我们启动程序,会发现有一个Route Tester页面,关于RouteDebug匹配的页面信息,请大家查询RouteDebug的手册.

    Route Tester中的Route Data请求就是当前请求的路由信息

     我们请求/home/index,从图中可以看到被第4条路由捕获到.

      那这样的路由有什么作用呢?我想大部分做SEO的朋友都有经验,二级页面和三级页面,爬虫抓取的频率显然是不一样的,这样我们可以将三级甚至更深层的页面构造成二级页面~这个也是SEO的技巧之一

    注意:我们在写路由规则的时候,因为路由规则有前后顺序(指注册的先后顺序),也许写的路由规则被它前面的规则给捕获到,进行处理。那后面注册的路由就无效

    2、Controller学习

      在ASP.NET MVC中, 一个Controller可以包含多个Action. 每一个Action都是一个方法, 返回一个ActionResult实例.

       ActionResult类包括ExecuteResult方法, 当ActionResult对象返回后会执行此方法.

      Controller 处理流程:

      1. 页面处理流程 发送请求 –> UrlRoutingModule捕获请求 –> MvcRouteHandler.GetHttpHandler() –> MvcHandler.ProcessRequest

      2.MvcHandler.ProcessRequest() 处理流程: 使用工厂方法获取具体的Controller –> Controller.Execute() –> 释放Controller对象

      3.Controller.Execute() 处理流程: 获取Action –> 调用Action方法获取返回的ActionResult –> 调用ActionResult.ExecuteResult() 方法

      4.ActionResult.ExecuteResult() 处理流程: 获取IView对象-> 根据IView对象中的页面路径获取Page类-> 调用IView.RenderView() 方法(内部调用Page.RenderView方法)

      Controller对象的职责是传递数据,获取View对象(实现了IView接口的类),通知View对象显示.

      View对象的作用是显示.虽然显示的方法RenderView()是由Controller调用的,但是Controller仅仅是一个"指挥官"的作用, 具体的显示逻辑仍然在View对象中.

         注意IView接口与具体的ViewPage之间的联系.在Controller和View之间还存在着IView对象.对于ASP.NET程序提供了 WebFormView对象实现了IView接口.WebFormView负责根据虚拟目录获取具体的Page类,然后调用 Page.RenderView()

    Controller中的ActionResult

    在Controller中,每一个Aciton返回都是ActionResult,我们通过查看ActionResult的定义

    ActionResult
        // Summary:
        //     Encapsulates the result of an action method and is used to perform a framework-level
        //     operation on behalf of the action method.
        public abstract class ActionResult
        {
            // Summary:
            //     Initializes a new instance of the System.Web.Mvc.ActionResult class.
            protected ActionResult();
    
            // Summary:
            //     Enables processing of the result of an action method by a custom type that
            //     inherits from the System.Web.Mvc.ActionResult class.
            //
            // Parameters:
            //   context:
            //     The context in which the result is executed. The context information includes
            //     the controller, HTTP content, request context, and route data.
            public abstract void ExecuteResult(ControllerContext context);
        }

    关于ActionResult的派生类,大家可以参考:http://blog.csdn.net/steven_husm/article/details/4641281

    3、Filter的学习

    mvc项目中,action在执行前或执行后想做一些特殊的操作,比如身份校验、行为截取等,asp.net mvc提供了以下几种默认的Filter

    ASP.NET MVC 框架支持以下几种筛选器:

    1、授权筛选器– 实现了 IAuthorizationFilter 接口

      这一类的筛选器用来实现用户验证和对Action的访问授权。比如Authorize 就属于Authorization 筛选器。

    2、Action 筛选器– 实现了 IActionFilter 接口

      它可以包含一些Action执行前或者执行后的逻辑,比如有一些筛选器专门用来修改Action返回的数据。

    3、Result 筛选器– 实现了 IResultFilter 接口

      它可以包含一些view result生成前或者生成后的逻辑,比如有一些筛选器专门用来修改视图向浏览器展现前的结果。

    4、异常筛选器– 实现了IExceptionFilter 接口

      它用以用来处理Action或者Result的错误,也可以记录错误。

         筛选器的默认执行顺序也和上面的列出的序号相同,比如Authorization 筛选器会先于Action 筛选器执行,而Exception 筛选器总会在最后执行。当然你也可以根据需要通过Order属性设定筛选器执行的顺序。

    下面通过一个实际的例子来说明应用,新建一个mvc3项目,在项目中新加一个Common文件夹,并新加一个类LogUserOperationAttribute.cs

    LogUserOperationAttribute
        public class LogUserOperationAttribute : ActionFilterAttribute
        {
            public override void OnResultExecuted(ResultExecutedContext filterContext)
            {
                //todo写日志代码,这里注意并发性
                File.AppendAllText(@"C:\log.txt", string.Format("{0}日志", DateTime.Now));
                base.OnResultExecuted(filterContext);
            }
        }

    新加一个HomeControllers,并在HomeControllers中新加一个Action--Index以及视图文件

    HomeController
        public class HomeController : Controller
        {
            //
            // GET: /Home/
            [LogUserOperation]
            public ActionResult Index()
            {
                return View();
            }
        }

    然后保存代码,F5运行,当页面显示出来以后,在C盘已经有log.txt文件.

    abstract class ActionFilterAttribute这四个方法分别代表,Action执行时,Action执行后,Result返回时,Result返回后。(它们的执行顺序跟下图一致)

         当然,我们也可以在ASP.NET MVC 3.0中增加Global Action Filter,这个就把此类filter变成全局的filter,所有Controller的action都会通过个filter的规则来执行,它跟我们在Controller或某个action所标识的属性有很大的区别,就是全局跟部分的区别。对于Global Action Filter 有着很多的应用,比如系统的权限、系统异常的处理

    Gloable Filter实际应用--系统异常处理体系

    我们程序运行过程中会有各种不可预料的情况,执行某个Action会发生异常,那我们异常信息需要记录下来,我们可以像上面的例子一样,在Action或Controller上打上标记,但是那么多action和Controller都去打标记,确实是很痛苦的事情,而asp.net mvc3有一个全局的Filter,我们只需要将我们自定义的Filter注册成全局的Filter

    在Common文件夹中,新建一个类CustomHandleErrorAttribute.cs

    CustomHandleErrorAttribute
        public class CustomHandleErrorAttribute : IExceptionFilter 
        {
            public void OnException(ExceptionContext filterContext)
            {
                File.AppendAllText(@"C:\error.txt", string.Format("{0}错误{1}", DateTime.Now, filterContext.Exception.Message));
            }
        }

    在Global.asax文件中,RegisterGlobalFilters方法写注册代码

    注册全局Filters
         public static void RegisterGlobalFilters(GlobalFilterCollection filters)
            {
                filters.Add(new CustomHandleErrorAttribute());//系统的逻辑错误通过这个filters来处理
                filters.Add(new HandleErrorAttribute());
            }

     在HomeController中新加一个Action--->Error

    HomeController
        public class HomeController : Controller
        {
            //
            // GET: /Home/
            [LogUserOperation]
            public ActionResult Index()
            {
                return View();
            }
            public ActionResult Error()
            {
                try
                {
                    System.IO.File.Open("C:\\111111.exe", System.IO.FileMode.Open);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                return View();
            }
        }

    编译后运行项目~我们请求/Home/Error这个action,,程序出现异常,我们切换到C盘,发现C盘已经有error.txt文件
    注册到全局的Filters所有的ActionResult执行前后都会调用我们的CustomHandleErrorAttribute的重写的方法。

    GlobalFilters、ControllerFilters、ActionFilters的执行顺序问题

    GlobalFilters-->ControllerFilters-->ActionFilters《这个是有执行的前置条件的》

       当然这是在CustomHandleErrorAttribute类的定义上打上标记[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]的前提下。不然 如果Action打上了标签跟Controller的相同则它只会执行Action上的Filter

  • 相关阅读:
    修改代码一般在测试服务器
    人很臭尽量往香里去做...
    救赎
    写的css十个错误
    如何debug看源代码
    办公室倒水
    程序和思维
    mousewheel.js 和scroll api
    drupal.behavior 和 document.ready 没有直接的关系
    revision in drupal
  • 原文地址:https://www.cnblogs.com/hornet/p/2808810.html
Copyright © 2020-2023  润新知