RouteValueDictionary AddDefaults(RouteValueDictionary dict, RouteValueDictionary defaults) { if (defaults != null && defaults.Count > 0) { string key; foreach (var def in defaults) { key = def.Key; if (dict.ContainsKey(key)) { continue; } dict.Add(key, def.Value); } } return(dict); }
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { RouteValueDictionary translatedValues = values; // > Establecer {lang} if (!values.ContainsKey("lang")) { values.Add("lang", CultureInfo.CurrentCulture.Name); } else if ((string)values["lang"] != CultureInfo.CurrentCulture.Name) { values["lang"] = CultureInfo.CurrentCulture.Name; } // > Traducir valores de la Ruta foreach (KeyValuePair <string, object> pair in this.RouteValueTranslationProviders) { IRouteValueTranslationProvider translationProvider = pair.Value as IRouteValueTranslationProvider; if ( translationProvider != null && translatedValues.ContainsKey(pair.Key) ) { RouteValueTranslation translation = translationProvider.TranslateToTranslatedValue( translatedValues[pair.Key].ToString(), CultureInfo.CurrentCulture ); translatedValues[pair.Key] = translation.TranslatedValue; } } return(base.GetVirtualPath(requestContext, translatedValues)); }
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { if (values.ContainsKey("childAction") && (bool)values["childAction"] == true) { return(base.GetVirtualPath(requestContext, values)); } RouteValueDictionary translatedValues = values; foreach (KeyValuePair <string, object> pair in this.RouteValueTranslationProviders) { IRouteValueTranslationProvider translationProvider = pair.Value as IRouteValueTranslationProvider; if (translationProvider != null && translatedValues.ContainsKey(pair.Key)) { RouteValueTranslation translation = translationProvider.TranslateToTranslatedValue( translatedValues[pair.Key].ToString(), CultureInfo.CurrentCulture); translatedValues[pair.Key] = translation.TranslatedValue; } } return(base.GetVirtualPath(requestContext, translatedValues)); }
public string BuildUrl(Route route, RequestContext requestContext, RouteValueDictionary userValues, RouteValueDictionary constraints, out RouteValueDictionary usedValues) { usedValues = null; if (requestContext == null) { return(null); } RouteData routeData = requestContext.RouteData; var currentValues = routeData.Values ?? new RouteValueDictionary(); var values = userValues ?? new RouteValueDictionary(); var defaultValues = (route != null ? route.Defaults : null) ?? new RouteValueDictionary(); // The set of values we should be using when generating the URL in this route var acceptedValues = new RouteValueDictionary(); // Keep track of which new values have been used HashSet <string> unusedNewValues = new HashSet <string> (values.Keys, StringComparer.OrdinalIgnoreCase); // This route building logic is based on System.Web.Http's Routing code (which is Apache Licensed by MS) // and which can be found at mono's external/aspnetwebstack/src/System.Web.Http/Routing/HttpParsedRoute.cs // Hopefully this will ensure a much higher compatiblity with MS.NET's System.Web.Routing logic. (pruiz) #region Step 1: Get the list of values we're going to use to match and generate this URL // Find out which entries in the URL are valid for the URL we want to generate. // If the URL had ordered parameters a="1", b="2", c="3" and the new values // specified that b="9", then we need to invalidate everything after it. The new // values should then be a="1", b="9", c=<no value>. foreach (var item in parameterNames) { var parameterName = item.Key; object newParameterValue; bool hasNewParameterValue = values.TryGetValue(parameterName, out newParameterValue); if (hasNewParameterValue) { unusedNewValues.Remove(parameterName); } object currentParameterValue; bool hasCurrentParameterValue = currentValues.TryGetValue(parameterName, out currentParameterValue); if (hasNewParameterValue && hasCurrentParameterValue) { if (!ParametersAreEqual(currentParameterValue, newParameterValue)) { // Stop copying current values when we find one that doesn't match break; } } // If the parameter is a match, add it to the list of values we will use for URL generation if (hasNewParameterValue) { if (ParameterIsNonEmpty(newParameterValue)) { acceptedValues.Add(parameterName, newParameterValue); } } else { if (hasCurrentParameterValue) { acceptedValues.Add(parameterName, currentParameterValue); } } } // Add all remaining new values to the list of values we will use for URL generation foreach (var newValue in values) { if (ParameterIsNonEmpty(newValue.Value) && !acceptedValues.ContainsKey(newValue.Key)) { acceptedValues.Add(newValue.Key, newValue.Value); } } // Add all current values that aren't in the URL at all foreach (var currentValue in currentValues) { if (!acceptedValues.ContainsKey(currentValue.Key) && !parameterNames.ContainsKey(currentValue.Key)) { acceptedValues.Add(currentValue.Key, currentValue.Value); } } // Add all remaining default values from the route to the list of values we will use for URL generation foreach (var item in parameterNames) { object defaultValue; if (!acceptedValues.ContainsKey(item.Key) && !IsParameterRequired(item.Key, defaultValues, out defaultValue)) { // Add the default value only if there isn't already a new value for it and // only if it actually has a default value, which we determine based on whether // the parameter value is required. acceptedValues.Add(item.Key, defaultValue); } } // All required parameters in this URL must have values from somewhere (i.e. the accepted values) foreach (var item in parameterNames) { object defaultValue; if (IsParameterRequired(item.Key, defaultValues, out defaultValue) && !acceptedValues.ContainsKey(item.Key)) { // If the route parameter value is required that means there's // no default value, so if there wasn't a new value for it // either, this route won't match. return(null); } } // All other default values must match if they are explicitly defined in the new values var otherDefaultValues = new RouteValueDictionary(defaultValues); foreach (var item in parameterNames) { otherDefaultValues.Remove(item.Key); } foreach (var defaultValue in otherDefaultValues) { object value; if (values.TryGetValue(defaultValue.Key, out value)) { unusedNewValues.Remove(defaultValue.Key); if (!ParametersAreEqual(value, defaultValue.Value)) { // If there is a non-parameterized value in the route and there is a // new value for it and it doesn't match, this route won't match. return(null); } } } #endregion #region Step 2: If the route is a match generate the appropriate URL var uri = new StringBuilder(); var pendingParts = new StringBuilder(); var pendingPartsAreAllSafe = false; bool blockAllUriAppends = false; var allSegments = new List <PatternSegment?> (); // Build a list of segments plus separators we can use as template. foreach (var segment in segments) { if (allSegments.Count > 0) { allSegments.Add(null); // separator exposed as null. } allSegments.Add(segment); } // Finally loop thru al segment-templates building the actual uri. foreach (var item in allSegments) { var segment = item.GetValueOrDefault(); // If segment is a separator.. if (item == null) { if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return(null); } // Append any pending literals to the URL uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } } pendingPartsAreAllSafe = false; // Guard against appending multiple separators for empty segments if (pendingParts.Length > 0 && pendingParts[pendingParts.Length - 1] == '/') { // Dev10 676725: Route should not be matched if that causes mismatched tokens // Dev11 86819: We will allow empty matches if all subsequent segments are null if (blockAllUriAppends) { return(null); } // Append any pending literals to the URI (without the trailing slash) and prevent any future appends uri.Append(pendingParts.ToString(0, pendingParts.Length - 1)); pendingParts.Length = 0; } else { pendingParts.Append("/"); } #if false } else if (segment.AllLiteral) { // Spezial (optimized) case: all elements of segment are literals. pendingPartsAreAllSafe = true; foreach (var tk in segment.Tokens) { pendingParts.Append(tk.Name); } #endif } else { // Segments are treated as all-or-none. We should never output a partial segment. // If we add any subsegment of this segment to the generated URL, we have to add // the complete match. For example, if the subsegment is "{p1}-{p2}.xml" and we // used a value for {p1}, we have to output the entire segment up to the next "/". // Otherwise we could end up with the partial segment "v1" instead of the entire // segment "v1-v2.xml". bool addedAnySubsegments = false; foreach (var token in segment.Tokens) { if (token.Type == PatternTokenType.Literal) { // If it's a literal we hold on to it until we are sure we need to add it pendingPartsAreAllSafe = true; pendingParts.Append(token.Name); } else { if (token.Type == PatternTokenType.Standard || token.Type == PatternTokenType.CatchAll) { if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return(null); } // Append any pending literals to the URL uri.Append(pendingParts.ToString()); pendingParts.Length = 0; addedAnySubsegments = true; } } pendingPartsAreAllSafe = false; // If it's a parameter, get its value object acceptedParameterValue; bool hasAcceptedParameterValue = acceptedValues.TryGetValue(token.Name, out acceptedParameterValue); if (hasAcceptedParameterValue) { unusedNewValues.Remove(token.Name); } object defaultParameterValue; defaultValues.TryGetValue(token.Name, out defaultParameterValue); if (ParametersAreEqual(acceptedParameterValue, defaultParameterValue)) { // If the accepted value is the same as the default value, mark it as pending since // we won't necessarily add it to the URL we generate. pendingParts.Append(Convert.ToString(acceptedParameterValue, CultureInfo.InvariantCulture)); } else { if (blockAllUriAppends) { return(null); } // Add the new part to the URL as well as any pending parts if (pendingParts.Length > 0) { // Append any pending literals to the URL uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } uri.Append(Convert.ToString(acceptedParameterValue, CultureInfo.InvariantCulture)); addedAnySubsegments = true; } } else { Debug.Fail("Invalid path subsegment type"); } } } if (addedAnySubsegments) { // See comment above about why we add the pending parts if (pendingParts.Length > 0) { if (blockAllUriAppends) { return(null); } // Append any pending literals to the URL uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } } } } if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return(null); } // Append any pending literals to the URI uri.Append(pendingParts.ToString()); } } // Process constraints keys if (constraints != null) { // If there are any constraints, mark all the keys as being used so that we don't // generate query string items for custom constraints that don't appear as parameters // in the URI format. foreach (var constraintsItem in constraints) { unusedNewValues.Remove(constraintsItem.Key); } } // Encode the URI before we append the query string, otherwise we would double encode the query string var encodedUri = new StringBuilder(); encodedUri.Append(UriEncode(uri.ToString())); uri = encodedUri; // Add remaining new values as query string parameters to the URI if (unusedNewValues.Count > 0) { // Generate the query string bool firstParam = true; foreach (string unusedNewValue in unusedNewValues) { object value; if (acceptedValues.TryGetValue(unusedNewValue, out value)) { uri.Append(firstParam ? '?' : '&'); firstParam = false; uri.Append(Uri.EscapeDataString(unusedNewValue)); uri.Append('='); uri.Append(Uri.EscapeDataString(Convert.ToString(value, CultureInfo.InvariantCulture))); } } } #endregion usedValues = acceptedValues; return(uri.ToString()); }
public RouteValueDictionary Match(string path, RouteValueDictionary defaults) { var ret = new RouteValueDictionary(); string url = Url; string [] argSegs; int argsCount; if (String.IsNullOrEmpty(path)) { argSegs = null; argsCount = 0; } else { // quick check if (String.Compare(url, path, StringComparison.Ordinal) == 0 && url.IndexOf('{') < 0) { return(AddDefaults(ret, defaults)); } argSegs = path.Split('/'); argsCount = argSegs.Length; if (String.IsNullOrEmpty(argSegs [argsCount - 1])) { argsCount--; // path ends with a trailinig '/' } } bool haveDefaults = defaults != null && defaults.Count > 0; if (argsCount == 1 && String.IsNullOrEmpty(argSegs [0])) { argsCount = 0; } if (!haveDefaults && ((haveSegmentWithCatchAll && argsCount < segmentCount) || (!haveSegmentWithCatchAll && argsCount != segmentCount))) { return(null); } int i = 0; foreach (PatternSegment segment in segments) { if (i >= argsCount) { break; } if (segment.AllLiteral) { if (String.Compare(argSegs [i], segment.Tokens [0].Name, StringComparison.OrdinalIgnoreCase) != 0) { return(null); } i++; continue; } if (!MatchSegment(i, argsCount, argSegs, segment.Tokens, ret)) { return(null); } i++; } // Check the remaining segments, if any, and see if they are required // // If a segment has more than one section (i.e. there's at least one // literal, then it cannot match defaults // // All of the remaining segments must have all defaults provided and they // must not be literals or the match will fail. if (i < segmentCount) { if (!haveDefaults) { return(null); } for (; i < segmentCount; i++) { var segment = segments [i]; if (segment.AllLiteral) { return(null); } var tokens = segment.Tokens; if (tokens.Count != 1) { return(null); } // if token is catch-all, we're done. if (tokens [0].Type == PatternTokenType.CatchAll) { break; } if (!defaults.ContainsKey(tokens [0].Name)) { return(null); } } } else if (!haveSegmentWithCatchAll && argsCount > segmentCount) { return(null); } return(AddDefaults(ret, defaults)); }
public RouteValueDictionary Match(string virtualPath, RouteValueDictionary defaultValues) { IList <string> source = RouteParser.SplitUrlToPathSegmentStrings(virtualPath); if (defaultValues == null) { defaultValues = new RouteValueDictionary(); } RouteValueDictionary matchedValues = new RouteValueDictionary(); bool flag = false; bool flag2 = false; for (int i = 0; i < this.PathSegments.Count; i++) { PathSegment segment = this.PathSegments[i]; if (source.Count <= i) { flag = true; } string a = flag ? null : source[i]; if (segment is SeparatorPathSegment) { if (!flag && !string.Equals(a, "/", StringComparison.Ordinal)) { return(null); } } else { ContentPathSegment contentPathSegment = segment as ContentPathSegment; if (contentPathSegment != null) { if (contentPathSegment.IsCatchAll) { this.MatchCatchAll(contentPathSegment, source.Skip <string>(i), defaultValues, matchedValues); flag2 = true; } else if (!this.MatchContentPathSegment(contentPathSegment, a, defaultValues, matchedValues)) { return(null); } } } } if (!flag2 && (this.PathSegments.Count < source.Count)) { for (int j = this.PathSegments.Count; j < source.Count; j++) { if (!RouteParser.IsSeparator(source[j])) { return(null); } } } if (defaultValues != null) { foreach (KeyValuePair <string, object> pair in defaultValues) { if (!matchedValues.ContainsKey(pair.Key)) { matchedValues.Add(pair.Key, pair.Value); } } } return(matchedValues); }
public BoundUrl Bind(RouteValueDictionary currentValues, RouteValueDictionary values, RouteValueDictionary defaultValues, RouteValueDictionary constraints) { if (currentValues == null) { currentValues = new RouteValueDictionary(); } if (values == null) { values = new RouteValueDictionary(); } if (defaultValues == null) { defaultValues = new RouteValueDictionary(); } RouteValueDictionary acceptedValues = new RouteValueDictionary(); HashSet <string> unusedNewValues = new HashSet <string>(values.Keys, StringComparer.OrdinalIgnoreCase); ForEachParameter(this.PathSegments, delegate(ParameterSubsegment parameterSubsegment) { object obj2; object obj3; string key = parameterSubsegment.ParameterName; bool flag = values.TryGetValue(key, out obj2); if (flag) { unusedNewValues.Remove(key); } bool flag2 = currentValues.TryGetValue(key, out obj3); if ((flag && flag2) && !RoutePartsEqual(obj3, obj2)) { return(false); } if (flag) { if (IsRoutePartNonEmpty(obj2)) { acceptedValues.Add(key, obj2); } } else if (flag2) { acceptedValues.Add(key, obj3); } return(true); }); foreach (KeyValuePair <string, object> pair in values) { if (IsRoutePartNonEmpty(pair.Value) && !acceptedValues.ContainsKey(pair.Key)) { acceptedValues.Add(pair.Key, pair.Value); } } foreach (KeyValuePair <string, object> pair2 in currentValues) { string str = pair2.Key; if (!acceptedValues.ContainsKey(str) && (GetParameterSubsegment(this.PathSegments, str) == null)) { acceptedValues.Add(str, pair2.Value); } } ForEachParameter(this.PathSegments, delegate(ParameterSubsegment parameterSubsegment) { object obj2; if (!acceptedValues.ContainsKey(parameterSubsegment.ParameterName) && !IsParameterRequired(parameterSubsegment, defaultValues, out obj2)) { acceptedValues.Add(parameterSubsegment.ParameterName, obj2); } return(true); }); if (!ForEachParameter(this.PathSegments, delegate(ParameterSubsegment parameterSubsegment) { object obj2; if (IsParameterRequired(parameterSubsegment, defaultValues, out obj2) && !acceptedValues.ContainsKey(parameterSubsegment.ParameterName)) { return(false); } return(true); })) { return(null); } RouteValueDictionary otherDefaultValues = new RouteValueDictionary(defaultValues); ForEachParameter(this.PathSegments, delegate(ParameterSubsegment parameterSubsegment) { otherDefaultValues.Remove(parameterSubsegment.ParameterName); return(true); }); foreach (KeyValuePair <string, object> pair3 in otherDefaultValues) { object obj2; if (values.TryGetValue(pair3.Key, out obj2)) { unusedNewValues.Remove(pair3.Key); if (!RoutePartsEqual(obj2, pair3.Value)) { return(null); } } } StringBuilder builder = new StringBuilder(); StringBuilder builder2 = new StringBuilder(); bool flag2 = false; for (int i = 0; i < this.PathSegments.Count; i++) { PathSegment segment = this.PathSegments[i]; if (segment is SeparatorPathSegment) { if (flag2 && (builder2.Length > 0)) { builder.Append(builder2.ToString()); builder2.Length = 0; } flag2 = false; if ((builder2.Length > 0) && (builder2[builder2.Length - 1] == '/')) { return(null); } builder2.Append("/"); } else { ContentPathSegment segment2 = segment as ContentPathSegment; if (segment2 != null) { bool flag3 = false; foreach (PathSubsegment subsegment2 in segment2.Subsegments) { LiteralSubsegment subsegment3 = subsegment2 as LiteralSubsegment; if (subsegment3 != null) { flag2 = true; builder2.Append(UrlEncode(subsegment3.Literal)); } else { ParameterSubsegment subsegment4 = subsegment2 as ParameterSubsegment; if (subsegment4 != null) { object obj3; object obj4; if (flag2 && (builder2.Length > 0)) { builder.Append(builder2.ToString()); builder2.Length = 0; flag3 = true; } flag2 = false; if (acceptedValues.TryGetValue(subsegment4.ParameterName, out obj3)) { unusedNewValues.Remove(subsegment4.ParameterName); } defaultValues.TryGetValue(subsegment4.ParameterName, out obj4); if (RoutePartsEqual(obj3, obj4)) { builder2.Append(UrlEncode(Convert.ToString(obj3, CultureInfo.InvariantCulture))); } else { if (builder2.Length > 0) { builder.Append(builder2.ToString()); builder2.Length = 0; } builder.Append(UrlEncode(Convert.ToString(obj3, CultureInfo.InvariantCulture))); flag3 = true; } } } } if (flag3 && (builder2.Length > 0)) { builder.Append(builder2.ToString()); builder2.Length = 0; } } } } if (flag2 && (builder2.Length > 0)) { builder.Append(builder2.ToString()); } if (constraints != null) { foreach (KeyValuePair <string, object> pair4 in constraints) { unusedNewValues.Remove(pair4.Key); } } if (unusedNewValues.Count > 0) { bool flag5 = true; foreach (string str2 in unusedNewValues) { object obj5; if (acceptedValues.TryGetValue(str2, out obj5)) { builder.Append(flag5 ? '?' : '&'); flag5 = false; builder.Append(Uri.EscapeDataString(str2)); builder.Append('='); builder.Append(Uri.EscapeDataString(Convert.ToString(obj5, CultureInfo.InvariantCulture))); } } } return(new BoundUrl { Url = builder.ToString(), Values = acceptedValues }); }
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); }
public RouteValueDictionary Match(string path, RouteValueDictionary defaults) { var ret = new RouteValueDictionary(); string url = Url; string [] argSegs; int argsCount; if (String.IsNullOrEmpty(path)) { argSegs = null; argsCount = 0; } else { // quick check if (String.Compare(url, path, StringComparison.Ordinal) == 0 && url.IndexOf('{') < 0) { return(AddDefaults(ret, defaults)); } argSegs = path.Split('/'); argsCount = argSegs.Length; if (String.IsNullOrEmpty(argSegs[argsCount - 1])) { argsCount--; // path ends with a trailing '/' } } bool haveDefaults = defaults != null && defaults.Count > 0; if (argsCount == 1 && String.IsNullOrEmpty(argSegs [0])) { argsCount = 0; } if (!haveDefaults && ((haveSegmentWithCatchAll && argsCount < segmentCount) || (!haveSegmentWithCatchAll && argsCount != segmentCount))) { return(null); } int i = 0; foreach (PatternSegment segment in segments) { if (i >= argsCount) { break; } if (segment.AllLiteral) { if (String.Compare(argSegs [i], segment.Tokens [0].Name, StringComparison.OrdinalIgnoreCase) != 0) { return(null); } i++; continue; } string pathSegment = argSegs [i]; int pathSegmentLength = pathSegment != null ? pathSegment.Length : -1; int pathIndex = 0; PatternTokenType tokenType; List <PatternToken> tokens = segment.Tokens; int tokensCount = tokens.Count; // Process the path segments ignoring the defaults for (int tokenIndex = 0; tokenIndex < tokensCount; tokenIndex++) { var token = tokens [tokenIndex]; if (pathIndex > pathSegmentLength - 1) { return(null); } tokenType = token.Type; var tokenName = token.Name; // Catch-all if (i > segmentCount - 1 || tokenType == PatternTokenType.CatchAll) { if (tokenType != PatternTokenType.CatchAll) { return(null); } StringBuilder sb = new StringBuilder(); for (int j = i; j < argsCount; j++) { if (j > i) { sb.Append('/'); } sb.Append(argSegs [j]); } ret.Add(tokenName, sb.ToString()); break; } // Literal sections if (token.Type == PatternTokenType.Literal) { int nameLen = tokenName.Length; if (pathSegmentLength < nameLen || String.Compare(pathSegment, pathIndex, tokenName, 0, nameLen, StringComparison.OrdinalIgnoreCase) != 0) { return(null); } pathIndex += nameLen; continue; } int nextTokenIndex = tokenIndex + 1; if (nextTokenIndex >= tokensCount) { // Last token ret.Add(tokenName, pathSegment.Substring(pathIndex)); continue; } // Next token is a literal - greedy matching. It seems .NET // uses a simple and naive algorithm here which finds the // last ocurrence of the next section literal and assigns // everything before that to this token. See the // GetRouteData28 test in RouteTest.cs var nextToken = tokens [nextTokenIndex]; string nextTokenName = nextToken.Name; int lastIndex = pathSegment.LastIndexOf(nextTokenName, pathSegmentLength - 1, pathSegmentLength - pathIndex, StringComparison.OrdinalIgnoreCase); if (lastIndex == -1) { return(null); } int copyLength = lastIndex - pathIndex; string sectionValue = pathSegment.Substring(pathIndex, copyLength); if (String.IsNullOrEmpty(sectionValue)) { return(null); } ret.Add(tokenName, sectionValue); pathIndex += copyLength; } i++; } // Check the remaining segments, if any, and see if they are required // // If a segment has more than one section (i.e. there's at least one // literal, then it cannot match defaults // // All of the remaining segments must have all defaults provided and they // must not be literals or the match will fail. if (i < segmentCount) { if (!haveDefaults) { return(null); } for (; i < segmentCount; i++) { var segment = segments [i]; if (segment.AllLiteral) { return(null); } var tokens = segment.Tokens; if (tokens.Count != 1) { return(null); } if (!defaults.ContainsKey(tokens [0].Name)) { return(null); } } } else if (!haveSegmentWithCatchAll && argsCount > segmentCount) { return(null); } return(AddDefaults(ret, defaults)); }
public RouteValueDictionary Match(string virtualPath, RouteValueDictionary defaultValues) { IList <string> requestPathSegments = RouteParser.SplitUrlToPathSegmentStrings(virtualPath); if (defaultValues == null) { defaultValues = new RouteValueDictionary(); } RouteValueDictionary matchedValues = new RouteValueDictionary(); // This flag gets set once all the data in the URL has been parsed through, but // the route we're trying to match against still has more parts. At this point // we'll only continue matching separator characters and parameters that have // default values. bool ranOutOfStuffToParse = false; // This value gets set once we start processing a catchall parameter (if there is one // at all). Once we set this value we consume all remaining parts of the URL into its // parameter value. bool usedCatchAllParameter = false; for (int i = 0; i < PathSegments.Count; i++) { PathSegment pathSegment = PathSegments[i]; if (requestPathSegments.Count <= i) { ranOutOfStuffToParse = true; } string requestPathSegment = ranOutOfStuffToParse ? null : requestPathSegments[i]; if (pathSegment is SeparatorPathSegment) { if (ranOutOfStuffToParse) { // If we're trying to match a separator in the route but there's no more content, that's OK } else { if (!String.Equals(requestPathSegment, "/", StringComparison.Ordinal)) { return(null); } } } else { ContentPathSegment contentPathSegment = pathSegment as ContentPathSegment; if (contentPathSegment != null) { if (contentPathSegment.IsCatchAll) { Debug.Assert(i == (PathSegments.Count - 1), "If we're processing a catch-all, we should be on the last route segment."); MatchCatchAll(contentPathSegment, requestPathSegments.Skip(i), defaultValues, matchedValues); usedCatchAllParameter = true; } else { if (!MatchContentPathSegment(contentPathSegment, requestPathSegment, defaultValues, matchedValues)) { return(null); } } } else { Debug.Fail("Invalid path segment type"); } } } if (!usedCatchAllParameter) { if (PathSegments.Count < requestPathSegments.Count) { // If we've already gone through all the parts defined in the route but the URL // still contains more content, check that the remaining content is all separators. for (int i = PathSegments.Count; i < requestPathSegments.Count; i++) { if (!RouteParser.IsSeparator(requestPathSegments[i])) { return(null); } } } } // Copy all remaining default values to the route data if (defaultValues != null) { foreach (var defaultValue in defaultValues) { if (!matchedValues.ContainsKey(defaultValue.Key)) { matchedValues.Add(defaultValue.Key, defaultValue.Value); } } } return(matchedValues); }
public BoundUrl Bind(RouteValueDictionary currentValues, RouteValueDictionary values, RouteValueDictionary defaultValues, RouteValueDictionary constraints) { if (currentValues == null) { currentValues = new RouteValueDictionary(); } if (values == null) { values = new RouteValueDictionary(); } if (defaultValues == null) { defaultValues = new RouteValueDictionary(); } // The set of values we should be using when generating the URL in this route RouteValueDictionary acceptedValues = new RouteValueDictionary(); // Keep track of which new values have been used HashSet <string> unusedNewValues = new HashSet <string>(values.Keys, StringComparer.OrdinalIgnoreCase); // Step 1: Get the list of values we're going to try to use to match and generate this URL // Find out which entries in the URL are valid for the URL we want to generate. // If the URL had ordered parameters a="1", b="2", c="3" and the new values // specified that b="9", then we need to invalidate everything after it. The new // values should then be a="1", b="9", c=<no value>. ForEachParameter(PathSegments, delegate(ParameterSubsegment parameterSubsegment) { // If it's a parameter subsegment, examine the current value to see if it matches the new value string parameterName = parameterSubsegment.ParameterName; object newParameterValue; bool hasNewParameterValue = values.TryGetValue(parameterName, out newParameterValue); if (hasNewParameterValue) { unusedNewValues.Remove(parameterName); } object currentParameterValue; bool hasCurrentParameterValue = currentValues.TryGetValue(parameterName, out currentParameterValue); if (hasNewParameterValue && hasCurrentParameterValue) { if (!RoutePartsEqual(currentParameterValue, newParameterValue)) { // Stop copying current values when we find one that doesn't match return(false); } } // If the parameter is a match, add it to the list of values we will use for URL generation if (hasNewParameterValue) { if (IsRoutePartNonEmpty(newParameterValue)) { acceptedValues.Add(parameterName, newParameterValue); } } else { if (hasCurrentParameterValue) { acceptedValues.Add(parameterName, currentParameterValue); } } return(true); }); // Add all remaining new values to the list of values we will use for URL generation foreach (var newValue in values) { if (IsRoutePartNonEmpty(newValue.Value)) { if (!acceptedValues.ContainsKey(newValue.Key)) { acceptedValues.Add(newValue.Key, newValue.Value); } } } // Add all current values that aren't in the URL at all foreach (var currentValue in currentValues) { string parameterName = currentValue.Key; if (!acceptedValues.ContainsKey(parameterName)) { ParameterSubsegment parameterSubsegment = GetParameterSubsegment(PathSegments, parameterName); if (parameterSubsegment == null) { acceptedValues.Add(parameterName, currentValue.Value); } } } // Add all remaining default values from the route to the list of values we will use for URL generation ForEachParameter(PathSegments, delegate(ParameterSubsegment parameterSubsegment) { if (!acceptedValues.ContainsKey(parameterSubsegment.ParameterName)) { object defaultValue; if (!IsParameterRequired(parameterSubsegment, defaultValues, out defaultValue)) { // Add the default value only if there isn't already a new value for it and // only if it actually has a default value, which we determine based on whether // the parameter value is required. acceptedValues.Add(parameterSubsegment.ParameterName, defaultValue); } } return(true); }); // All required parameters in this URL must have values from somewhere (i.e. the accepted values) bool hasAllRequiredValues = ForEachParameter(PathSegments, delegate(ParameterSubsegment parameterSubsegment) { object defaultValue; if (IsParameterRequired(parameterSubsegment, defaultValues, out defaultValue)) { if (!acceptedValues.ContainsKey(parameterSubsegment.ParameterName)) { // If the route parameter value is required that means there's // no default value, so if there wasn't a new value for it // either, this route won't match. return(false); } } return(true); }); if (!hasAllRequiredValues) { return(null); } // All other default values must match if they are explicitly defined in the new values RouteValueDictionary otherDefaultValues = new RouteValueDictionary(defaultValues); ForEachParameter(PathSegments, delegate(ParameterSubsegment parameterSubsegment) { otherDefaultValues.Remove(parameterSubsegment.ParameterName); return(true); }); foreach (var defaultValue in otherDefaultValues) { object value; if (values.TryGetValue(defaultValue.Key, out value)) { unusedNewValues.Remove(defaultValue.Key); if (!RoutePartsEqual(value, defaultValue.Value)) { // If there is a non-parameterized value in the route and there is a // new value for it and it doesn't match, this route won't match. return(null); } } } // Step 2: If the route is a match generate the appropriate URL StringBuilder url = new StringBuilder(); StringBuilder pendingParts = new StringBuilder(); bool pendingPartsAreAllSafe = false; bool blockAllUrlAppends = false; for (int i = 0; i < PathSegments.Count; i++) { PathSegment pathSegment = PathSegments[i]; // parsedRouteUrlPart if (pathSegment is SeparatorPathSegment) { if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUrlAppends) { return(null); } // Append any pending literals to the URL url.Append(pendingParts.ToString()); pendingParts.Length = 0; } } pendingPartsAreAllSafe = false; // Guard against appending multiple separators for empty segements if (pendingParts.Length > 0 && pendingParts[pendingParts.Length - 1] == '/') { // Dev10 676725: Route should not be matched if that causes mismatched tokens // Dev11 86819: We will allow empty matches if all subsequent segments are null if (blockAllUrlAppends) { return(null); } // Append any pending literals to the URL(without the trailing slash) and prevent any future appends url.Append(pendingParts.ToString(0, pendingParts.Length - 1)); pendingParts.Length = 0; blockAllUrlAppends = true; } else { pendingParts.Append("/"); } } else { ContentPathSegment contentPathSegment = pathSegment as ContentPathSegment; if (contentPathSegment != null) { // Segments are treated as all-or-none. We should never output a partial segment. // If we add any subsegment of this segment to the generated URL, we have to add // the complete match. For example, if the subsegment is "{p1}-{p2}.xml" and we // used a value for {p1}, we have to output the entire segment up to the next "/". // Otherwise we could end up with the partial segment "v1" instead of the entire // segment "v1-v2.xml". bool addedAnySubsegments = false; foreach (PathSubsegment subsegment in contentPathSegment.Subsegments) { LiteralSubsegment literalSubsegment = subsegment as LiteralSubsegment; if (literalSubsegment != null) { // If it's a literal we hold on to it until we are sure we need to add it pendingPartsAreAllSafe = true; pendingParts.Append(UrlEncode(literalSubsegment.Literal)); } else { ParameterSubsegment parameterSubsegment = subsegment as ParameterSubsegment; if (parameterSubsegment != null) { if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUrlAppends) { return(null); } // Append any pending literals to the URL url.Append(pendingParts.ToString()); pendingParts.Length = 0; addedAnySubsegments = true; } } pendingPartsAreAllSafe = false; // If it's a parameter, get its value object acceptedParameterValue; bool hasAcceptedParameterValue = acceptedValues.TryGetValue(parameterSubsegment.ParameterName, out acceptedParameterValue); if (hasAcceptedParameterValue) { unusedNewValues.Remove(parameterSubsegment.ParameterName); } object defaultParameterValue; defaultValues.TryGetValue(parameterSubsegment.ParameterName, out defaultParameterValue); if (RoutePartsEqual(acceptedParameterValue, defaultParameterValue)) { // If the accepted value is the same as the default value, mark it as pending since // we won't necessarily add it to the URL we generate. pendingParts.Append(UrlEncode(Convert.ToString(acceptedParameterValue, CultureInfo.InvariantCulture))); } else { if (blockAllUrlAppends) { return(null); } // Add the new part to the URL as well as any pending parts if (pendingParts.Length > 0) { // Append any pending literals to the URL url.Append(pendingParts.ToString()); pendingParts.Length = 0; } url.Append(UrlEncode(Convert.ToString(acceptedParameterValue, CultureInfo.InvariantCulture))); addedAnySubsegments = true; } } else { Debug.Fail("Invalid path subsegment type"); } } } if (addedAnySubsegments) { // See comment above about why we add the pending parts if (pendingParts.Length > 0) { if (blockAllUrlAppends) { return(null); } // Append any pending literals to the URL url.Append(pendingParts.ToString()); pendingParts.Length = 0; } } } else { Debug.Fail("Invalid path segment type"); } } } if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUrlAppends) { return(null); } // Append any pending literals to the URL url.Append(pendingParts.ToString()); } } // Process constraints keys if (constraints != null) { // If there are any constraints, mark all the keys as being used so that we don't // generate query string items for custom constraints that don't appear as parameters // in the URL format. foreach (var constraintsItem in constraints) { unusedNewValues.Remove(constraintsItem.Key); } } // Add remaining new values as query string parameters to the URL if (unusedNewValues.Count > 0) { // Generate the query string bool firstParam = true; foreach (string unusedNewValue in unusedNewValues) { object value; if (acceptedValues.TryGetValue(unusedNewValue, out value)) { url.Append(firstParam ? '?' : '&'); firstParam = false; url.Append(Uri.EscapeDataString(unusedNewValue)); url.Append('='); url.Append(Uri.EscapeDataString(Convert.ToString(value, CultureInfo.InvariantCulture))); } } } return(new BoundUrl { Url = url.ToString(), Values = acceptedValues }); }
/// <summary> /// 分页Pager显示 /// </summary> /// <param name="html"></param> /// <param name="currentPageStr">标识当前页码的QueryStringKey</param> /// <param name="pageSize">每页显示</param> /// <param name="totalCount">总数据量</param> /// <returns></returns> public static string Pager(this HtmlHelper html, string currentPageStr, int pageSize, int totalCount) { var queryString = html.ViewContext.HttpContext.Request.QueryString; int currentPage = 1; //当前页 var totalPages = Math.Max((totalCount + pageSize - 1) / pageSize, 1); //总页数 var dict = new System.Web.Routing.RouteValueDictionary(html.ViewContext.RouteData.Values); var output = new StringBuilder(); output.Append("<ul class=\"pagination\">"); //output.AppendFormat("<li><a href=\"#\">{0}</a></li>", totalCount); if (!string.IsNullOrEmpty(queryString[currentPageStr])) { //与相应的QueryString绑定 foreach (string key in queryString.Keys) { if (queryString[key] != null && !string.IsNullOrEmpty(key)) { dict[key] = queryString[key]; } } int.TryParse(queryString[currentPageStr], out currentPage); } else { //获取 ~/Page/{page number} 的页号参数 if (dict.ContainsKey(currentPageStr)) { int.TryParse(dict[currentPageStr].ToString(), out currentPage); } } //保留查询字符到下一页 foreach (string key in queryString.Keys) { dict[key] = queryString[key]; } //如果有需要,保留表单值到下一页 (我暂时不需要, 所以注释掉) //var formValue = html.ViewContext.HttpContext.Request.Form; //foreach (string key in formValue.Keys) // if (formValue[key] != null && !string.IsNullOrEmpty(key)) // dict[key] = formValue[key]; if (currentPage <= 0) { currentPage = 1; } if (totalPages > 1) { if (currentPage != 1) { //处理首页连接 dict[currentPageStr] = 1; output.AppendFormat("<li>{0}</li>", html.RouteLink("首页", dict)); } if (currentPage > 1) { //处理上一页的连接 dict[currentPageStr] = currentPage - 1; output.AppendFormat("<li>{0}</li>", html.RouteLink("<<", dict)); } int currint = 5; for (int i = 0; i <= 10; i++) { //一共最多显示10个页码,前面5个,后面5个 if ((currentPage + i - currint) >= 1 && (currentPage + i - currint) <= totalPages) { if (currint == i) { //当前页处理 output.Append(string.Format("<li class=\"active\"><a href=\"#\">{0}</a></li>", currentPage)); } else { //一般页处理 dict[currentPageStr] = currentPage + i - currint; output.AppendFormat("<li>{0}</li>", html.RouteLink((currentPage + i - currint).ToString(), dict)); } } } if (currentPage < totalPages) { //处理下一页的链接 dict[currentPageStr] = currentPage + 1; output.AppendFormat("<li>{0}</li>", html.RouteLink(">>", dict)); } if (currentPage != totalPages) { dict[currentPageStr] = totalPages; output.AppendFormat("<li>{0}</li>", html.RouteLink("末页", dict)); } } // output.AppendFormat("<li>{0} / {1}</li>", currentPage, totalPages);//这个统计加不加都行 output.Append("</ul>"); return(output.ToString()); }
/// <summary> /// 分页Pager显示 /// </summary> /// <param name="html">this.Html</param> /// <param name="currentPageStr">标识当前页码的QueryStringKey</param> /// <param name="pageSize">每页显示</param> /// <param name="totalCount">总数据量</param> /// <returns></returns> public static MvcHtmlString Pager(this HtmlHelper html, string currentPageStr, int pageSize, int totalCount) { //得到Get参数集合变量 // var queryString = null; NameValueCollection queryString = html.ViewContext.HttpContext.Request.QueryString; //当前页 int currentPage = 1; //总页数 int totalPages = Math.Max((totalCount + pageSize - 1) / pageSize, 1); //获取路由url参数集合 RouteValueDictionary dict = new System.Web.Routing.RouteValueDictionary();//html.ViewContext.RouteData.Values StringBuilder output = new StringBuilder(); if (!string.IsNullOrEmpty(queryString[currentPageStr])) { //与相应的QueryString绑定 foreach (string key in queryString.Keys) { if (queryString[key] != null && !string.IsNullOrEmpty(key)) { dict[key] = queryString[key]; } } int.TryParse(queryString[currentPageStr], out currentPage); } else { //获取 ~/Page/{page number} 的页号参数 if (dict.ContainsKey(currentPageStr)) { int.TryParse(dict[currentPageStr].ToString(), out currentPage); } } // 保留查询字符到下一页 if (queryString.AllKeys.Count() > 0 && queryString.AllKeys[0] != null) { foreach (string key in queryString.Keys) { dict[key] = queryString[key]; } } //如果有需要,保留表单值到下一页 //var formValue = html.ViewContext.HttpContext.Request.Form; //foreach (string key in formValue.Keys) // if (formValue[key] != null && !string.IsNullOrEmpty(key)) // dict[key] = formValue[key]; UrlHelper urlHelper = new UrlHelper(html.ViewContext.RequestContext); if (currentPage <= 0) { currentPage = 1; } if (totalPages > 1) { //if (currentPage != 1) //{ // //处理首页连接 // dict[currentPageStr] = 1; // //output.AppendFormat("<li class=\"nex_page\">{0}</li>", html.RouteLink("首页", dict)); // output.AppendFormat(@"<li class=""nex_page""><a href=""{0}"">首页</a></li>", urlHelper.RouteUrl(dict)); //} if (currentPage > 1) { //处理上一页的连接 dict[currentPageStr] = currentPage - 1; output.AppendFormat(@"<span class=""nex_page""><a href=""{0}"">上一页</a></span>", urlHelper.RouteUrl(dict)); } else { output.Append("<span class=\"disabled\">上一页</span>"); } int currint = 4; for (int i = 0; i <= 8; i++) { //一共最多显示10个页码,前面5个,后面5个 if ((currentPage + i - currint) >= 1 && (currentPage + i - currint) <= totalPages) { if (currint == i) { //当前页处理 output.Append(string.Format("<span class='active'><a href=\"#\" style='background: #198dd2;color: #fff;border: 1px solid #198dd2;'>{0}</a></span>", currentPage)); } else { //一般页处理 dict[currentPageStr] = currentPage + i - currint; // output.AppendFormat("<li>{0}</li>", html.RouteLink((currentPage + i - currint).ToString(), dict)); output.AppendFormat(@"<span><a href=""{0}"">{1}</a></span>", urlHelper.RouteUrl(dict), (currentPage + i - currint).ToString()); } } } if (currentPage < totalPages) { dict[currentPageStr] = "replace"; output.AppendFormat("<span class=\"page-picker\"><input type=\"text\" custompage onkeyup=\"if(event.keyCode==13){{window.location.href=\'{2}\'+this.value;}}\" address=\"{1}\" class=\"px\" size=\"2\" ><span> / {0} 页</span></span>", totalPages, urlHelper.RouteUrl(dict), urlHelper.RouteUrl(dict).Replace("replace", "")); dict[currentPageStr] = currentPage + 1; output.AppendFormat(@"<span class=""nex_page""><a href=""{0}"">下一页</a></span>", urlHelper.RouteUrl(dict)); //output.AppendFormat("<li class=\"nex_page\">{0}</li>", html.RouteLink("下一页", dict)); } else { dict[currentPageStr] = "replace"; output.AppendFormat("<span class=\"page-picker\"><input type=\"text\" custompage onkeyup=\"if(event.keyCode==13){{window.location.href=\'{2}\'+this.value;}}\" address=\"{1}\" class=\"px\" size=\"2\" ><span> / {0} 页</span></span>", totalPages, urlHelper.RouteUrl(dict), urlHelper.RouteUrl(dict).Replace("replace", "")); output.Append("<span class=\"disabled\">下一页</span>"); } //if (currentPage != totalPages) //{ // //处理末页 // dict[currentPageStr] = totalPages; // //output.AppendFormat("<li class=\"nex_page\">{0}</li>", html.RouteLink("末页", dict)); // output.AppendFormat(@"<li class=""nex_page""><a href=""{0}"">末页</a></li>", urlHelper.RouteUrl(dict)); //} } else { //当前只有一页 output.Append("<span class=\"disabled\">上一页</span>"); output.Append(string.Format("<span class=\"font3\">{0}</span>", currentPage)); output.Append("<span class=\"disabled\">下一页</span>"); } //output.AppendFormat("{0} / {1}", currentPage, totalPages);//这个统计加不加都行 return(MvcHtmlString.Create(output.ToString())); }