public static bool TryGetRouteValue(this HttpRouteValueDictionary routeValues, string key, out object value) { if (routeValues.TryGetValue(key, out value)) { return(true); } return(routeValues.GetSubRoutes()?.FirstOrDefault()?.Values.TryGetValue(key, out value) ?? false); }
private static bool IsParameterRequired(SwaggerPathParameterSubSegment parameterSubsegment, HttpRouteValueDictionary defaultValues, out object defaultValue) { if (parameterSubsegment.IsCatchAll) { defaultValue = null; return(false); } return(!defaultValues.TryGetValue(parameterSubsegment.ParameterName, out defaultValue)); }
public static IEnumerable <IHttpRouteData> GetSubRoutes(this HttpRouteValueDictionary routeValues) { object subRoutes = null; if (routeValues.TryGetValue(SubRouteDataKey, out subRoutes)) { return(subRoutes as IHttpRouteData[]); } return(null); }
private static void MatchCatchAll(PathContentSegment contentPathSegment, IEnumerable <string> remainingRequestSegments, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary matchedValues) { object obj2; string str = string.Join(string.Empty, remainingRequestSegments.ToArray <string>()); PathParameterSubsegment subsegment = contentPathSegment.Subsegments[0] as PathParameterSubsegment; if (str.Length > 0) { obj2 = str; } else { defaultValues.TryGetValue(subsegment.ParameterName, out obj2); } matchedValues.Add(subsegment.ParameterName, obj2); }
private static void MatchCatchAll(SwaggerPathContentSegment contentPathSegment, IEnumerable <string> remainingRequestSegments, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary matchedValues) { string remainingRequest = String.Join(String.Empty, remainingRequestSegments.ToArray()); SwaggerPathParameterSubSegment catchAllSegment = contentPathSegment.Subsegments[0] as SwaggerPathParameterSubSegment; object catchAllValue; if (remainingRequest.Length > 0) { catchAllValue = remainingRequest; } else { defaultValues.TryGetValue(catchAllSegment.ParameterName, out catchAllValue); } matchedValues.Add(catchAllSegment.ParameterName, catchAllValue); }
private static bool MatchContentPathSegment(PathContentSegment routeSegment, string requestPathSegment, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary matchedValues) { if (String.IsNullOrEmpty(requestPathSegment)) { // If there's no data to parse, we must have exactly one parameter segment and no other segments - otherwise no match if (routeSegment.Subsegments.Count > 1) { return false; } PathParameterSubsegment parameterSubsegment = routeSegment.Subsegments[0] as PathParameterSubsegment; if (parameterSubsegment == null) { return false; } // We must have a default value since there's no value in the request URI object parameterValue; if (defaultValues.TryGetValue(parameterSubsegment.ParameterName, out parameterValue)) { // If there's a default value for this parameter, use that default value matchedValues.Add(parameterSubsegment.ParameterName, parameterValue); return true; } else { // If there's no default value, this segment doesn't match return false; } } // Find last literal segment and get its last index in the string int lastIndex = requestPathSegment.Length; int indexOfLastSegmentUsed = routeSegment.Subsegments.Count - 1; PathParameterSubsegment parameterNeedsValue = null; // Keeps track of a parameter segment that is pending a value PathLiteralSubsegment lastLiteral = null; // Keeps track of the left-most literal we've encountered while (indexOfLastSegmentUsed >= 0) { int newLastIndex = lastIndex; PathParameterSubsegment parameterSubsegment = routeSegment.Subsegments[indexOfLastSegmentUsed] as PathParameterSubsegment; if (parameterSubsegment != null) { // Hold on to the parameter so that we can fill it in when we locate the next literal parameterNeedsValue = parameterSubsegment; } else { PathLiteralSubsegment literalSubsegment = routeSegment.Subsegments[indexOfLastSegmentUsed] as PathLiteralSubsegment; if (literalSubsegment != null) { lastLiteral = literalSubsegment; int startIndex = lastIndex - 1; // If we have a pending parameter subsegment, we must leave at least one character for that if (parameterNeedsValue != null) { startIndex--; } if (startIndex < 0) { return false; } int indexOfLiteral = requestPathSegment.LastIndexOf(literalSubsegment.Literal, startIndex, StringComparison.OrdinalIgnoreCase); if (indexOfLiteral == -1) { // If we couldn't find this literal index, this segment cannot match return false; } // If the first subsegment is a literal, it must match at the right-most extent of the request URI. // Without this check if your route had "/Foo/" we'd match the request URI "/somethingFoo/". // This check is related to the check we do at the very end of this function. if (indexOfLastSegmentUsed == (routeSegment.Subsegments.Count - 1)) { if ((indexOfLiteral + literalSubsegment.Literal.Length) != requestPathSegment.Length) { return false; } } newLastIndex = indexOfLiteral; } else { Contract.Assert(false, "Invalid path segment type"); } } if ((parameterNeedsValue != null) && (((lastLiteral != null) && (parameterSubsegment == null)) || (indexOfLastSegmentUsed == 0))) { // If we have a pending parameter that needs a value, grab that value int parameterStartIndex; int parameterTextLength; if (lastLiteral == null) { if (indexOfLastSegmentUsed == 0) { parameterStartIndex = 0; } else { parameterStartIndex = newLastIndex; Contract.Assert(false, "indexOfLastSegementUsed should always be 0 from the check above"); } parameterTextLength = lastIndex; } else { // If we're getting a value for a parameter that is somewhere in the middle of the segment if ((indexOfLastSegmentUsed == 0) && (parameterSubsegment != null)) { parameterStartIndex = 0; parameterTextLength = lastIndex; } else { parameterStartIndex = newLastIndex + lastLiteral.Literal.Length; parameterTextLength = lastIndex - parameterStartIndex; } } string parameterValueString = requestPathSegment.Substring(parameterStartIndex, parameterTextLength); if (String.IsNullOrEmpty(parameterValueString)) { // If we're here that means we have a segment that contains multiple sub-segments. // For these segments all parameters must have non-empty values. If the parameter // has an empty value it's not a match. return false; } else { // If there's a value in the segment for this parameter, use the subsegment value matchedValues.Add(parameterNeedsValue.ParameterName, parameterValueString); } parameterNeedsValue = null; lastLiteral = null; } lastIndex = newLastIndex; indexOfLastSegmentUsed--; } // If the last subsegment is a parameter, it's OK that we didn't parse all the way to the left extent of // the string since the parameter will have consumed all the remaining text anyway. If the last subsegment // is a literal then we *must* have consumed the entire text in that literal. Otherwise we end up matching // the route "Foo" to the request URI "somethingFoo". Thus we have to check that we parsed the *entire* // request URI in order for it to be a match. // This check is related to the check we do earlier in this function for LiteralSubsegments. return (lastIndex == 0) || (routeSegment.Subsegments[0] is PathParameterSubsegment); }
private static void MatchCatchAll(PathContentSegment contentPathSegment, IEnumerable<string> remainingRequestSegments, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary matchedValues) { string remainingRequest = String.Join(String.Empty, remainingRequestSegments.ToArray()); PathParameterSubsegment catchAllSegment = contentPathSegment.Subsegments[0] as PathParameterSubsegment; object catchAllValue; if (remainingRequest.Length > 0) { catchAllValue = remainingRequest; } else { defaultValues.TryGetValue(catchAllSegment.ParameterName, out catchAllValue); } matchedValues.Add(catchAllSegment.ParameterName, catchAllValue); }
private static bool IsParameterRequired(PathParameterSubsegment parameterSubsegment, HttpRouteValueDictionary defaultValues, out object defaultValue) { if (parameterSubsegment.IsCatchAll) { defaultValue = null; return false; } return !defaultValues.TryGetValue(parameterSubsegment.ParameterName, out defaultValue); }
public BoundRouteTemplate Bind(IDictionary<string, object> currentValues, IDictionary<string, object> values, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary constraints) { if (currentValues == null) { currentValues = new HttpRouteValueDictionary(); } if (values == null) { values = new HttpRouteValueDictionary(); } if (defaultValues == null) { defaultValues = new HttpRouteValueDictionary(); } // The set of values we should be using when generating the URI in this route HttpRouteValueDictionary acceptedValues = new HttpRouteValueDictionary(); // 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 URI // Find out which entries in the URI are valid for the URI we want to generate. // If the URI 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(PathParameterSubsegment 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 URI 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 URI 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 URI at all foreach (var currentValue in currentValues) { string parameterName = currentValue.Key; if (!acceptedValues.ContainsKey(parameterName)) { PathParameterSubsegment 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 URI generation ForEachParameter(_pathSegments, delegate(PathParameterSubsegment 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 URI must have values from somewhere (i.e. the accepted values) bool hasAllRequiredValues = ForEachParameter(_pathSegments, delegate(PathParameterSubsegment 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 HttpRouteValueDictionary otherDefaultValues = new HttpRouteValueDictionary(defaultValues); ForEachParameter(_pathSegments, delegate(PathParameterSubsegment 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 URI StringBuilder uri = new StringBuilder(); StringBuilder pendingParts = new StringBuilder(); bool pendingPartsAreAllSafe = false; bool blockAllUriAppends = false; for (int i = 0; i < _pathSegments.Count; i++) { PathSegment pathSegment = _pathSegments[i]; // parsedRouteUriPart if (pathSegment is PathSeparatorSegment) { if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return null; } // Append any pending literals to the URI 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; blockAllUriAppends = true; } else { pendingParts.Append("/"); } } else { PathContentSegment contentPathSegment = pathSegment as PathContentSegment; 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 URI, 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) { PathLiteralSubsegment literalSubsegment = subsegment as PathLiteralSubsegment; 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(literalSubsegment.Literal); } else { PathParameterSubsegment parameterSubsegment = subsegment as PathParameterSubsegment; if (parameterSubsegment != null) { if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return null; } // Append any pending literals to the URI 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(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 URI we generate. pendingParts.Append(Convert.ToString(acceptedParameterValue, CultureInfo.InvariantCulture)); } else { if (blockAllUriAppends) { return null; } // Add the new part to the URI as well as any pending parts if (pendingParts.Length > 0) { // Append any pending literals to the URI uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } uri.Append(Convert.ToString(acceptedParameterValue, CultureInfo.InvariantCulture)); addedAnySubsegments = true; } } else { Contract.Assert(false, "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 URI uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } } } else { Contract.Assert(false, "Invalid path segment type"); } } } 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 StringBuilder 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))); } } } return new BoundRouteTemplate { BoundTemplate = uri.ToString(), Values = acceptedValues }; }
protected virtual bool ProcessConstraint(HttpRequestMessage request, object constraint, string parameterName, HttpRouteValueDictionary values, HttpRouteDirection routeDirection) { IHttpRouteConstraint customConstraint = constraint as IHttpRouteConstraint; if (customConstraint != null) { return customConstraint.Match(request, this, parameterName, values, routeDirection); } // If there was no custom constraint, then treat the constraint as a string which represents a Regex. string constraintsRule = constraint as string; if (constraintsRule == null) { throw Error.InvalidOperation(SRResources.Route_ValidationMustBeStringOrCustomConstraint, parameterName, RouteTemplate, typeof(IHttpRouteConstraint).Name); } object parameterValue; values.TryGetValue(parameterName, out parameterValue); string parameterValueString = Convert.ToString(parameterValue, CultureInfo.InvariantCulture); string constraintsRegEx = "^(" + constraintsRule + ")$"; return Regex.IsMatch(parameterValueString, constraintsRegEx, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); }
private static bool MatchContentPathSegment(SwaggerPathContentSegment routeSegment, string requestPathSegment, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary matchedValues) { if (String.IsNullOrEmpty(requestPathSegment)) { // If there's no data to parse, we must have exactly one parameter segment and no other segments - otherwise no match if (routeSegment.Subsegments.Count > 1) { return(false); } SwaggerPathParameterSubSegment parameterSubsegment = routeSegment.Subsegments[0] as SwaggerPathParameterSubSegment; if (parameterSubsegment == null) { return(false); } // We must have a default value since there's no value in the request URI object parameterValue; if (defaultValues.TryGetValue(parameterSubsegment.ParameterName, out parameterValue)) { // If there's a default value for this parameter, use that default value matchedValues.Add(parameterSubsegment.ParameterName, parameterValue); return(true); } else { // If there's no default value, this segment doesn't match return(false); } } // Find last literal segment and get its last index in the string int lastIndex = requestPathSegment.Length; int indexOfLastSegmentUsed = routeSegment.Subsegments.Count - 1; SwaggerPathParameterSubSegment parameterNeedsValue = null; // Keeps track of a parameter segment that is pending a value SwaggerPathLiteralSubsegment lastLiteral = null; // Keeps track of the left-most literal we've encountered while (indexOfLastSegmentUsed >= 0) { int newLastIndex = lastIndex; SwaggerPathParameterSubSegment parameterSubsegment = routeSegment.Subsegments[indexOfLastSegmentUsed] as SwaggerPathParameterSubSegment; if (parameterSubsegment != null) { // Hold on to the parameter so that we can fill it in when we locate the next literal parameterNeedsValue = parameterSubsegment; } else { SwaggerPathLiteralSubsegment literalSubsegment = routeSegment.Subsegments[indexOfLastSegmentUsed] as SwaggerPathLiteralSubsegment; if (literalSubsegment != null) { lastLiteral = literalSubsegment; int startIndex = lastIndex - 1; // If we have a pending parameter subsegment, we must leave at least one character for that if (parameterNeedsValue != null) { startIndex--; } if (startIndex < 0) { return(false); } int indexOfLiteral = requestPathSegment.LastIndexOf(literalSubsegment.Literal, startIndex, StringComparison.OrdinalIgnoreCase); if (indexOfLiteral == -1) { // If we couldn't find this literal index, this segment cannot match return(false); } // If the first subsegment is a literal, it must match at the right-most extent of the request URI. // Without this check if your route had "/Foo/" we'd match the request URI "/somethingFoo/". // This check is related to the check we do at the very end of this function. if (indexOfLastSegmentUsed == (routeSegment.Subsegments.Count - 1)) { if ((indexOfLiteral + literalSubsegment.Literal.Length) != requestPathSegment.Length) { return(false); } } newLastIndex = indexOfLiteral; } else { Contract.Assert(false, "Invalid path segment type"); } } if ((parameterNeedsValue != null) && (((lastLiteral != null) && (parameterSubsegment == null)) || (indexOfLastSegmentUsed == 0))) { // If we have a pending parameter that needs a value, grab that value int parameterStartIndex; int parameterTextLength; if (lastLiteral == null) { if (indexOfLastSegmentUsed == 0) { parameterStartIndex = 0; } else { parameterStartIndex = newLastIndex; Contract.Assert(false, "indexOfLastSegementUsed should always be 0 from the check above"); } parameterTextLength = lastIndex; } else { // If we're getting a value for a parameter that is somewhere in the middle of the segment if ((indexOfLastSegmentUsed == 0) && (parameterSubsegment != null)) { parameterStartIndex = 0; parameterTextLength = lastIndex; } else { parameterStartIndex = newLastIndex + lastLiteral.Literal.Length; parameterTextLength = lastIndex - parameterStartIndex; } } string parameterValueString = requestPathSegment.Substring(parameterStartIndex, parameterTextLength); if (String.IsNullOrEmpty(parameterValueString)) { // If we're here that means we have a segment that contains multiple sub-segments. // For these segments all parameters must have non-empty values. If the parameter // has an empty value it's not a match. return(false); } else { // If there's a value in the segment for this parameter, use the subsegment value matchedValues.Add(parameterNeedsValue.ParameterName, parameterValueString); } parameterNeedsValue = null; lastLiteral = null; } lastIndex = newLastIndex; indexOfLastSegmentUsed--; } // If the last subsegment is a parameter, it's OK that we didn't parse all the way to the left extent of // the string since the parameter will have consumed all the remaining text anyway. If the last subsegment // is a literal then we *must* have consumed the entire text in that literal. Otherwise we end up matching // the route "Foo" to the request URI "somethingFoo". Thus we have to check that we parsed the *entire* // request URI in order for it to be a match. // This check is related to the check we do earlier in this function for LiteralSubsegments. return((lastIndex == 0) || (routeSegment.Subsegments[0] is SwaggerPathParameterSubSegment)); }
public SwaggerBoundRouteTemplate Bind(IDictionary <string, object> currentValues, IDictionary <string, object> values, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary constraints) { if (currentValues == null) { currentValues = new HttpRouteValueDictionary(); } if (values == null) { values = new HttpRouteValueDictionary(); } if (defaultValues == null) { defaultValues = new HttpRouteValueDictionary(); } // The set of values we should be using when generating the URI in this route HttpRouteValueDictionary acceptedValues = new HttpRouteValueDictionary(); // 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 URI // Find out which entries in the URI are valid for the URI we want to generate. // If the URI 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(SwaggerPathParameterSubSegment 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 URI 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 URI 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 URI at all foreach (var currentValue in currentValues) { string parameterName = currentValue.Key; if (!acceptedValues.ContainsKey(parameterName)) { SwaggerPathParameterSubSegment 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 URI generation ForEachParameter(_pathSegments, delegate(SwaggerPathParameterSubSegment 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 URI must have values from somewhere (i.e. the accepted values) bool hasAllRequiredValues = ForEachParameter(_pathSegments, delegate(SwaggerPathParameterSubSegment 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 HttpRouteValueDictionary otherDefaultValues = new HttpRouteValueDictionary(defaultValues); ForEachParameter(_pathSegments, delegate(SwaggerPathParameterSubSegment 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 URI StringBuilder uri = new StringBuilder(); StringBuilder pendingParts = new StringBuilder(); bool pendingPartsAreAllSafe = false; bool blockAllUriAppends = false; for (int i = 0; i < _pathSegments.Count; i++) { SwaggerPathSegment pathSegment = _pathSegments[i]; // parsedRouteUriPart if (pathSegment is SwaggerPathSeparatorSegment) { if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return(null); } // Append any pending literals to the URI 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; blockAllUriAppends = true; } else { pendingParts.Append("/"); } } else { SwaggerPathContentSegment contentPathSegment = pathSegment as SwaggerPathContentSegment; 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 URI, 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 (SwaggerPathSubsegment subsegment in contentPathSegment.Subsegments) { SwaggerPathLiteralSubsegment literalSubsegment = subsegment as SwaggerPathLiteralSubsegment; 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(literalSubsegment.Literal); } else { SwaggerPathParameterSubSegment parameterSubsegment = subsegment as SwaggerPathParameterSubSegment; if (parameterSubsegment != null) { if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return(null); } // Append any pending literals to the URI 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(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 URI we generate. pendingParts.Append(Convert.ToString(acceptedParameterValue, CultureInfo.InvariantCulture)); } else { if (blockAllUriAppends) { return(null); } // Add the new part to the URI as well as any pending parts if (pendingParts.Length > 0) { // Append any pending literals to the URI uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } uri.Append(Convert.ToString(acceptedParameterValue, CultureInfo.InvariantCulture)); addedAnySubsegments = true; } } else { Contract.Assert(false, "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 URI uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } } } else { Contract.Assert(false, "Invalid path segment type"); } } } 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 StringBuilder 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))); } } } return(new SwaggerBoundRouteTemplate { BoundTemplate = uri.ToString(), Values = acceptedValues }); }
public static bool TryGetRouteValue(this HttpRouteValueDictionary routeValues, string key, out object value) { return(routeValues.TryGetValue(key, out value)); }
private static bool MatchContentPathSegment(PathContentSegment routeSegment, string requestPathSegment, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary matchedValues) { if (string.IsNullOrEmpty(requestPathSegment)) { if (routeSegment.Subsegments.Count <= 1) { object obj2; PathParameterSubsegment subsegment3 = routeSegment.Subsegments[0] as PathParameterSubsegment; if (subsegment3 == null) { return(false); } if (defaultValues.TryGetValue(subsegment3.ParameterName, out obj2)) { matchedValues.Add(subsegment3.ParameterName, obj2); return(true); } } return(false); } if (routeSegment.Subsegments.Count == 1) { return(MatchSingleContentPathSegment(routeSegment.Subsegments[0], requestPathSegment, matchedValues)); } int length = requestPathSegment.Length; int num2 = routeSegment.Subsegments.Count - 1; PathParameterSubsegment subsegment = null; PathLiteralSubsegment subsegment2 = null; while (num2 >= 0) { int num3 = length; PathParameterSubsegment subsegment4 = routeSegment.Subsegments[num2] as PathParameterSubsegment; if (subsegment4 != null) { subsegment = subsegment4; } else { PathLiteralSubsegment subsegment5 = routeSegment.Subsegments[num2] as PathLiteralSubsegment; if (subsegment5 != null) { subsegment2 = subsegment5; int startIndex = length - 1; if (subsegment != null) { startIndex--; } if (startIndex < 0) { return(false); } int num5 = requestPathSegment.LastIndexOf(subsegment5.Literal, startIndex, StringComparison.OrdinalIgnoreCase); if (num5 == -1) { return(false); } if ((num2 == (routeSegment.Subsegments.Count - 1)) && ((num5 + subsegment5.Literal.Length) != requestPathSegment.Length)) { return(false); } num3 = num5; } } if ((subsegment != null) && (((subsegment2 != null) && (subsegment4 == null)) || (num2 == 0))) { int num6; int num7; if (subsegment2 == null) { if (num2 == 0) { num6 = 0; } else { num6 = num3; } num7 = length; } else if ((num2 == 0) && (subsegment4 != null)) { num6 = 0; num7 = length; } else { num6 = num3 + subsegment2.Literal.Length; num7 = length - num6; } string str = requestPathSegment.Substring(num6, num7); if (string.IsNullOrEmpty(str)) { return(false); } matchedValues.Add(subsegment.ParameterName, str); subsegment = null; subsegment2 = null; } length = num3; num2--; } if (length != 0) { return(routeSegment.Subsegments[0] is PathParameterSubsegment); } return(true); }