/// <summary> /// Adds a new outbound route to the <see cref="TreeRouter"/>. /// </summary> /// <param name="handler">The <see cref="IRouter"/> for handling the link generation.</param> /// <param name="routeTemplate">The <see cref="RouteTemplate"/> of the route.</param> /// <param name="requiredLinkValues">The <see cref="RouteValueDictionary"/> containing the route values.</param> /// <param name="routeName">The route name.</param> /// <param name="order">The route order.</param> /// <returns>The <see cref="OutboundRouteEntry"/>.</returns> public OutboundRouteEntry MapOutbound( IRouter handler, RouteTemplate routeTemplate, RouteValueDictionary requiredLinkValues, string routeName, int order) { if (handler == null) { throw new ArgumentNullException(nameof(handler)); } if (routeTemplate == null) { throw new ArgumentNullException(nameof(routeTemplate)); } if (requiredLinkValues == null) { throw new ArgumentNullException(nameof(requiredLinkValues)); } var entry = new OutboundRouteEntry() { Handler = handler, Order = order, Precedence = RoutePrecedence.ComputeOutbound(routeTemplate), RequiredLinkValues = requiredLinkValues, RouteName = routeName, RouteTemplate = routeTemplate, }; var constraintBuilder = new RouteConstraintBuilder(_constraintResolver, routeTemplate.TemplateText); foreach (var parameter in routeTemplate.Parameters) { if (parameter.InlineConstraints != null) { if (parameter.IsOptional) { constraintBuilder.SetOptional(parameter.Name); } foreach (var constraint in parameter.InlineConstraints) { constraintBuilder.AddResolvedConstraint(parameter.Name, constraint.Constraint); } } } entry.Constraints = constraintBuilder.Build(); entry.Defaults = new RouteValueDictionary(); foreach (var parameter in entry.RouteTemplate.Parameters) { if (parameter.DefaultValue != null) { entry.Defaults.Add(parameter.Name, parameter.DefaultValue); } } OutboundEntries.Add(entry); return(entry); }
private VirtualPathData GenerateVirtualPath( VirtualPathContext context, OutboundRouteEntry entry, TemplateBinder binder) { // In attribute the context includes the values that are used to select this entry - typically // these will be the standard 'action', 'controller' and maybe 'area' tokens. However, we don't // want to pass these to the link generation code, or else they will end up as query parameters. // // So, we need to exclude from here any values that are 'required link values', but aren't // parameters in the template. // // Ex: // template: api/Products/{action} // required values: { id = "5", action = "Buy", Controller = "CoolProducts" } // // result: { id = "5", action = "Buy" } var inputValues = new RouteValueDictionary(); foreach (var kvp in context.Values) { if (entry.RequiredLinkValues.ContainsKey(kvp.Key)) { var parameter = entry.RouteTemplate.GetParameter(kvp.Key); if (parameter == null) { continue; } } inputValues.Add(kvp.Key, kvp.Value); } var bindingResult = binder.GetValues(context.AmbientValues, inputValues); if (bindingResult == null) { // A required parameter in the template didn't get a value. return(null); } var matched = RouteConstraintMatcher.Match( entry.Constraints, bindingResult.CombinedValues, context.HttpContext, this, RouteDirection.UrlGeneration, _constraintLogger); if (!matched) { // A constraint rejected this link. return(null); } var pathData = entry.Handler.GetVirtualPath(context); if (pathData != null) { // If path is non-null then the target router short-circuited, we don't expect this // in typical MVC scenarios. return(pathData); } var path = binder.BindValues(bindingResult.AcceptedValues); if (path == null) { return(null); } return(new VirtualPathData(this, path)); }