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

 

posted on 2012-04-12 16:36  wolfram  阅读(2992)  评论(5编辑  收藏  举报

导航