public void ToString_TemplateRoundtrips(string template) { var routePattern = RoutePatternFactory.Parse(template); var sb = new StringBuilder(); RoutePatternWriter.WriteString(sb, routePattern.PathSegments); Assert.Equal(template, sb.ToString()); }
// CreateEndpoints processes the route pattern, replacing area/controller/action parameters with endpoint values // Because of default values it is possible for a route pattern to resolve to multiple endpoints private int CreateEndpoints( List <Endpoint> endpoints, ref StringBuilder patternStringBuilder, ActionDescriptor action, int routeOrder, RoutePattern routePattern, IReadOnlyDictionary <string, object> allDefaults, IReadOnlyDictionary <string, object> nonInlineDefaults, string name, RouteValueDictionary dataTokens, IDictionary <string, IList <IParameterPolicy> > allParameterPolicies, bool suppressLinkGeneration, bool suppressPathMatching, List <Action <EndpointModel> > conventions) { var newPathSegments = routePattern.PathSegments.ToList(); var hasLinkGenerationEndpoint = false; // Create a mutable copy var nonInlineDefaultsCopy = nonInlineDefaults != null ? new RouteValueDictionary(nonInlineDefaults) : null; var resolvedRouteValues = ResolveActionRouteValues(action, allDefaults); for (var i = 0; i < newPathSegments.Count; i++) { // Check if the pattern can be shortened because the remaining parameters are optional // // e.g. Matching pattern {controller=Home}/{action=Index} against HomeController.Index // can resolve to the following endpoints: (sorted by RouteEndpoint.Order) // - / // - /Home // - /Home/Index if (UseDefaultValuePlusRemainingSegmentsOptional( i, action, resolvedRouteValues, allDefaults, ref nonInlineDefaultsCopy, newPathSegments)) { // The route pattern has matching default values AND an optional parameter // For link generation we need to include an endpoint with parameters and default values // so the link is correctly shortened // e.g. {controller=Home}/{action=Index}/{id=17} if (!hasLinkGenerationEndpoint) { var ep = CreateEndpoint( action, resolvedRouteValues, name, GetPattern(ref patternStringBuilder, newPathSegments), newPathSegments, nonInlineDefaultsCopy, routeOrder++, dataTokens, suppressLinkGeneration, true, conventions); endpoints.Add(ep); hasLinkGenerationEndpoint = true; } var subPathSegments = newPathSegments.Take(i); var subEndpoint = CreateEndpoint( action, resolvedRouteValues, name, GetPattern(ref patternStringBuilder, subPathSegments), subPathSegments, nonInlineDefaultsCopy, routeOrder++, dataTokens, suppressLinkGeneration, suppressPathMatching, conventions); endpoints.Add(subEndpoint); } UpdatePathSegments(i, action, resolvedRouteValues, routePattern, newPathSegments, ref allParameterPolicies); } var finalEndpoint = CreateEndpoint( action, resolvedRouteValues, name, GetPattern(ref patternStringBuilder, newPathSegments), newPathSegments, nonInlineDefaultsCopy, routeOrder++, dataTokens, suppressLinkGeneration, suppressPathMatching, conventions); endpoints.Add(finalEndpoint); return(routeOrder); string GetPattern(ref StringBuilder sb, IEnumerable <RoutePatternPathSegment> segments) { if (sb == null) { sb = new StringBuilder(); } RoutePatternWriter.WriteString(sb, segments); var rawPattern = sb.ToString(); sb.Length = 0; return(rawPattern); } }