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

ASP.NET MVC 源码分析(二) —— 从 IRouteBuilder认识路由构建

2015-06-06 18:01 801 查看
  我们来看IRouteBuilder的定义:

public interface IRouteBuilder
{
IRouter DefaultHandler { get; set; }

IServiceProvider ServiceProvider { get; }

IList<IRouter> Routes { get; }

IRouter Build();
}


一个默认的IRouter对象,一个Build方法,一个IRouter集合和一个获取服务对象IServiceProvider。

我们进一步看IRouteBuilder的实现RouterBuilder:

public class RouteBuilder : IRouteBuilder
{
public RouteBuilder()
{
Routes = new List<IRouter>();
}

public IRouter DefaultHandler { get; set; }

public IServiceProvider ServiceProvider { get; set; }

public IList<IRouter> Routes
{
get;
private set;
}

public IRouter Build()
{
var routeCollection = new RouteCollection();

foreach (var route in Routes)
{
routeCollection.Add(route);
}

return routeCollection;
}
}


View Code
主要的实现是Build方法,这个方法的实现也很简单,遍历Routes向一个实现了IRouter接口的RouteCollection对象添加IRouter,我们可以先看一下RouteCollection的实现:

public class RouteCollection : IRouteCollection
{
private readonly List<IRouter> _routes = new List<IRouter>();
private readonly List<IRouter> _unnamedRoutes = new List<IRouter>();
private readonly Dictionary<string, INamedRouter> _namedRoutes =
new Dictionary<string, INamedRouter>(StringComparer.OrdinalIgnoreCase);

private RouteOptions _options;

public IRouter this[int index]
{
get { return _routes[index]; }
}

public int Count
{
get { return _routes.Count; }
}

public void Add([NotNull] IRouter router)
{
var namedRouter = router as INamedRouter;
if (namedRouter != null)
{
if (!string.IsNullOrEmpty(namedRouter.Name))
{
_namedRoutes.Add(namedRouter.Name, namedRouter);
}
}
else
{
_unnamedRoutes.Add(router);
}

_routes.Add(router);
}

public async virtual Task RouteAsync(RouteContext context)
{
for (var i = 0; i < Count; i++)
{
var route = this[i];

var oldRouteData = context.RouteData;

var newRouteData = new RouteData(oldRouteData);
newRouteData.Routers.Add(route);

try
{
context.RouteData = newRouteData;

await route.RouteAsync(context);
if (context.IsHandled)
{
break;
}
}
finally
{
if (!context.IsHandled)
{
context.RouteData = oldRouteData;
}
}
}
}

public virtual VirtualPathData GetVirtualPath(VirtualPathContext context)
{
EnsureOptions(context.Context);

// If we're using Best-Effort link generation then it means that we'll first look for a route where
// the route values are validated (context.IsBound == true). If we can't find a match like that, then
// we'll return the path from the first route to return one.
var useBestEffort = _options.UseBestEffortLinkGeneration;

if (!string.IsNullOrEmpty(context.RouteName))
{
var isValidated = false;
VirtualPathData bestPathData = null;
INamedRouter matchedNamedRoute;
if (_namedRoutes.TryGetValue(context.RouteName, out matchedNamedRoute))
{
bestPathData = matchedNamedRoute.GetVirtualPath(context);
isValidated = context.IsBound;
}

// If we get here and context.IsBound == true, then we know we have a match, we want to keep
// iterating to see if we have multiple matches.
foreach (var unnamedRoute in _unnamedRoutes)
{
// reset because we're sharing the context
context.IsBound = false;

var pathData = unnamedRoute.GetVirtualPath(context);
if (pathData == null)
{
continue;
}

if (bestPathData != null)
{
// There was already a previous route which matched the name.
throw new InvalidOperationException(
Resources.FormatNamedRoutes_AmbiguousRoutesFound(context.RouteName));
}
else if (context.IsBound)
{
// This is the first 'validated' match that we've found.
bestPathData = pathData;
isValidated = true;
}
else
{
Debug.Assert(bestPathData == null);

// This is the first 'unvalidated' match that we've found.
bestPathData = pathData;
isValidated = false;
}
}

if (isValidated || useBestEffort)
{
context.IsBound = isValidated;

if (bestPathData != null)
{
bestPathData = new VirtualPathData(
bestPathData.Router,
NormalizeVirtualPath(bestPathData.VirtualPath),
bestPathData.DataTokens);
}

return bestPathData;
}
else
{
return null;
}
}
else
{
VirtualPathData bestPathData = null;
for (var i = 0; i < Count; i++)
{
var route = this[i];

var pathData = route.GetVirtualPath(context);
if (pathData == null)
{
continue;
}

if (context.IsBound)
{
// This route has validated route values, short circuit.
return new VirtualPathData(
pathData.Router,
NormalizeVirtualPath(pathData.VirtualPath),
pathData.DataTokens);
}
else if (bestPathData == null)
{
// The values aren't validated, but this is the best we've seen so far
bestPathData = pathData;
}
}

if (useBestEffort)
{
return new VirtualPathData(
bestPathData.Router,
NormalizeVirtualPath(bestPathData.VirtualPath),
bestPathData.DataTokens);
}
else
{
return null;
}
}
}

private PathString NormalizeVirtualPath(PathString path)
{
var url = path.Value;

if (!string.IsNullOrEmpty(url) && _options.LowercaseUrls)
{
var indexOfSeparator = url.IndexOfAny(new char[] { '?', '#' });

// No query string, lowercase the url
if (indexOfSeparator == -1)
{
url = url.ToLowerInvariant();
}
else
{
var lowercaseUrl = url.Substring(0, indexOfSeparator).ToLowerInvariant();
var queryString = url.Substring(indexOfSeparator);

// queryString will contain the delimiter ? or # as the first character, so it's safe to append.
url = lowercaseUrl + queryString;
}

return new PathString(url);
}

return path;
}

private void EnsureOptions(HttpContext context)
{
if (_options == null)
{
_options = context.RequestServices.GetRequiredService<IOptions<RouteOptions>>().Options;
}
}
}


乍一看这个类的功能还是比较庞大的,我们主要关注他对IRouter接口签名的实现:

Task RouteAsync(RouteContext context):

通过代码我们可以看到,这个方法主要对RouteCollection本身持有的Route 规则循环添加到路由上下文RouteContext.RouteData中。

VirtualPathData GetVirtualPath(VirtualPathContext context):


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