зеркало из https://github.com/aspnet/Routing.git
removing datatokens/constraints/url-generation
This commit is contained in:
Родитель
d4904e8701
Коммит
85225055b9
|
@ -1,16 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Template
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a URI generated from a <see cref="TemplateParsedRoute"/>.
|
||||
/// </summary>
|
||||
public class BoundRouteTemplate
|
||||
{
|
||||
public string BoundTemplate { get; set; }
|
||||
|
||||
public IDictionary<string, object> Values { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Template
|
||||
{
|
||||
public interface ITemplateRouteConstraint
|
||||
{
|
||||
bool Match(HttpContext context, IRoute route, string parameterName, IDictionary<string, object> values, RouteDirection routeDirection);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Template
|
||||
{
|
||||
public interface IVirtualPathData
|
||||
{
|
||||
IRoute Route { get; }
|
||||
|
||||
string VirtualPath { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Template
|
||||
{
|
||||
public enum RouteDirection
|
||||
{
|
||||
UriResolution = 0,
|
||||
UriGeneration
|
||||
}
|
||||
}
|
|
@ -2,16 +2,12 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Template
|
||||
{
|
||||
public sealed class TemplateParsedRoute
|
||||
public class TemplateParsedRoute
|
||||
{
|
||||
public TemplateParsedRoute(IList<PathSegment> pathSegments)
|
||||
{
|
||||
|
@ -21,392 +17,6 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
|
||||
internal IList<PathSegment> PathSegments { get; private set; }
|
||||
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Not changing original algorithm")]
|
||||
[SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode", Justification = "Not changing original algorithm")]
|
||||
public BoundRouteTemplate Bind(IDictionary<string, object> currentValues, IDictionary<string, object> values, IDictionary<string, object> defaultValues, IDictionary<string, object> constraints)
|
||||
{
|
||||
if (currentValues == null)
|
||||
{
|
||||
currentValues = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (values == null)
|
||||
{
|
||||
values = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (defaultValues == null)
|
||||
{
|
||||
defaultValues = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
// The set of values we should be using when generating the URI in this route
|
||||
IDictionary<string, object> acceptedValues = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// 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
|
||||
IDictionary<string, object> otherDefaultValues = new Dictionary<string, object>(defaultValues, StringComparer.OrdinalIgnoreCase);
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
private static string EscapeReservedCharacters(Match m)
|
||||
{
|
||||
return "%" + Convert.ToUInt16(m.Value[0]).ToString("x2", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private static bool ForEachParameter(IList<PathSegment> pathSegments, Func<PathParameterSubsegment, bool> action)
|
||||
{
|
||||
for (int i = 0; i < pathSegments.Count; i++)
|
||||
|
@ -458,47 +68,6 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
return true;
|
||||
}
|
||||
|
||||
private static PathParameterSubsegment GetParameterSubsegment(IList<PathSegment> pathSegments, string parameterName)
|
||||
{
|
||||
PathParameterSubsegment foundParameterSubsegment = null;
|
||||
|
||||
ForEachParameter(pathSegments, delegate(PathParameterSubsegment parameterSubsegment)
|
||||
{
|
||||
if (String.Equals(parameterName, parameterSubsegment.ParameterName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
foundParameterSubsegment = parameterSubsegment;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return foundParameterSubsegment;
|
||||
}
|
||||
|
||||
private static bool IsParameterRequired(PathParameterSubsegment parameterSubsegment, IDictionary<string, object> defaultValues, out object defaultValue)
|
||||
{
|
||||
if (parameterSubsegment.IsCatchAll)
|
||||
{
|
||||
defaultValue = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return !defaultValues.TryGetValue(parameterSubsegment.ParameterName, out defaultValue);
|
||||
}
|
||||
|
||||
private static bool IsRoutePartNonEmpty(object routePart)
|
||||
{
|
||||
string routePartString = routePart as string;
|
||||
if (routePartString != null)
|
||||
{
|
||||
return routePartString.Length > 0;
|
||||
}
|
||||
return routePart != null;
|
||||
}
|
||||
|
||||
public IDictionary<string, object> Match(string virtualPath, IDictionary<string, object> defaultValues)
|
||||
{
|
||||
IList<string> requestPathSegments = TemplateRouteParser.SplitUriToPathSegmentStrings(virtualPath);
|
||||
|
@ -808,35 +377,5 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool RoutePartsEqual(object a, object b)
|
||||
{
|
||||
string sa = a as string;
|
||||
string sb = b as string;
|
||||
if (sa != null && sb != null)
|
||||
{
|
||||
// For strings do a case-insensitive comparison
|
||||
return String.Equals(sa, sb, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a != null && b != null)
|
||||
{
|
||||
// Explicitly call .Equals() in case it is overridden in the type
|
||||
return a.Equals(b);
|
||||
}
|
||||
else
|
||||
{
|
||||
// At least one of them is null. Return true if they both are
|
||||
return a == b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string UriEncode(string str)
|
||||
{
|
||||
string escape = Uri.EscapeUriString(str);
|
||||
return Regex.Replace(escape, "([#?])", new MatchEvaluator(EscapeReservedCharacters));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,68 +2,34 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNet.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Template
|
||||
{
|
||||
/// <summary>
|
||||
/// Route class for self-host (i.e. hosted outside of ASP.NET). This class is mostly the
|
||||
/// same as the System.Web.Routing.Route implementation.
|
||||
/// This class has the same URL matching functionality as System.Web.Routing.Route. However,
|
||||
/// in order for this route to match when generating URLs, a special "httproute" key must be
|
||||
/// specified when generating the URL.
|
||||
/// </summary>
|
||||
public class TemplateRoute : IRoute
|
||||
{
|
||||
/// <summary>
|
||||
/// Key used to signify that a route URL generation request should include HTTP routes (e.g. Web API).
|
||||
/// If this key is not specified then no HTTP routes will match.
|
||||
/// </summary>
|
||||
public static readonly string HttpRouteKey = "httproute";
|
||||
private readonly IDictionary<string, object> _defaults;
|
||||
private readonly IRouteEndpoint _endpoint;
|
||||
private readonly TemplateParsedRoute _parsedRoute;
|
||||
private readonly string _routeTemplate;
|
||||
|
||||
private string _routeTemplate;
|
||||
private IDictionary<string, object> _defaults;
|
||||
private IDictionary<string, object> _constraints;
|
||||
private IDictionary<string, object> _dataTokens;
|
||||
|
||||
public TemplateRoute()
|
||||
: this(routeTemplate: null, defaults: null, constraints: null, dataTokens: null, handler: null)
|
||||
public TemplateRoute(IRouteEndpoint endpoint, string routeTemplate)
|
||||
: this(endpoint, routeTemplate, null)
|
||||
{
|
||||
}
|
||||
|
||||
public TemplateRoute(string routeTemplate)
|
||||
: this(routeTemplate, defaults: null, constraints: null, dataTokens: null, handler: null)
|
||||
public TemplateRoute(IRouteEndpoint endpoint, string routeTemplate, IDictionary<string, object> defaults)
|
||||
{
|
||||
}
|
||||
if (endpoint == null)
|
||||
{
|
||||
throw new ArgumentNullException("endpoint");
|
||||
}
|
||||
|
||||
public TemplateRoute(string routeTemplate, IDictionary<string, object> defaults)
|
||||
: this(routeTemplate, defaults, constraints: null, dataTokens: null, handler: null)
|
||||
{
|
||||
}
|
||||
|
||||
public TemplateRoute(string routeTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints)
|
||||
: this(routeTemplate, defaults, constraints, dataTokens: null, handler: null)
|
||||
{
|
||||
}
|
||||
|
||||
public TemplateRoute(string routeTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens)
|
||||
: this(routeTemplate, defaults, constraints, dataTokens, handler: null)
|
||||
{
|
||||
}
|
||||
|
||||
public TemplateRoute(string routeTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, IRouteEndpoint handler)
|
||||
{
|
||||
_endpoint = endpoint;
|
||||
_routeTemplate = routeTemplate == null ? String.Empty : routeTemplate;
|
||||
_defaults = defaults ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
_constraints = constraints ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
_dataTokens = dataTokens ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
Handler = handler;
|
||||
|
||||
// The parser will throw for invalid routes.
|
||||
ParsedRoute = TemplateRouteParser.Parse(RouteTemplate);
|
||||
_parsedRoute = TemplateRouteParser.Parse(RouteTemplate);
|
||||
}
|
||||
|
||||
public IDictionary<string, object> Defaults
|
||||
|
@ -71,159 +37,39 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
get { return _defaults; }
|
||||
}
|
||||
|
||||
public IDictionary<string, object> Constraints
|
||||
public IRouteEndpoint Endpoint
|
||||
{
|
||||
get { return _constraints; }
|
||||
get { return _endpoint; }
|
||||
}
|
||||
|
||||
public IDictionary<string, object> DataTokens
|
||||
{
|
||||
get { return _dataTokens; }
|
||||
}
|
||||
|
||||
public IRouteEndpoint Handler { get; private set; }
|
||||
|
||||
public string RouteTemplate
|
||||
{
|
||||
get { return _routeTemplate; }
|
||||
}
|
||||
|
||||
internal TemplateParsedRoute ParsedRoute { get; private set; }
|
||||
|
||||
public virtual RouteMatch GetRouteData(HttpContext request)
|
||||
public virtual RouteMatch Match(RouteContext context)
|
||||
{
|
||||
if (request == null)
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException("request");
|
||||
throw new ArgumentNullException("context");
|
||||
}
|
||||
|
||||
var requestPath = request.Request.Path.Value;
|
||||
var requestPath = context.RequestPath;
|
||||
if (!String.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
|
||||
{
|
||||
requestPath = requestPath.Substring(1);
|
||||
}
|
||||
|
||||
IDictionary<string, object> values = ParsedRoute.Match(requestPath, _defaults);
|
||||
IDictionary<string, object> values = _parsedRoute.Match(requestPath, _defaults);
|
||||
if (values == null)
|
||||
{
|
||||
// If we got back a null value set, that means the URI did not match
|
||||
return null;
|
||||
}
|
||||
|
||||
// Validate the values
|
||||
if (!ProcessConstraints(request, values, RouteDirection.UriResolution))
|
||||
else
|
||||
{
|
||||
return null;
|
||||
return new RouteMatch(_endpoint, values);
|
||||
}
|
||||
|
||||
return new RouteMatch(null, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to generate a URI that represents the values passed in based on current
|
||||
/// values from the <see cref="HttpRouteData"/> and new values using the specified <see cref="TemplateRoute"/>.
|
||||
/// </summary>
|
||||
/// <param name="request">The HTTP request message.</param>
|
||||
/// <param name="values">The route values.</param>
|
||||
/// <returns>A <see cref="VirtualPathData"/> instance or null if URI cannot be generated.</returns>
|
||||
public virtual IVirtualPathData GetVirtualPath(HttpContext request, IDictionary<string, object> values)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new ArgumentNullException("request");
|
||||
}
|
||||
|
||||
// Only perform URL generation if the "httproute" key was specified. This allows these
|
||||
// routes to be ignored when a regular MVC app tries to generate URLs. Without this special
|
||||
// key an HTTP route used for Web API would normally take over almost all the routes in a
|
||||
// typical app.
|
||||
if (values != null && !values.Keys.Contains(HttpRouteKey, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// Remove the value from the collection so that it doesn't affect the generated URL
|
||||
var newValues = GetRouteDictionaryWithoutHttpRouteKey(values);
|
||||
|
||||
IRouteValues routeData = request.GetFeature<IRouteValues>();
|
||||
IDictionary<string, object> requestValues = routeData == null ? null : routeData.Values;
|
||||
|
||||
BoundRouteTemplate result = ParsedRoute.Bind(requestValues, newValues, _defaults, _constraints);
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Assert that the route matches the validation rules
|
||||
if (!ProcessConstraints(request, result.Values, RouteDirection.UriGeneration))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new VirtualPathData(this, result.BoundTemplate);
|
||||
}
|
||||
|
||||
private static IDictionary<string, object> GetRouteDictionaryWithoutHttpRouteKey(IDictionary<string, object> routeValues)
|
||||
{
|
||||
var newRouteValues = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
if (routeValues != null)
|
||||
{
|
||||
foreach (var routeValue in routeValues)
|
||||
{
|
||||
if (!String.Equals(routeValue.Key, HttpRouteKey, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
newRouteValues.Add(routeValue.Key, routeValue.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newRouteValues;
|
||||
}
|
||||
|
||||
protected virtual bool ProcessConstraint(HttpContext request, object constraint, string parameterName, IDictionary<string, object> values, RouteDirection routeDirection)
|
||||
{
|
||||
ITemplateRouteConstraint customConstraint = constraint as ITemplateRouteConstraint;
|
||||
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 new InvalidOperationException(String.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
Resources.TemplateRoute_ValidationMustBeStringOrCustomConstraint,
|
||||
parameterName,
|
||||
RouteTemplate,
|
||||
typeof(ITemplateRouteConstraint).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 bool ProcessConstraints(HttpContext request, IDictionary<string, object> values, RouteDirection routeDirection)
|
||||
{
|
||||
if (Constraints != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, object> constraintsItem in Constraints)
|
||||
{
|
||||
if (!ProcessConstraint(request, constraintsItem.Value, constraintsItem.Key, values, routeDirection))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public RouteMatch Match(RouteContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
namespace Microsoft.AspNet.Routing.Template
|
||||
{
|
||||
public class VirtualPathData : IVirtualPathData
|
||||
{
|
||||
private string _virtualPath;
|
||||
|
||||
public VirtualPathData(IRoute route, string virtualPath)
|
||||
{
|
||||
if (route == null)
|
||||
{
|
||||
throw new ArgumentNullException("route");
|
||||
}
|
||||
|
||||
if (virtualPath == null)
|
||||
{
|
||||
throw new ArgumentNullException("virtualPath");
|
||||
}
|
||||
|
||||
Route = route;
|
||||
VirtualPath = virtualPath;
|
||||
}
|
||||
|
||||
public IRoute Route { get; private set; }
|
||||
|
||||
public string VirtualPath
|
||||
{
|
||||
get { return _virtualPath; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
|
||||
_virtualPath = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче