您的位置:首页 > 编程语言 > ASP

带你读开源—ASP.NET_MVC(七)

2016-09-09 20:47 267 查看
我们继续谈细节。
我们注意到ASP.NETMVC项目都包含一个Global.asax文件,这个文件代码很少,只包括三个方法,即RegisterGlobalFilters、RegisterRoutes、Application_Start。

我们看RegisterRoutes方法的定义(代码段1)。

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
);
}


代码段 1
代码段1中的routes.MapRoute语句是路由映射的具体实现,我们在MVC源码中看其定义(代码段2)。

[SuppressMessage("Microsoft.Design","CA1054:UriParametersShouldNotBeStrings", MessageId = "2#",Justification = "This is not a regular URL as it may contain specialrouting characters.")]
public static Route MapRoute(this RouteCollection routes, string name,string url, object defaults, object constraints, string[] namespaces)
{
if (routes == null)
{
throw new ArgumentNullException("routes");
}
if (url == null)
{
throw new ArgumentNullException("url");
}

Route route = new Route(url, new MvcRouteHandler())
{
Defaults = CreateRouteValueDictionaryUncached(defaults),
Constraints = CreateRouteValueDictionaryUncached(constraints),
DataTokens = new RouteValueDictionary()
};

ConstraintValidation.Validate(route);

if ((namespaces != null) && (namespaces.Length > 0))
{
route.DataTokens[RouteDataTokenKeys.Namespaces] = namespaces;
}

routes.Add(name, route);

return route;
}


代码段 2
代码段2中route用一个MvcRouteHandler来实例化,即封装了MVC路由处理机制。我们完全可以自定义一个IRouteHandler的实现,例如代码段3:

publicclass MyRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new MyHandler();
}
}


代码段 3
代码段3中的MyHandler定义见代码段4。

publicclass MyHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
context.Response.Write("HELLO");
}
}


代码段 4
我们现在把代码段1中的routes.MapRoute这句代码,更换为代码段5。

routes.Add("MyRoute", newRoute("My", new MyRouteHandler()));


代码段 5
好了,现在我们按下F5进行调试,在浏览器地址栏输入http://localhost:48580/My,回车,看见图1了没?浏览器显示我们在代码段4中希望输出的“HELLO”。这里我们自己定义了一个新的路由规则,即当我们访问http://localhost:48580/My的时候,就会调用我们自定义的MyRouteHandler,成就感有木有?有木有?当然,我们自定义的这条路由规则URL只是静态匹配,它非常简单,以至于简单到不能再简单,以至于毫无用途,但我们毕竟通过这个例子,明白了MVC路由系统的大概端倪。当然,代码段5中的静态URL(My)也可以更换成动态URL,比如“{My}/{Love}”,这样当我们访问http://localhost:48580/hello/kitty时,就会调用我们自定义的路由。可见,ASP.NET的路由系统把花括号“{}”中的内容看作一个变量(或者占位符),之间用斜杠“/”分割,并以此来匹配浏览器输入的URL字符串。



图 1
前面我们谈控制器时,没有提到控制器是如何实例化的,下面我们进入这个话题。MVC框架中的controller由控制器工厂进行实例化(代码段6),而系统预置的控制器工厂是DefaultControllerFactory,它实现了IControllerFactory接口。

// Instantiate the controller andcall Execute
factory =ControllerBuilder.GetControllerFactory();
controller = factory.CreateController(RequestContext,controllerName);


代码段 6
我们看看DefaultControllerFactory的具体实现(代码段7),这段代码中最核心的语句是【Type controllerType =GetControllerType(requestContext, controllerName);[1] IController controller = GetControllerInstance(requestContext,controllerType); [2]】,这两句的功能是:首先根据路由传递过来的Controller名称得到控制器的类型;再根据控制器类型,创建IController的具体实例。

public virtual IControllerCreateController(RequestContext requestContext, string controllerName)
{
if (requestContext == null)
{
throw newArgumentNullException("requestContext");
}

if(String.IsNullOrEmpty(controllerName) &&!requestContext.RouteData.HasDirectRouteMatch())
{
throw newArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}

Type controllerType =GetControllerType(requestContext, controllerName);
IController controller =GetControllerInstance(requestContext, controllerType);
return controller;
}


代码段 7
我们跟踪进入语句[1]的具体定义(代码段8),可以看到其中有三句很扎眼的注释【// first search in the current route's namespace collection】、【// then search in the application's default namespace collection】、【// if all else fails, search every namespace】,这些注释清楚的告诉我们MVC框架搜索控制器的顺序。

protected internal virtual TypeGetControllerType(RequestContext requestContext, string controllerName)
{
if (requestContext == null)
{
throw newArgumentNullException("requestContext");
}

if(String.IsNullOrEmpty(controllerName) &&
(requestContext.RouteData ==null || !requestContext.RouteData.HasDirectRouteMatch()))
{
throw newArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}

RouteData routeData =requestContext.RouteData;
if (routeData != null &&routeData.HasDirectRouteMatch())
{
returnGetControllerTypeFromDirectRoute(routeData);
}

// first search in the currentroute's namespace collection
object routeNamespacesObj;
Type match;
if(routeData.DataTokens.TryGetValue(RouteDataTokenKeys.Namespaces, outrouteNamespacesObj))
{
IEnumerable<string>routeNamespaces = routeNamespacesObj as IEnumerable<string>;
if (routeNamespaces != null&& routeNamespaces.Any())
{
HashSet<string>namespaceHash = new HashSet<string>(routeNamespaces,StringComparer.OrdinalIgnoreCase);
match =GetControllerTypeWithinNamespaces(routeData.Route, controllerName,namespaceHash);

// the UseNamespaceFallbackkey might not exist, in which case its value is implicitly "true"
if (match != null ||false.Equals(routeData.DataTokens[RouteDataTokenKeys.UseNamespaceFallback]))
{
// got a match or theroute requested we stop looking
return match;
}
}
}

// then search in the application'sdefault namespace collection
if(ControllerBuilder.DefaultNamespaces.Count > 0)
{
HashSet<string> namespaceDefaults = newHashSet<string>(ControllerBuilder.DefaultNamespaces,StringComparer.OrdinalIgnoreCase);
match =GetControllerTypeWithinNamespaces(routeData.Route, controllerName,namespaceDefaults);
if (match != null)
{
return match;
}
}

// if all else fails, search everynamespace
returnGetControllerTypeWithinNamespaces(routeData.Route, controllerName, null /* namespaces*/);
}


代码段 8
这三条注释所作用的语句中都用到了GetControllerTypeWithinNamespaces方法,其实现见代码段9。

private TypeGetControllerTypeWithinNamespaces(RouteBase route, string controllerName,HashSet<string> namespaces)
{
// Once the master list ofcontrollers has been created we can quickly index into it
ControllerTypeCache.EnsureInitialized(BuildManager);

ICollection<Type>matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName,namespaces);
switch (matchingTypes.Count)
{
case 0:
// no matching types
return null;

case 1:
// single matching type
return matchingTypes.First();

default:
// multiple matching types
throwCreateAmbiguousControllerException(route, controllerName, matchingTypes);
}
}


代码段 9
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  .net mvc asp.net 开源