《ASP.NET本质论》MVC处理程序

来源:互联网 发布:优酷视频解析 知乎 编辑:程序博客网 时间:2024/05/04 12:05

        在ASP.NET MVC2 下,引入了 Controller 的概念,请求将被首先路由到 Controller 进行处理,然后由Controller 分配到相应的 Action完成实际的处理工作。处理的数据结果就是所谓的 Modal 。然后,这个Modal 被传递给View转换成显示的界面元素,最终发送到客户端完成处理任务。
  在ASP.NET MVC2 下,整个MVC的路由中心也是一个处理程序,由这个处理程序再通过一个控制器的工厂来取得实际的 Controller,开始处理工作。默认情况下,这个处理程序的类型是 MvcRouteHandler ,这个处理程序可以在 routes.MapRoute 方法中指定。例如 ,默认的 MVC 设置如下所示:

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

MVC的路由接口 ITouteHandler
        在ASP.NET中,起着路由作用的处理程序不是一个普通的处理程序,因为对于MVC的请求来说根本不是通过请求的扩展名来判断的,所以,这个处理程序通过一个类型为UrlRoutingModule 的Module配置在网站中,通过HttpApplication的事件管道得到这个处理程序。
      在ASP.NET MVC中,路由处理程序必须实现接口 IRouteHandler。 IRouteHandler 定义在命名空间 System.Web.Routing 下,定义了一个获取MVC处理程序的方法 GetHttpHandler。代码如下所示:
 public interface IRouteHandler    {        IHttpHandler GetHttpHandler( RequestContext requestContext);    }

默认情况下,ASP.NET MVC2 使用MvcRouteHandler 这个处理程序,RouteCollection还提供了Add方法 ,可以通过提供一个Route类型的参数指定特定的路由处理程序。设置的方法如下:
 public static void RegisterRoutes(RouteCollection routes)        {            routes.Add(new Route("Catagory/{action}/{categoryName}",new CategoryTouteHandler()));        }

自定义的IRouteHandler
         下面,我们实现一个能够显示当前路由表的路由处理程序。如果用户请求地址的最后为(?routeinfo),那么,将返回一个匹配当前路由的描述。
          首先是类的定义,这个类必须实现接口 IRouteHandler,在GetHttpHandler中,我们检查当前请求是否以 ?routeinfo 结束。如果是的话,调用自定义的方法返回当前的路由描述串,否则,通过默认的MvcRouteHandler 返回控制器。 参考代码:

public class ShowRouteHandler:IRouteHandler    {        static readonly Regex regex = new Regex(@"^\?routeinfo{1}quot;,RegexOptions.IgnoreCase);        #region IRouteHandler Members        public IHttpHandler GetHttpHandler(RequestContext requestContext)        {            if (regex.IsMatch(requestContext.HttpContext.Request.Url.Query))            {                OutputReouteTable(requestContext);            }            IRouteHandler handler = new MvcRouteHandler();            return handler.GetHttpHandler(requestContext);        }        #endregion        private void OutputReouteTable(RequestContext requestContext)        {            HttpResponseBase response = requestContext.HttpContext.Response;            System.Web.Routing.RouteData data = requestContext.RouteData;            Table table = new Table();            foreach (Route route in RouteTable.Routes)            {                TableRow row = new TableRow();                table.Rows.Add(row);                TableCell cell = new TableCell();                row.Cells.Add(cell);                Label lbl = new Label();                lbl.Text = route.Url;                cell.Controls.Add(lbl);                if (route.GetRouteData(requestContext.HttpContext) != null)                {                    lbl.ForeColor = System.Drawing.Color.Black;                }                else                {                    lbl.ForeColor = System.Drawing.Color.Gray;                }                HtmlTextWriter writer = new HtmlTextWriter(response.Output);                table.RenderControl(writer);                response.End();            }        }    }

  注册路由处理程序
            在Global.asax的RegisterToutes方法中,使用自定义的路由处理程序替换默认的路由处理程序。代码如下所示:
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            );            */            RouteTable.Routes.Add(                new Route(                    "{controller}/{action}/{id}",                    new RouteValueDictionary(                        new { Action="Index",id=(string)null}),                    new ShowRouteHandler()                        )                );        }


获取控制器的工厂接口  IControllerFactory
      在ASP.NET MVC2 中,获取控制器的工厂接口为 IControllerFactory,这个接口定义在命名空间System.Web.Mvc下,具体的定义如下:
public interface IControllerFactory{     IController CreateController(RequestContext requestContext,string controllerName);    void ReleaseController(IController controller);}

默认情况下,我们使用定义在命名空间System.Web.Mvc 下的 DefaultControllerFactory
实现。这个类的定义如下:
public class DefaultControllerFactory : IControllerFactory

MVC 请求的处理过程

处理步骤

说明

当网站应用程序收到第一次请求的时候实施路由

在Global.asax中,将Route对象添加到RouteTable对象中

UrlRoutingModule使用第一个匹配的Route对象创建RouteData参数对象,然后创建RequestContext对象

创建MVC路由处理对象

MvcRouteHandler对象创建一个MvcHandler类的对象实例

创建控制器

MvcHandler对象通过IControllerFactory对象,一般使用DefaultControllerFactory 这个实现类创建控制器对象的实例

执行控制器

MvcHandler调用控制器的Execute方法

调用Action

Controller检查被调用的Action,然后执行Action方法

处理返回结果

Action方法接受用户的输入参数,处理后得到准备显示的数据,通过返回一个ViewResultRedirectResultContentResultJsonResultFileResultEmptyResult




资源处理程序
        资源处理程序 AssemblyResourceLoader运行程序员通过HTTP访问嵌入在网站程序集中的资源,例如:script脚本、图片或者数据文件等。
       AssemblyResourceLoader 通过 GetWebResourceUrl 方法得到一个嵌入资源的地址,通过这个地址,浏览器可以通过请求得到嵌入的资源。具体资源地址通过请求参数传递给AssemblyResourceLoader,地址形式如下:
    WebResource.axd?d=<encrypted identifier>&t=<time stamp value>
        参数中的 <encrypted identifier> 用来唯一标识这个嵌入的Web资源, <time stamp value>是一个时间戳,当程序集发生变化的时候可以被检测出来,以使缓冲的资源失效。
       资源的处理程序配置
       资源处理程序已经在系统的web.config中进行了配置,可以直接在程序集中使用,代码如下所示:

   <add path="WebResource.axd" verb="GET" type="System.Web.Handlers.AssemblyResourceLoader" validate="True" />

定义嵌入的资源
     参见:http://support.microsoft.com/kb/910445/zh-cn
     WebResourceAttribute标签必须附加在嵌入资源的程序集中。WebResourceAttribute标签用来说明嵌入在程序集中的资源。它接受两个参数:一个嵌入资源的名称,另一个是资源的MIME类型。
      资源的名称并不一定是资源的文件名,这个名称是由资源所在的命名空间加上资源文件名组成,如果一个文件在一个文件夹中,相当于增加了一层与文件夹同名的命名空间。注册和使用的时候,都是以资源名称为准的。

获取资源的地址
       如果需要在HTTP中使用嵌入的资源,必须获取嵌入资源的地址,这可以通过ClientScriptManager 对象的方法 GetWebResourceUrl 得到。通常我们通过页面对象的ClientScritp属性来得到这个对象,方法的定义如下所示:

public string GetWebResourceUrl(Type type,string resourceName)

其中type表示资源在的的程序集中定义的某个对象类型,resourceName表示资源的名称。
       GetWebResourceUrl方法返回一个没有经过编码的URL地址,使用这个地址可以得到嵌入在网站程序集的资源,资源可以是脚本、图片或者任何静态的文件。


禁止的处理程序
       对于Web服务器上的一些资源,是不允许客户端直接请求获取的,例如,网站的配置文件、App_Code文件夹中的代码文件等。
      对于不允许客户端获取的文件,可以将这些请求的处理程序映射到一个特殊的处理程序来解决,这个处理程序简单地返回一个禁止访问的回应即可。在ASP.NET中这个处理程序的类型是 HttpForbiddenHandler
        HttpForbiddenHandler 直接返回一个状态码为 403 的HTTP回应,来表示访问被拒绝。可以使用它来禁止所希望禁止的任何请求,在系统的web.config中,已经将项目文件、代码文件以及其他类型被禁止客户端访问的文件映射到了这个处理程序。
 <add path="*.asax" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />            <add path="*.ascx" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />            <add path="*.master" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />            <add path="*.skin" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />

也可以自定义增加 ,比如增加一个禁止访问Excel 

  <add path="*.xls" verb="*" type="System.Web.HttpForbiddenHandler" validate="True" />


虚拟路径提供器

        定义虚拟路径提供器
       默认情况下,我们在网站文件夹中加入一个aspx页面模板文件,就可以通过客户端的浏览器进行访问,看到生成的HTML。在这个过程中,网站应用程序又是如何找到这个aspx模板文件的呢?
        在ASP.NET网站应用程序中,网站应用程序中的资源可以分为两类,一类是固定定位的,另一类是可通过虚拟目录动态访问的。
        第一类的资源都是必须保存在固定位置,使用固定命名规则的资源:

Global.asas,全局应用程序类

Web.config,网站配置文件

使用XmlSiteMapProvide 的站点地图文件
bin文件夹下面的程序集,App_Code文件夹下面的代码文件,全局资源文件夹App_GlobalResources下面的资源,任何的本地资源App_LocalResources
App_Data,网站应用程序的数据文件夹


      第二类资源是通过虚拟目录访问的资源。包括以下的类型:

ASP.NET页面、母板页、用户空,一起其他生成的对象

标准的Web资源,例如,扩展名为.htm和.jpg的资源等
任何映射到BuildProvider实例的自定义扩展
在App_Theme文件夹中的命名主题

         对于第二类资源,默认情况下是直接在网站的文件中进行定位,比如说,我们访问网站根目录下的default.aspx,那么网站应用程序将在网站的根目录下寻找这个default.aspx文件。在ASP.NET 2.0之后,通过虚拟目录访问的资源的过程是可以定义的。
      定义在System.Web.Hosting命名空间下的抽象基类 VirtualPathProvider定义了定位资源的约定如下:

public abstract class VirtualPathProvider : MarshalByRefObject

在网站中,虚拟目录通过类型VirtualDirectory表示,这个类型也定义在同一命名空间下:
public abstract class VirtualDirectory : VirtualFileBase

虚拟目录中的文件通过VirtualFile表示:
public abstract class VirtualFile : VirtualFileBase

对于VirtualFile来说,可以通过对象的Open方法获得一个字节流,以便读取文件的时间内容:
public abstract Stream Open()

     当现实自定义的VirtulaPathProvider的时候,必须至少实现下面列出的两个方法:
FileExists和GetFile
     如果实现中还涉及虚拟文件系统中的目录,那么还必须实现关于目录的两个方法:
DirectoryExists 和 GetDirectory。


          注册虚拟路径提供器
           必须注册自定义的VirtualPathProvider,才能在网站应用程序中使用。注册必须在第一次通过虚拟文件系统定位资源之前进行。在 .NET 4.0之前,有两种注册的方法:通过Global.asax中的Application_Start事件注册,或者通过定义在App_Code文件夹中任意类的AppInitialize静态方法。方法的原型定义如下:

http://msdn.microsoft.com/ZH-CN/library/bhesyfec(v=VS.90)

http://support.microsoft.com/kb/910441/zh-cn