public bool BuildUrl(Route route, RequestContext requestContext, RouteValueDictionary userValues, out string value) { value = null; if (requestContext == null) { return(false); } RouteData routeData = requestContext.RouteData; RouteValueDictionary defaultValues = route != null ? route.Defaults : null; RouteValueDictionary ambientValues = routeData.Values; if (defaultValues != null && defaultValues.Count == 0) { defaultValues = null; } if (ambientValues != null && ambientValues.Count == 0) { ambientValues = null; } if (userValues != null && userValues.Count == 0) { userValues = null; } // Check URL parameters // It is allowed to take ambient values for required parameters if: // // - there are no default values provided // - the default values dictionary contains at least one required // parameter value // bool canTakeFromAmbient; if (defaultValues == null) { canTakeFromAmbient = true; } else { canTakeFromAmbient = false; foreach (KeyValuePair <string, bool> de in parameterNames) { if (defaultValues.ContainsKey(de.Key)) { canTakeFromAmbient = true; break; } } } bool allMustBeInUserValues = false; foreach (KeyValuePair <string, bool> de in parameterNames) { string parameterName = de.Key; // Is the parameter required? if (defaultValues == null || !defaultValues.ContainsKey(parameterName)) { // Yes, it is required (no value in defaults) // Has the user provided value for it? if (userValues == null || !userValues.ContainsKey(parameterName)) { if (allMustBeInUserValues) { return(false); // partial override => no match } if (!canTakeFromAmbient || ambientValues == null || !ambientValues.ContainsKey(parameterName)) { return(false); // no value provided => no match } } else if (canTakeFromAmbient) { allMustBeInUserValues = true; } } } // Check for non-url parameters if (defaultValues != null) { foreach (var de in defaultValues) { string parameterName = de.Key; if (parameterNames.ContainsKey(parameterName)) { continue; } object parameterValue = null; // Has the user specified value for this parameter and, if // yes, is it the same as the one in defaults? if (userValues != null && userValues.TryGetValue(parameterName, out parameterValue)) { object defaultValue = de.Value; if (defaultValue is string && parameterValue is string) { if (String.Compare((string)defaultValue, (string)parameterValue, StringComparison.Ordinal) != 0) { return(false); // different value => no match } } else if (defaultValue != parameterValue) { return(false); // different value => no match } } } } // Check the constraints RouteValueDictionary constraints = route != null ? route.Constraints : null; if (constraints != null && constraints.Count > 0) { HttpContextBase context = requestContext.HttpContext; bool invalidConstraint; foreach (var de in constraints) { if (!Route.ProcessConstraintInternal(context, route, de.Value, de.Key, userValues, RouteDirection.UrlGeneration, out invalidConstraint) || invalidConstraint) { return(false); // constraint not met => no match } } } // We're a match, generate the URL var ret = new StringBuilder(); bool canTrim = true; // Going in reverse order, so that we can trim without much ado int tokensCount = tokens.Length - 1; for (int i = tokensCount; i >= 0; i--) { PatternToken token = tokens [i]; if (token == null) { if (i < tokensCount && ret.Length > 0 && ret [0] != '/') { ret.Insert(0, '/'); } continue; } if (token.Type == PatternTokenType.Literal) { ret.Insert(0, token.Name); continue; } string parameterName = token.Name; object tokenValue; #if SYSTEMCORE_DEP if (userValues.GetValue(parameterName, out tokenValue)) { if (!defaultValues.Has(parameterName, tokenValue)) { canTrim = false; if (tokenValue != null) { ret.Insert(0, tokenValue.ToString()); } continue; } if (!canTrim && tokenValue != null) { ret.Insert(0, tokenValue.ToString()); } continue; } if (defaultValues.GetValue(parameterName, out tokenValue)) { object ambientTokenValue; if (ambientValues.GetValue(parameterName, out ambientTokenValue)) { tokenValue = ambientTokenValue; } if (!canTrim && tokenValue != null) { ret.Insert(0, tokenValue.ToString()); } continue; } canTrim = false; if (ambientValues.GetValue(parameterName, out tokenValue)) { if (tokenValue != null) { ret.Insert(0, tokenValue.ToString()); } continue; } #endif } // All the values specified in userValues that aren't part of the original // URL, the constraints or defaults collections are treated as overflow // values - they are appended as query parameters to the URL if (userValues != null) { bool first = true; foreach (var de in userValues) { string parameterName = de.Key; #if SYSTEMCORE_DEP if (parameterNames.ContainsKey(parameterName) || defaultValues.Has(parameterName) || constraints.Has(parameterName)) { continue; } #endif object parameterValue = de.Value; if (parameterValue == null) { continue; } var parameterValueAsString = parameterValue as string; if (parameterValueAsString != null && parameterValueAsString.Length == 0) { continue; } if (first) { ret.Append('?'); first = false; } else { ret.Append('&'); } ret.Append(Uri.EscapeDataString(parameterName)); ret.Append('='); if (parameterValue != null) { ret.Append(Uri.EscapeDataString(de.Value.ToString())); } } } value = ret.ToString(); return(true); }