路由原理,mvc西路由的映射和兑现IHttpHandler挂载
分类:计算机编程

首先我们了解一下一般的方法

    我们只需要在web.config配置文件中做映射处理即可。

 1、概要

当我们新建一个MVC项目时,打开他的Web.Config文件可以发现

    <httpModules>
      <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> 我们知道ScriptModule 类就是管理用于 ASP.NET 中 AJAX 功能的 HTTP 模块,在此我们不做介绍
      <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> //这个UrlRoutingModule类才是重点
    </httpModules>

这个HttpModule,拦截所有请求,对请求进行处理,最终创建和执行合适的处理请求的HttpHandler(MVC3之后,这个UrlRoutingModule集成到MVC程序集中了)。

 

  当客户端在本地浏览器上输入网址来请求咱们的一个MVC程序时,服务端接收到请求.....此处省略N个字(和asp.net处理一样).....

  HttpApplication的事件注册,即将 UrlRoutingModule 注册到HttpApplication的事件中

public class UrlRoutingModule : IHttpModule
{
    protected virtual void Init(HttpApplication application)
    {   //开始只是把要执行的具体方法注册到事件中,等待事件被触发时,在执行已被注册的方法。
        application.PostResolveRequestCache  = new EventHandler(this.OnApplicationPostResolveRequestCache); 
        application.PostMapRequestHandler  = new EventHandler(this.OnApplicationPostMapRequestHandler);
    }
}

  注册完事件之后,那么就要开始执行HttpApplication事件。

1、执行Global.asax文件中Application_Start方法。
即:在此处将一个自己定义的路由规则注册到路由集合中。这个路由集合可以由RouteTable.Routes获得。

 protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RegisterRoutes(RouteTable.Routes);
        }

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                "Default", // 路由名称
                "{controller}/{action}/{id}", // 带有参数的 URL
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值
            ); //在路由表里添加一条路由规则
        }

本章将讲述ASP.NET MVC5 的路由原理,即URL映射机制。

ASP.NET路由[ASP.NET Routing]

  ASP.NET路由允许你在使用URL时不必匹配到网站中具体的文件,因为这个URL不必匹配到一个文件,你使用了描述用户行为且更容易被用户理解的URL。

  ASP.NET MVC框架和ASP.NET动态数据(Dynamic Data)扩展路由为MVC应用和动态数据应用增加了特色。

  在不使用路由的ASP.NET应用中,一个新的请求会被映射到一个物理文件并由该文件处理这个请求,例如一个.aspx文件。例如,如下请求会题映射到一个包含代码和标签来向浏览器渲染响应的Products.aspx文件。Web页面人员使用id=4的查询字符来确定显示的内容。

  使用ASP.NET路由,你可以定义映射请求-处理程序(request-handler)文件的URL模式,但没必要在将这些文件的名字包含在这个URL中。除此之外,你还可以通过在URL模式中使用占位符来向请求处理程序传输变量数据,而不必使用查询字符串。

  例如,如下请求 ,路由分析器会向页面处理器传入Products,show,beverages这些值。在这个例子中,如果使用server/application/{area}/{action}/{category}URL模式定义路由,页面处理器将会收到一个字典集合,这个集合中包含以下键值对,area:Products,action:show,category:beverages。如果是在一个不被URL路由管理的请求中,/Products/show/beverages片断将会被当作应用中的一个文件路径解释执行。

第一种形式:

 <system.web>
    <urlMappings enabled="true">

      <add url="~/d" mappedUrl="SmackBio.WebSocketSDK.GenericHandler"/>

    </urlMappings>

注释:这里的url就是我们需要在请求的具体写法,然后mappedUrl则是我们实际项目中的处理位置。

第二种形式:

 <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>    
      <add path="/socket" verb="*" name="GenericHandler" type="SmackBio.WebSocketSDK.GenericHandler"/>
    </handlers>
  </system.webServer>

注释:这里的path就是我们请求的入口地址,type则是我们的实际项目中的方法类位置。

2、依次执行HttpApplication的事件。  

BeginRequest
AuthenticateRequest
PostAuthenticateRequest
AuthorizeRequest
PostAuthorizeRequest
ResolveRequestCache
PostResolveRequestCache 在UrlRoutingModule类中,在此事件中注册了一个执行方法,即:OnApplicationPostResolveRequestCache
PostMapRequestHandler                                                       OnApplicationPostMapRequestHandler
AcquireRequestState
PostAcquireRequestState
PreRequesHandlerExecute
PostRequeshandlerExecute
ReleaseRequesState
PostReleaseRequestState
UpdateRequestCache
PostUpdateRequestCach
LogRequest
PostLogRequest
EndRequest

OnApplicationPostResolveRequestCache方法

private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
{
    HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context);
    this.PostResolveRequestCache(context);
}
//这里用HttpContextWrapper类包装当前的HttpContext,实质上也是一个请求的上下文。他可以使用诸如Typemock Isolator或Rhino Mocks的Mock对象框进行模拟变得更简单。
//并把这个包装之后的上下文作为PostResolveRequestCache的参数

------------------------------------------------------------------------------------------------------------------

public virtual void PostResolveRequestCache(HttpContextBase context)
{
    RouteData routeData = this.RouteCollection.GetRouteData(context);
//GetRouteData方法内部遍历路由集合中的每个路由对象去和上下文中指定的请求URL去匹配。如成功,就返回当前的路由对象RouteData,如不成功,返回null
//this.RouteCollection就是RouteTable.Routes,即:路由集合。

    if (routeData != null)----这里便是判断是否匹配成功
    {
        IRouteHandler routeHandler = routeData.RouteHandler;----//获取一个处理当前匹配成功的路由的对象
     //这个routeHandler其实就是一个MvcRouteHandle类
     ----因为在第一句中执行的GetRouteData方法中,为RouteData的RouteHandler属性赋值为MvcRouteHandler
        if (routeHandler == null)
        {
            throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoRouteHandler, new object[0]));
        }
        if (!(routeHandler is StopRoutingHandler))
        {
            RequestContext requestContext = new RequestContext(context, routeData);//把当前的请求的信息和与当前请求匹配成功的路由信息再包装起来。
            IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
            //根据包装后的请求信息,最终得到一个MvcHandler
       ---MvcRouteHandler中只有一个方法,GetHttpHandler方法,返回MvcHandler,Mvc中处理请求的类。
       ---PageRouteHandler中也只有一个方法,GetHttpHandler方法,返回的是Page,asp.net中处理请求的类。 

图片 1图片 2MvcRouteHandler

    public class MvcRouteHandler : IRouteHandler
    {

        protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new MvcHandler(requestContext);
        }

        IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
        {
            return this.GetHttpHandler(requestContext);
        }
    }

 

          if (httpHandler == null)

            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoHttpHandler, new object[] { routeHandler.GetType() }));
            }
            //RequestData类是UrlRoutingModule类中嵌套的一个私有类,把处理请求的类和当前请求的虚拟路径
            RequestData data2 = new RequestData {
                OriginalPath = context.Request.Path,
                HttpHandler = httpHandler
            };
            context.Items[_requestDataKey] = data2;
            //把封装的处理类MvcHandler和请求的虚拟路径,赋值到 HttpContextWrapper类中。(这样在用到处理类时,就需要实例化,直接取值即可)
            //HttpContextWrapper类包装当前的HttpContext,实质上也是一个请求的上下文。
            context.RewritePath("~/UrlRouting.axd");

context.RemapHandler(httpHandler);//博客是里这里是这么一句,但是我反编译没找到,可能是版本的问题吧!
//将MvcHandler 实例 映射到管线中(通常我们是利用web.config 进行配置的,但是MvcHandler 没有默认无参构造函数,所以直接通过向其传递一个实例进行映射)

} } } 

OnApplicationPostMapRequestHandler方法
该方法做的事情很简单,就是重写下请求路径,让输出的路径和输入的路径相同,在这里用来记忆输入路径的是context.Items[],从上下两段代码中可以看到.
这个事件负责根据文件扩展名映射到具体的httphandle处理类,而MVC的URL信息没有具体的文件后缀名 为了使处理模块能够在iis7中实现路由,则采取了这么一种简单的解决办法。先把路径指向~/UrlRouting.axd,在此事件中会设置一个UrlRouting.axd类型的Handler避免报错,并在下一步事件中替换掉此处的Handler再把~/UrlRouting.axd这个路径给改回来。

图片 3图片 4View Code

private void OnApplicationPostMapRequestHandler(object sender, EventArgs e)
{
    HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context);
    this.PostMapRequestHandler(context);
}



 public virtual void PostMapRequestHandler(HttpContextBase context)
{
    RequestData data = (RequestData) context.Items[_requestDataKey];
    if (data != null)
    {
        context.RewritePath(data.OriginalPath);
        context.Handler = data.HttpHandler;
    }
}

 

上文中得到了一个MvcHandler类实例,MvcHandler继承实现了IHttpAsyncHandler, IHttpHandler, IRequiresSessionState三个接口。而这三个接口如果都实现了,在MVC框架下是不是任何http请求就可以通吃了吗?从MSDN我们得知,事实不是这样的:注意,即使 MvcHandler 实现 IHttpHandler,也不能将其映射为处理程序(例如.mvc 文件扩展名),因为该类不支持无参数构造函数。 (它唯一的构造函数需要一个 RequestContext 对象) 但是,还好,我们还有MvcHttpHandler。

如你所知,MvcHttpHandler可以“弥补”MvcHandler的不足,为什么这样说呢?因为MvcHandler没有无参的构造函数,因此即使MvcHandler实现了 IHttpHandler接口,在IIS中也不能将其映射为某类文件扩展名的处理程序,而MvcHttpHandler就提供了不通过路由模块的情况下直接处理映射的处理程序。

  • MvcHttpHandler.使用此处理程序可便于实现直接处理程序映射(不通过路由模块)。如果要将文件扩展名(如 .mvc)直接映射到一个 MVC 处理程序,此处理程序将非常有用。在内部,MvcHttpHandler 将执行 ASP.NET 路由通常执行(通过 MvcRouteHandler 和 MvcHandler)的任务。但是,它是作为处理程序而不是作为模块来执行这些任务的。对所有请求启用 UrlRoutingModule 时,通常不使用此处理程序。
  • MvcHandler.此处理程序负责启动 MVC 应用程序的 ASP.NET 管道。它从 MVC 控制器工厂接收 Controller 实例;此控制器处理请求的进一步处理。请注意,即使 MvcHandler 实现了 IHttpHandler,它也不能映射为处理程序(例如,针对 .mvc 文件扩展名),因为该类不支持无参数构造函数(而处理程序要求是无参数构造函数)。(其唯一的构造函数需要 RequestContext 对象。)

简单点就是解释:为什么MVC在浏览器输入地址就能访问到类(或类中的方法)?这是怎么做到的?我自己可以通过.NET写出一个自己的MVC框架吗?

一、      路由[Routes]

  路由是被处理程序映射的URL模式。处理程序可以是一个物理文件,例如Web Form应用中的.aspx文件。处理也可以是处理请求的类,例如MVC应用中的控制器。为了定义一个路由,你需要创建一个Route类的实例来指定URL模式,处理程序和可选的路由名称。

  你需要给RouteTable类的Routes静态属性添加Route对象来为应用添加路由。Routes属性是一个RouteCollection对象,其中存放着应用中所有路由规则。

  你通常没有必要为MVC应用编写代码添加来添加路由规则。Visual Studio的MVC项目模板包含了预配置的URL路由规则。他们定义在MvcApplication类,这个类在Global.asax文件中。

mvc路由配置方法

这是我们不同使用的映射形式。但是在mvc路由中我们挂起一般处理程序却发现行不通了,下面我们就要配置路由方法进行映射。

在mvc中我们分为三步:

    1.实现处理代码程序(实现一般处理程序继承类IHttpHandler)

图片 5图片 6

 1   public class GenericHandler : IHttpHandler
 2     {
 3         public void ProcessRequest(HttpContext context)
 4         {
 5             if (context.IsWebSocketRequest || context.IsWebSocketRequestUpgrading)
 6             {
 7                 context.AcceptWebSocketRequest(new SBWebSocketHandler());
 8             }
 9             else
10             {
11                 context.Response.ContentType = "text/plain";
12                 context.Response.Write("Service running");
13             }
14         }
15 
16         public bool IsReusable
17         {
18             get
19             {
20                 return false;
21             }
22         }
23     }

View Code

    2.定义一个类路由规则(实现路由IRouteHandler接口然后指向处理代码程序类)

图片 7图片 8

 public class PlainRouteHandler : IRouteHandler
    {

        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new GenericHandler();
        }
    }

 public static void RegisterHandler(RouteCollection routes)
        {

            RouteTable.Routes.Add("socket",
                 new Route("socket", new MvcZodiac.Controllers.PlainRouteHandler()));
        }

View Code

 

    3.注册到程序中(在Global.asax中的Application_Start方法注册)

 RegisterHandler(RouteTable.Routes);

 这里补充一下,这句话一定要写在路由注册之前,不然不会起作用。例如:

图片 9

 

 3、HttpApplication事件继续执行

BeginRequest
AuthenticateRequest
PostAuthenticateRequest
AuthorizeRequest
PostAuthorizeRequest
ResolveRequestCache
PostResolveRequestCache 
PostMapRequestHandler                                                       
AcquireRequestState
PostAcquireRequestState
PreRequesHandlerExecute
PostRequeshandlerExecute
ReleaseRequesState
PostReleaseRequestState
UpdateRequestCache
PostUpdateRequestCach
LogRequest
PostLogRequest
EndRequest

在11-12个事件的时候拿到第7个事件的时候创建的MVCHandler对象执行他的ProcessRequest方法。

public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
{
    protected virtual void ProcessRequest(HttpContext httpContext)
    {
        //使用HttpContextWrapper对HttpContext进行封装,封装的目的是为了解耦以获得可测试性.然后从RequestContext.RouteData中提取Controller名称.
        HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
        this.ProcessRequest(httpContext2);
    }

    protected internal virtual void ProcessRequest(HttpContextBase httpContext)
    {
        IController controller;
        IControllerFactory controllerFactory;
        this.ProcessRequestInit(httpContext, out controller, out controllerFactory); //获取到Controler实例       ----下节详细介绍
        try
        {
                controller.Execute(this.RequestContext); //当前Controler对象的Action的创建与执行                   ----下节详细介绍   
                执行包括:加载TempData, 创建及执行Action,处理Action返回的ActionResult ,保存TempData数据。
        }
        finally
        {
            controllerFactory.ReleaseController(controller); //释放当前Controler对象
        }
    }
}

流程如下图,MvcHandler实例来处理请求,他做为处理的主干,当完成之后,释放当前的Controler实例,继续执行HttpApplication事件

图片 10

 此图摘自:

答案是:可以。

二、      URL模式[URL Patterns]

  一个URL模式可以包含字面值(literal)和可变的占位符(参考URL参数)。这些字面值和占位符在URL片断中通过斜线(/)字符来分开和定位。

  当一个请求到达,这个URL被解析成片断和占位符,这些变量会提供给请求处理器。这个过程和将数据通过查询字符串(query strings)中解析和传输至请求处理器很类似。这两种情况下变量信息都会包含在URL中并传到处理程序的键值对的表单中。对于查询字符串而言,所有键(keys)和值(values)都包含在URL中。对于路由方式,所有键就是在URL模式中定义的占位符名称,只有值包含中URL中。

  在一个URL模式中,你定义的占位符被大括号包裹起来({and})。你可以在一个片断中定义多个占位符,但是它们必须被字面值分隔开。例如,{language}-{country}/{action}就是一个合法的路由模式。然而{language}{country}/{action}不是一个合法的路由模式,因为它们的占位符之间缺少字面值或分隔符。因此,路由无法确定language和country占位符的值是何处分隔。

  下表给出了合法的路由模式,以及其各自能正确匹配的URL请求。

路由定义

匹配URL示例

{controller}/{action}/{id}

/Products/show/beverages

{table}/Details.aspx

/Products/Details.aspx

blog/{action}/{entry}

/blog/show/123

{reporttype}/{year}/{month}/{day}

/sales/2008/1/5

{locale}/{action}

/US/show

{language}-{country}/{action}

/en-US/show

模拟URL映射

先来看一个Demo,在传统的.NET WebForms项目中,实现URL的拦截。

打开VS2013,新建一个“ASP.NET Web窗体应用程序”项目,并取名为Demo4URLRouting。

图片 11

为了方便测试,注释掉Default.aspx页面的内容和模板引用。这样做以后,看起来是这样

图片 12

然后新建一个ControllerFactory类,实现IHttpHandler接口。

图片 13图片 14

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 
 6 namespace Demo4URLRouting
 7 {
 8     /// <summary>
 9     /// 自己写的ControllerFactory,试图扮演MVC5中的RouteConfig(路由配置)角色。
10     /// </summary>
11     public class ControllerFactory : IHttpHandler
12     {
13         ControllerFactory()
14         { 
15         }
16 
17         public bool IsReusable
18         {
19             get;
20             set;
21         }
22 
23         public void ProcessRequest(HttpContext context)
24         {
25             context.Response.Write(string.Format("ControllerFactory来拦截请求-> URL为: {0}",context.Request.RawUrl));
26         }
27     }
28 }

View Code

下一步,打开Web.config配置文件,在system.webServer节点下添加一项handlers配置

图片 15

代码如下:

图片 16图片 17

<!--for url routing test Start-->
    <handlers>
      <add name="ControllerFactory" verb="*" path="*Account/*" type="Demo4URLRouting.ControllerFactory,Demo4URLRouting" preCondition="integratedMode"/>
    </handlers>
    <!--for url routing test End-->

View Code

此配置的意图是:拦截站点URL中包含Account关键字的全部URL地址。

按F5运行项目,在地址栏站点后,输入account,发现已成功被ControllerFactory类拦截。

图片 18

继续,输入/account/login。发现地址未被拦截。而是跳转到了默认项目的登陆页面。

图片 19

怎么回事呢?

原来,是项目下的Global.asax文件中的内容引起,注释掉RouteConfig注册的路由。

图片 20

重新输入/account/login,发现已成功被ControllerFactory类拦截。

图片 21

现在我们修改下ControllFactory类中ProcessRequest方法的代码,实现URL和类及类中方法的映射。(注意,代码做了非常简单的处理,我们假定忽略大小写因素,路由格式也和传统MVC类似)

图片 22图片 23

 1 public void ProcessRequest(HttpContext context)
 2         {
 3             context.Response.Write(string.Format("ControllerFactory来拦截请求-> URL为: {0}",context.Request.RawUrl));
 4             context.Response.Write("<br/>");
 5 
 6             /**
 7              * 将拦截的URL地址分发给对应的Controller
 8              **/
 9             //简单处理,截取URL中第一个/和/之间的字符串做为要查找的Controller对象
10             string url = context.Request.RawUrl;
11             string actionURL = url.IndexOf('?') > 0 ? url.Substring(0, url.IndexOf('?')) : url.Substring(0);
12             string[] strArray = actionURL.Split('/');
13             //得到类名称
14             string targetClassName = (strArray[1]   "Controller");
15             string methodName = string.Empty;
16             if (strArray.Length > 2 && !string.IsNullOrEmpty(strArray[2]))
17                 methodName = strArray[2];//以'/'做分割,类名称后为方法名称
18             /**
19              * 从URL获取类名称之后,利用反射实现方法的调用。
20              **/
21             //获取Controller实例
22             object instance = Activator.CreateInstance(Type.GetType(string.Format("Demo4URLRouting.Controllers.{0}", targetClassName)));
23             if (instance != null)
24             {
25                 object outputObj = null;//方法输出内容
26                 if (string.IsNullOrEmpty(methodName) || instance.GetType().GetMethod(methodName) == null)
27                     methodName = "Index";//如果url没有输入方法或方法不存在,默认调用Index方法
28                 try
29                 {
30                     //调用实例方法
31                     outputObj = instance.GetType().GetMethod(methodName).Invoke(instance, null);
32                     context.Response.Write(outputObj);
33                 }
34                 catch(MissingMethodException mme)
35                 {
36                     context.Response.Write(string.Format("<font color='red'>Error! Method not Found!</font>  {0}",mme.Message));
37                 }
38             }
39         }

View Code

在代码中,进行了简单的url分析,获取类名和方法名,再通过反射机制,实现调用。

然后,为了配合测试,我们在解决方案中新建一个Controllers文件夹,在该文件夹下新建一个类AccountController,代码如下

图片 24图片 25

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Demo4URLRouting.Controllers
{
    public class AccountController
    {
        public string Index()
        {
            return "this is the AccountController,<b>Index</b> method.";
        }

        public string HelloWorld()
        {
            return "this is <font color='red'>HelloWorld method</font> which is in the Class named AccountController.";
        }

    }
}

View Code

AccountController类中定义了两个方法,Index()和HelloWorld()。然后我们通过浏览器进行访问

图片 26

图片 27

可以看到,通过在浏览器地址中,输入/Account,实现对AccountController类中Index方法的访问;输入/Account/HelloWorld实现对AccountController类中的HelloWorld方法的访问。

到此,我们初步实现了在WebForms环境下,URL到类的映射。此过程的核心是handlers的配置和利用反射原理调用类中的方法。Demo的不足是没有去消除大小写问题,没有更灵活的路由配置等。

那么问题来了,MVC中这一整套URL映射的机制,是如何实现的呢?

MVC应用中标准的URL模式[Typical URL Patterns in MVC Applications]

  在MVC应用中,路由标准的URL模式包含{controller}和 {action}占位符。

  当接收到一个请求时,它先被发送到UrlRoutingModule对象,再发送到MvcHandler HTTP处理程序。MvcHandler HTTP处理程序确定需要执行的控制器,通过给URL中的controller值添加”Controller”后缀从而确定将处理本次请求的控制器类型名称。URL中的action值确定调用的处理方法。

  例如,URL路径 /Products会被映射成ProductsController控制器。action参数的值是被调用的处理方法的名称。URL路径/Products/show的映射结果将会是调用类ProductsController 的方法Show。

  下表给出了默认的URL模式和它们能处理的URL请求示例。

默认URL模式

匹配URL示例

{controller}/{action}/{id}

http://server/application/Products/show/beverages

{resource}.axd/{*pathInfo}

http://server/application/WebResource.axd?d=...

  路由使用模式 {resource}.axd/{*pathInfo} 来阻止对网络文件的请求,例如WebReource.axd,ScriptResource.axd被传递给一个控制器。

  对于IIS7.0,可以不用扩展名。对于IIS6.0,你必须将扩展名.mvc添加到URL模式,如下所示:{controller}.mvc/{action}/{id}

MVC5 URL映射机制

实际上,在写本篇文章之前,我翻看了大量网上有关MVC的资料,发现大多数对MVC映射机制(也叫MVC路由机制)的描述都非常笼统。

类似不懂装懂,或者让你去理解Model-View-Controller的三层,什么业务分离,或者说了半天说一堆废话。。。我是深恶痛绝的。如果仅仅是解释微软MSDN上能查到的东西?那你的解释意义在哪里?不说了,泡沫信息太多了。

在了解MVC URL映射机制这一套原理之前,你首先要了解ASP.NET Routing。 ASP.NET Routing是.NET的一套独立组件。总的来说,它可以做两件事情:

1. 将URL请求地址的片段转交到handler处理;

  1. 构造(创建)URL地址。

Routing目前的信息实在太少,感兴趣的可以在MSDN.aspx)上做初步了解。总的来说,Routing做了我们前面模拟做的所有事情,而且毋庸置疑,做的更好更强大。

在MVC中,从URL到Controller,简要过程大概是这样:

URL -->  RouteData对象 --> MvcHandler对象 --> IControllerFactory接口 --> Controller 

这个过程很复杂,要详细阐述其过程,估计要三篇文章以上的篇幅。这里就不再阐述。

MVC框架中,RouteConfig类中这段代码已经做了URL到Controller映射的所有工作。你所需要做的,只是匹配Controller了。

图片 28图片 29

    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }

View Code

三、      为Web Forms应用程序添加路由[Add Routes to a Web Forms Application]

  在Web Form应用中,你可以使用类RouteCollection的方法MapPageRoute(String, String, String) 创建路由。方法MapPageRoute创建Route对象并将其添加RouteCollection对象中。你需要为Route对象在参数在指定一些属性,用来传给方法MapPageRoute。

  通常情况下,你在方法中添加的路由会被Global.asax文件中Application_Start 方法处理器调用。这种方法确保了这些路由在应用程序启动时可以正常调用。它也允许你在为应用程序做单元测试时可直接调用该方法。当你在做单元测试时如果想要直接调用一个方法,该方法在注册时就必须是静态(Visual Basic中Shared)且有一个RouteCollection参数。

  下例中演示了Global.asax文件中添加一个Route对象,该对象定义了action和categoryName两个参数。URL中还定义了一个被定向到名为Categories.aspx的物理页面。

图片 30图片 31

protected void Application_Start(object sender, EventArgs e)
{
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapPageRoute("",
        "Category/{action}/{categoryName}",
        "~/categoriespage.aspx");
}

View Code

总结

本篇文章重点讲述了ASP.NET MVC中的URL映射机制。先是通过在WebForms中模拟URL映射,让初学者有一个直观的认识。然后简要的介绍了MVC中URL映射的机制。由于篇幅所限,在介绍URL映射机制时,只做了简要的阐述。因为知识量很大,而且建议读者要有ASP.NET Routing组件的基础。关于Routing组件,以后有空的话,我再单独写文章来讲。

在下一篇文章中,我将讲述Controller的实际应用及扩展,项目中Controller扮演的角色。敬请期待。

本文原始地址。

四、      为MVC应用程序添加路由[Adding Routes to an MVC Application]

  在MVC应用程序中,如果你采用MVC实现控制器的约定,即派生自类ControllerBase且以“Controller”结尾命名,那么你根本不需要搬运添加路由。预配置的路由将会执行你实现的控制器类中的处理方法。

  如果你希望在MVC应用程序中添加自定义的路由,你可以使用方法MapRoute(RouteCollection, String, String) 来取代方法MapPageRoute(String, String, String)。

  下面示例中演示在Global.asax文件中创建默认MVC路由的代码,即是Visual Studio中MVC应用程序的项目模板。

图片 32图片 33

public class MvcApplication : System.Web.HttpApplication
{
    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 = "" }  // Parameter defaults
        );

    }

    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);
    }
}

View Code

五、      为URL参数设置默认值[Setting Default Values for URL Parameters]

  当你定义一个路由时,你可以为参数指定一个默认值。如果一个参数的值不在URL中,则使用默认值。给路由设置默认值时是给类Route的属性Defaults指定一个字典对象。下例演示了如何使用方法MapPageRoute(String, String, String, Boolean, RouteValueDictionary)添加一个包含默认值的路由。

图片 34图片 35

void Application_Start(object sender, EventArgs e) 
{
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapPageRoute("",
        "Category/{action}/{categoryName}",
        "~/categoriespage.aspx",
        true,
        new RouteValueDictionary 
            {{"categoryName", "food"}, {"action", "show"}});
}

View Code

  下图中演示了ASP.NET路由处理URL请求,路由定义(categoryName的默认值为food,action默认为show)和解析结果:

URL

Parameter values

/Category

action = "show" (default value)

categoryName = "food" (default value)

/Category/add

action = "add"

categoryName = "food" (default value)

/Category/add/beverages

action = "add"

categoryName= "beverages"

  对于MVC应用程序,方法RouteCollectionExtensions.MapRoute的重载版本,如MapRoute(RouteCollection, String, String, Object, Object),允许你指定默认值。

六、      URL模式中处理可变数量片断[Handling a Variable Number of Segments in a URL Pattern]

  有时候你必须处理包含可变个数的URL片断的URL请求。当你定义一个路由时,你可以指定当一个URL拥有比模式中还要多的片断时,额外的片断会被当作最后一个片断对待。你需要为最后一个参数添加一个星号(*)来用这种方式处理额外的片断。这被称作全匹配(catch-all)参数。包含全匹配参数也将匹配最后参数不带任何值的URL。下例展示一个可以匹配无法确定片断长度的路由模式。

  query/{queryname}/{*queryvalues}

  下图中演示了ASP.NET路由处理URL请求,路由定义和解析结果。

URL

Parameter values

/query/select/bikes/onsale

queryname = "select"

queryvalues = "bikes/onsale"

/query/select/bikes

queryname = "select"

queryvalues = "bikes"

/query/select

queryname = "select"

queryvalues = Empty string

七、      为路由添加约束[Adding Constraints to Routes]

  路由模式中除了定义了可匹配URL请求的参数个数,还可以指定这些参数的值需要满足的某些约束。如果一个URL中的参数值不符合一个路由的约束,那么此路由不会处理该请求。你添加的约束条件是为了确保URL参数中包含的值能在你的应用程序中工作。

  约束条件是用正则表达式或实现了接口IRouteConstraint的对象来定义。当你将一条路由添加进Routes 集合时,你可以添加包含验证测试的对象RouteValueDictionary约束条件。字典中的键唯一标识符合约束条件的参数。字典中的值既可以是符合正则表达式的字符串,也可以是实现接口IRouteConstraint的对象。

  如果你提供一个字符串,路由会将它当作符合正则表达式的值,并调用Regex类的IsMatch()方法检查该参数值是否符合规则。正则表达式总是不区分大小写检查。

  如果你提供了一个IRouteConstraint对象,ASP.NET路由调用IRouteConstraint对象的Match()方法来检查参数值是否合法。Match()方法返回布尔值来表明参数值是否合法。

  下例中演示如何利用方法MapPageRoute 添加一条路由,并为参数locale和year添加约束。(MVC应用程序中,使用方法MapRoute。)

图片 36图片 37

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapPageRoute("",
        "Category/{action}/{categoryName}",
        "~/categoriespage.aspx",
        true,
        new RouteValueDictionary 
            {{"categoryName", "food"}, {"action", "show"}},
        new RouteValueDictionary 
            {{"locale", "[a-z]{2}-[a-z]{2}"},{"year", @"d{4}"}}
       );
}

View Code

  当路由处理URL请求时,例子中路由的定义和解析后的结果如下表所示:

URL

Result

/US

No match. Both locale and year are required.

/US/08

No match. The constraint on year requires 4 digits.

/US/2008

locale = "US"

year = "2008"

八、      路由不起作用的情况[Scenarios When Routing Is Not Applied]

  在某些情况下,ASP.NET路由即使可用也不会处理请求。这节将介绍几种路由不会处理请求的情况。

URL模式匹配一个有效的物理文件[A Physical File is Found that Matches the URL Pattern]

  默认情况下,路由不会处理请求时,将其映射到Web服务器上一个已存在的物理文件。例如,如果存在一个物理文件Products/Beverages/Coffee.aspx,路由就不会处理请求

  如果你希望路由能处理所有请求,即使请求指向一个文件,你可以通过设置对象RouteCollection的属性RouteExistingFiles为true来重写默认行为。当你将这个值设置为true则所有与定义路由模式匹配的请求都会被路由处理。

显式禁用路由[Routing Is Explicitly Disabled for a URL Pattern]

  你也可以指明路由不处理某些URL请求。定义一个路由并指明由类StopRoutingHandler处理该模式,以阻止路由处理某些请求。当一个请求被StopRoutingHandler对象处理时,StopRoutingHandler对象块会为请求添加一些额外的信息。相反,这个请求会被当作一个ASP.NET页面、Web服务或是其他ASP.NET终端处理。你可以使用方法RouteCollection.Ignore(MVC应用程序中RouteCollectionExtensions.IgnoreRoute)来创建使用类 StopRoutingHandler的路由。下面演示如何阻止对WebResource.axd文件的请求。

图片 38图片 39

public static void RegisterRoutes(RouteCollection routes)
{
  routes.Ignore("{resource}.axd/{*pathInfo}");
}

View Code

  URL是如何被路由匹配的[How URLs Are Matched to Routes]

  当路由开始处理URL请求时,它为尝试着将请求匹配到一条路由规则上。一条路由规则能否匹配URL请求取决与以下条件:

    • 在你的项目中是否包含自定义的路由规则或者默认路由。
    • 集合Routes中添加路由规则的顺序。
    • 为路由规则添加的默认值。
    • 为路由规则添加的约束。
    • 是否定义了匹配对物理文件请求的路由。

  为了避免不合适的路由处理了请求,在定义路由规则时一定要考虑所有这些条件。集合 Routes中Route对象出现的顺序也应仔细思考。路由集合中从第一项到最后一项依次尝试的路由匹配。当有一个匹配成功,不再对后续路由尝试匹配。通常,添加路由时应先添加最具体的,最后添加最不明确的规则。

  例如你添加了如下路由规则:

    •   路由规则1,{controller}/{action}/{id}
    •   路由规则2, products/show/{id}

  路由2绝不会处理请求,因为路由1会最先尝试匹配,并且与路由2匹配的请求在它上面也总是可以工作。例如请求

    •   controller is products.
    •   action is show.
    •   id is bikes.

  当请求的参数缺少时,默认值就能起作用。然而,它们可能会匹配到并非你所愿的请求。例如,假如添加如下两条路由规则:

    •   路由1:{report}/{year}/{month},year,month带有默认值。
    •   路由2:{report}/{year},year有默认值。

  路由2将绝无处理请求的机会。路由1希望能按月份匹配,而路由2则希望是按年度。但是,路由1中的默认值会让所有匹配路由2的请求在路由1中也能工作。

  你可以在路由中包含常量来避免这种歧义,例如annual/{report}/{year}和monthly/{report}/{year}/{month}。

  如果一个URL不能匹配到在RouteTable集合中的任何一个Route对象,ASP.NET路由将不会处理该请求。

九、      从路由创建URL[Creating URLs from Routes]

  如果你想要创建一个链接到你站点页面的超链接,你可以使用URL模式编程创建符合路由的URL。当你修改了路由模式,URL会自动匹配到新的模式上。

十、      在路由页面访问URL参数[Accessing URL Parameters in a Routed Page]

参见:

十一、      配置路由环境[Configuration Settings for Routing]

  ASP.NET中,要让应用程序支持路由功能,需要添加如下配置:

图片 40图片 41

<configuration>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <!-- more -->
    </modules>
  </system.webServer>
</configuration>

View Code

  当runAllManagedModulesForAllRequests为true时,如下URL

  然而,IIS7的更新导致不必添加配置项runAllManagedModulesForAllRequests,因为它本来就支持ASP.NET路由功能。

  如果你的站点运行在IIS7且IIS已更新,你就不必设置该配置项为true。事实上,并不推荐设置它,因为它为所有请求增加了不必要的操作。如果设置该配置为true,所有请求,包括.htm,.jpg和其他静态文件都会通过ASP.NET请求通道。

  默认runAllManagedModulesForAllRequests为false。如果网站配置文件中没有明确将其设置为true,而你又在未安装SP1的Windows 7,不包含必要更新的IIS7中运行你的网站。 结果就是,你会看到路由不会工作的错误提示。如果路由中存在一些问题,你可以试试下面的方法:

    •   将Windows 7更新到SP1,因为它添加了IIS7的更新。
    •   安装微软件在先前文章中的描述的更新。
    •   在Web.Config文件中设置runAllManagedModulesForAllRequests为true。注意这会添加一些额外开销。

十二、      ASP.NET路由与安全性[ASP.NET Routing and Security]

  授权规则可应用于单独映射的路由URL或同时映射的路由URL和物理URL。例如,授权规则可以声明所有用户都可以访问以开头Category的URL,但是只有管理员才能访问Categories.aspx页面。如果路由URL模式contoso.com/Category/{controller}/{action} 映射到物理地址contoso.com/Categoriespage.aspx,你只能为路由地址添加授权规则,当用一个路由地址请求过之后,所有用户都被允许可访问Categoriespage.aspx。然而,当使用物理地址请求之后,只有管理才有权限访问。

  默认情况下,授权规则应用于路由地址和物理地址。

十三、      ASP.NET Web Form和路由安全性[ASP.NET Web Forms and Route Security]

  在ASP.NET Web窗体应用程序中,你不应该将站点的安全性全寄托在路由授权规则,因为它们可能留下一些未保护处理的物理地址。

十四、      ASP.NET MVC和路由安全性[ASP.NET MVC and Route Security]

  你不能使用路由或web.config文件保证MVC应用程序的安全性。唯一能保证MVC应用安全的做法是给所有控制器应用特性 AuthorizeAttribute ,并在登录和注册的方法(action)上应用特性AllowAnonymousAttribute 。

  扩展请查看:

十五、      参考类[Class Reference]

Class

Description

Route

Represents a route in a Web Forms or MVC application.

DynamicDataRoute

Represents a route in a Dynamic Data application.

RouteBase

Serves as the base class for all classes that represent an ASP.NET route.

RouteTable

Stores the routes for an application.

RouteCollection

Provides methods that enable you to manage a collection of routes.

RouteCollectionExtensions

Provides additional methods that enable you to manage a collection of routes in MVC applications.

RouteData

Contains the values for a requested route.

RequestContext

Contains information about the HTTP request that corresponds to a route.

StopRoutingHandler

Provides a way to specify that ASP.NET routing should not handle requests for a URL pattern.

PageRouteHandler

Provides a way to define routes for Web Forms applications.

RouteValueDictionary

Provides a way to store route ConstraintsDefaults, and DataTokensobjects.

VirtualPathData

Provides a way to generate URLs from route information.

十六、      ASP.NET路由VS URL重写[ASP.NET Routing versus URL Rewriting]

  ASP.NET路由与URL重写不同。URL重写方式处理到达的请求时,先修改其URL再将请求发送至Web页面。例如,一个应用程序中可能使用URL重写将/Products/Widgets/修改为/Products.aspx?id=4。同时,URL重写的特点是没有基于你的模式创建URL的API。使用URL重写,如果你需要个性URL规则,你不得不手动更新所有相关联的超链接。

  使用ASP.NET路由,在处理到达的请求时URL不会发生变化,因为路由功能可能从URL中提取值。当你需要创建一个URL时,给一个方法传入参数值就能为你生成URL。修改URL规则,只需要调整一个地方,你在应用程序中创建的所有链接都会自动使用新规则。

 

  源地址:

本文由pc28.am发布于计算机编程,转载请注明出处:路由原理,mvc西路由的映射和兑现IHttpHandler挂载

上一篇:没有了 下一篇:没有了
猜你喜欢
热门排行
精彩图文