技术开发 频道

深入理解ASP.NET MVC框架与自定义参数

    【IT168 评论】请求是如何进入MVC框架的(inbound)

  当一个URL请求到来时,系统调用一个注册的IHttpModules:UrlRoutingModule,它将完成如下工作:

  一、在RouteTable.Route中从第一个Route开始查找第一个匹配当前URL的Route。需要同时满足下面的条件,才能算匹配:

  1.URL匹配Route属性中Url的模型;

  2.所有{}中定义的参数都可以在请求的URL中找到对应,或者参数在Route的Defaults中也有定义,当然Defaults中定义优先级低于URL中的定义,亦或者参数被设置成UrlParameter.Optional;

  3.参数满足Route的Constraints定义的匹配规则,规则可能是一个正则式,或是一个IRouteConstraint对象。

  二、指定匹配的RouteBase将通过GetRouteData方法提供一个RouteData结构,RouteData包含如下四个属性:

  1.Route:Route对象自己;

  2.RouteHandler:一个实现了IRouteHandler的对象,MVC提供一个MvcRouteHandler类,从MapRoute的源码可以看出,总由MvcRouteHandler担任这个角色。MvcRouteHandler知道如何从RouteData中寻找Controller和Action。事实上MvcRouteHandler会寻找键值为"controller"和"action"参数,所以我们永远需要包含"controller"和"action"两个参数;

  3.Values:保存URL中参数的值字典,参数包括{}定义的,Route.Defaults中的,以及从QueryString中获得的;

  4.DataTokens:一个附加的字典,主要用于Areas机制,以后会详细讨论。

  注:如果GetRouteData返回null的话,就无法进入MVC框架,常见的情况是有直接的静态物理文件能够匹配这个url,通常是一些资源文件,如css,image等文件。可以通过设置RouteCollection的RouteExistingFiles为true,使得用于不匹配静态文件(注意true是不匹配,这个命名和奇怪)。下面的代码是GetRouteData中的代码段,说明了这一机制是如何实现的。

if (!this.RouteExistingFiles)
{
    
string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
    
if (((appRelativeCurrentExecutionFilePath != "~/") && (this._vpp != null)) && (this._vpp.FileExists(appRelativeCurrentExecutionFilePath) || this._vpp.DirectoryExists(appRelativeCurrentExecutionFilePath)))
    {
        return
null;
    }
}

 

  三、调用RouteData中的RouteHandler,同时为RouteHandler提供包括RouteData和HttpContextBase对象等上下文变量,并封装成一个叫requestContext的参数传递给RouteHandler。如果使用MvcRouteHandler,那么至此,就进入了MVC框架。

  几点注意点:

  1.由于上面提到的UrlRoutingModule的搜索行为,Route在RouteCollection中添加的顺序就十分重要了。记住:优先添加形式特殊的Route;

  2.MvcRouteHandler在调用Controller的Action的时候会自动为Action提供参数。参数来源于RouteData的键值对,MvcRouteHandler会自动将键名和Action的参数名比较,这种比较是大小写敏感的,所以在Action中写参数名的时候要注意了,关于这点以后会详细展开;

  3.如果希望URL中的参数是可选的,要设置参数为UrlParameter.Optional。

  自定义参数匹配规则

  Route的Constraints定义URL中参数匹配规则,规则可能是一个正则式,或是一个IRouteConstraint对象。熟悉的情况是,通过MapRoute的简化API设置Constraints为一个正则式。但如果我们的匹配规则较复杂呢?事实上,我们可以自己实现一个IRouteConstraint对象,IRouteConstraint只有一个Match方法,所以并不困难。我之前一篇文章ASP.NET MVC的全球化方案中有一个IRouteConstraint的实现。

  还有个例子,MVC框架有个HttpMethodConstraint实现了IRouteConstraint,我们可以像下面这样使用:

routes.MapRoute(null, "Articles/{id}",
new { controller = "Articles", action = "Show" },
new { httpMethod = new HttpMethodConstraint("GET") }
);

 

  这表示只有以GET方式请求的URL才能匹配这个Route。需要注意的是,我们常常会在Action上加上[HttpGet]或[HttpPost]来做类似的限定,但两者完全不同,这里的限定是在MVC框架外的,而[HttpGet]是在框架内的,显然这种方法高效些。

0
相关文章