Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.Routing
{
/// <summary>
/// Represents a URI generated from a <see cref="HttpParsedRoute"/>.
/// </summary>
internal class BoundRouteTemplate
{
public string BoundTemplate { get; set; }
public HttpRouteValueDictionary Values { get; set; }
}
}

View File

@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Net.Http;
namespace System.Web.Http.Routing
{
public class HttpMethodConstraint : IHttpRouteConstraint
{
public HttpMethodConstraint(params HttpMethod[] allowedMethods)
{
if (allowedMethods == null)
{
throw Error.ArgumentNull("allowedMethods");
}
AllowedMethods = new Collection<HttpMethod>(allowedMethods);
}
public Collection<HttpMethod> AllowedMethods { get; private set; }
protected virtual bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
if (route == null)
{
throw Error.ArgumentNull("route");
}
if (parameterName == null)
{
throw Error.ArgumentNull("parameterName");
}
if (values == null)
{
throw Error.ArgumentNull("values");
}
switch (routeDirection)
{
case HttpRouteDirection.UriResolution:
return AllowedMethods.Contains(request.Method);
case HttpRouteDirection.UriGeneration:
// We need to see if the user specified the HTTP method explicitly. Consider these two routes:
//
// a) Route: template = "/{foo}", Constraints = { httpMethod = new HttpMethodConstraint("GET") }
// b) Route: template = "/{foo}", Constraints = { httpMethod = new HttpMethodConstraint("POST") }
//
// A user might know ahead of time that a URI he/she is generating might be used with a particular HTTP
// method. If a URI will be used for an HTTP POST but we match on (a) while generating the URI, then
// the HTTP GET-specific route will be used for URI generation, which might have undesired behavior.
// To prevent this, a user might call RouteCollection.GetVirtualPath(..., { httpMethod = "POST" }) to
// signal that he is generating a URI that will be used for an HTTP POST, so he wants the URI
// generation to be performed by the (b) route instead of the (a) route, consistent with what would
// happen on incoming requests.
HttpMethod constraint;
if (!values.TryGetValue(parameterName, out constraint))
{
return true;
}
return AllowedMethods.Contains(constraint);
default:
throw Error.InvalidEnumArgument(String.Empty, (int)routeDirection, typeof(HttpRouteDirection));
}
}
#region IRouteConstraint Members
bool IHttpRouteConstraint.Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
return Match(request, route, parameterName, values, routeDirection);
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,229 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Web.Http.Controllers;
using System.Web.Http.Internal;
using System.Web.Http.Properties;
namespace System.Web.Http.Routing
{
/// <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 HttpRoute : IHttpRoute
{
/// <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>
internal const string HttpRouteKey = "httproute";
private const string HttpMethodParameterName = "httpMethod";
private string _routeTemplate;
private HttpParsedRoute _parsedRoute;
private HttpRouteValueDictionary _defaults;
private HttpRouteValueDictionary _constraints;
private HttpRouteValueDictionary _dataTokens;
public HttpRoute()
: this(null, null, null, null)
{
}
public HttpRoute(string routeTemplate)
: this(routeTemplate, null, null, null)
{
}
public HttpRoute(string routeTemplate, HttpRouteValueDictionary defaults)
: this(routeTemplate, defaults, null, null)
{
}
public HttpRoute(string routeTemplate, HttpRouteValueDictionary defaults, HttpRouteValueDictionary constraints)
: this(routeTemplate, defaults, constraints, null)
{
}
public HttpRoute(string routeTemplate, HttpRouteValueDictionary defaults, HttpRouteValueDictionary constraints, HttpRouteValueDictionary dataTokens)
{
_routeTemplate = String.IsNullOrWhiteSpace(routeTemplate) ? String.Empty : routeTemplate;
_defaults = defaults ?? new HttpRouteValueDictionary();
_constraints = constraints ?? new HttpRouteValueDictionary();
_dataTokens = dataTokens ?? new HttpRouteValueDictionary();
// The parser will throw for invalid routes.
_parsedRoute = HttpRouteParser.Parse(_routeTemplate);
}
public IDictionary<string, object> Defaults
{
get { return _defaults; }
}
public IDictionary<string, object> Constraints
{
get { return _constraints; }
}
public IDictionary<string, object> DataTokens
{
get { return _dataTokens; }
}
public string RouteTemplate
{
get { return _routeTemplate; }
}
public virtual IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request)
{
if (virtualPathRoot == null)
{
throw Error.ArgumentNull("virtualPathRoot");
}
if (request == null)
{
throw Error.ArgumentNull("request");
}
// Note: we don't validate host/port as this is expected to be done at the host level
string requestPath = request.RequestUri.AbsolutePath;
if (!requestPath.StartsWith(virtualPathRoot, StringComparison.OrdinalIgnoreCase))
{
return null;
}
string relativeRequestPath = null;
int virtualPathLength = virtualPathRoot.Length;
if (requestPath.Length > virtualPathLength && requestPath[virtualPathLength] == '/')
{
relativeRequestPath = requestPath.Substring(virtualPathLength + 1);
}
else
{
relativeRequestPath = requestPath.Substring(virtualPathLength);
}
string decodedRelativeRequestPath = UriQueryUtility.UrlDecode(relativeRequestPath);
HttpRouteValueDictionary values = _parsedRoute.Match(decodedRelativeRequestPath, _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, HttpRouteDirection.UriResolution))
{
return null;
}
return new HttpRouteData(this, 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="HttpRoute"/>.
/// </summary>
/// <param name="controllerContext">The HTTP execution context.</param>
/// <param name="values">The route values.</param>
/// <returns>A <see cref="HttpVirtualPathData"/> instance or null if URI cannot be generated.</returns>
public virtual IHttpVirtualPathData GetVirtualPath(HttpControllerContext controllerContext, IDictionary<string, object> values)
{
if (controllerContext == null)
{
throw Error.ArgumentNull("controllerContext");
}
// 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);
BoundRouteTemplate result = _parsedRoute.Bind(controllerContext.RouteData.Values, newValues, _defaults, _constraints);
if (result == null)
{
return null;
}
// Verify that the route matches the validation rules
if (!ProcessConstraints(controllerContext.Request, result.Values, HttpRouteDirection.UriGeneration))
{
return null;
}
return new HttpVirtualPathData(this, result.BoundTemplate);
}
private static IDictionary<string, object> GetRouteDictionaryWithoutHttpRouteKey(IDictionary<string, object> routeValues)
{
var newRouteValues = new Dictionary<string, object>();
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(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 bool ProcessConstraints(HttpRequestMessage request, HttpRouteValueDictionary values, HttpRouteDirection routeDirection)
{
if (Constraints != null)
{
foreach (KeyValuePair<string, object> constraintsItem in Constraints)
{
if (!ProcessConstraint(request, constraintsItem.Value, constraintsItem.Key, values, routeDirection))
{
return false;
}
}
}
return true;
}
}
}

View File

@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace System.Web.Http.Routing
{
public class HttpRouteData : IHttpRouteData
{
private IHttpRoute _route;
private IDictionary<string, object> _values;
public HttpRouteData(IHttpRoute route)
: this(route, new HttpRouteValueDictionary())
{
}
public HttpRouteData(IHttpRoute route, HttpRouteValueDictionary values)
{
if (route == null)
{
throw Error.ArgumentNull("route");
}
if (values == null)
{
throw Error.ArgumentNull("values");
}
_route = route;
_values = values;
}
public IHttpRoute Route
{
get { return _route; }
}
public IDictionary<string, object> Values
{
get { return _values; }
}
}
}

View File

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.Routing
{
public enum HttpRouteDirection
{
UriResolution = 0,
UriGeneration
}
}

View File

@ -0,0 +1,361 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Web.Http.Properties;
namespace System.Web.Http.Routing
{
internal static class HttpRouteParser
{
private static string GetLiteral(string segmentLiteral)
{
// Scan for errant single { and } and convert double {{ to { and double }} to }
// First we eliminate all escaped braces and then check if any other braces are remaining
string newLiteral = segmentLiteral.Replace("{{", String.Empty).Replace("}}", String.Empty);
if (newLiteral.Contains("{") || newLiteral.Contains("}"))
{
return null;
}
// If it's a valid format, we unescape the braces
return segmentLiteral.Replace("{{", "{").Replace("}}", "}");
}
private static int IndexOfFirstOpenParameter(string segment, int startIndex)
{
// Find the first unescaped open brace
while (true)
{
startIndex = segment.IndexOf('{', startIndex);
if (startIndex == -1)
{
// If there are no more open braces, stop
return -1;
}
if ((startIndex + 1 == segment.Length) ||
((startIndex + 1 < segment.Length) && (segment[startIndex + 1] != '{')))
{
// If we found an open brace that is followed by a non-open brace, it's
// a parameter delimiter.
// It's also a delimiter if the open brace is the last character - though
// it ends up being being called out as invalid later on.
return startIndex;
}
// Increment by two since we want to skip both the open brace that
// we're on as well as the subsequent character since we know for
// sure that it is part of an escape sequence.
startIndex += 2;
}
}
internal static bool IsSeparator(string s)
{
return String.Equals(s, "/", StringComparison.Ordinal);
}
private static bool IsValidParameterName(string parameterName)
{
if (parameterName.Length == 0)
{
return false;
}
for (int i = 0; i < parameterName.Length; i++)
{
char c = parameterName[i];
if (c == '/' || c == '{' || c == '}')
{
return false;
}
}
return true;
}
internal static bool IsInvalidRouteTemplate(string routeTemplate)
{
return routeTemplate.StartsWith("~", StringComparison.Ordinal) ||
routeTemplate.StartsWith("/", StringComparison.Ordinal) ||
(routeTemplate.IndexOf('?') != -1);
}
public static HttpParsedRoute Parse(string routeTemplate)
{
if (routeTemplate == null)
{
routeTemplate = String.Empty;
}
if (IsInvalidRouteTemplate(routeTemplate))
{
throw Error.Argument("routeTemplate", SRResources.Route_InvalidRouteTemplate);
}
IList<string> uriParts = SplitUriToPathSegmentStrings(routeTemplate);
Exception ex = ValidateUriParts(uriParts);
if (ex != null)
{
throw ex;
}
IList<PathSegment> pathSegments = SplitUriToPathSegments(uriParts);
Contract.Assert(uriParts.Count == pathSegments.Count, "The number of string segments should be the same as the number of path segments");
return new HttpParsedRoute(pathSegments);
}
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly",
Justification = "The exceptions are just constructed here, but they are thrown from a method that does have those parameter names.")]
private static IList<PathSubsegment> ParseUriSegment(string segment, out Exception exception)
{
int startIndex = 0;
List<PathSubsegment> pathSubsegments = new List<PathSubsegment>();
while (startIndex < segment.Length)
{
int nextParameterStart = IndexOfFirstOpenParameter(segment, startIndex);
if (nextParameterStart == -1)
{
// If there are no more parameters in the segment, capture the remainder as a literal and stop
string lastLiteralPart = GetLiteral(segment.Substring(startIndex));
if (lastLiteralPart == null)
{
exception = Error.Argument("routeTemplate", SRResources.Route_MismatchedParameter, segment);
return null;
}
if (lastLiteralPart.Length > 0)
{
pathSubsegments.Add(new PathLiteralSubsegment(lastLiteralPart));
}
break;
}
int nextParameterEnd = segment.IndexOf('}', nextParameterStart + 1);
if (nextParameterEnd == -1)
{
exception = Error.Argument("routeTemplate", SRResources.Route_MismatchedParameter, segment);
return null;
}
string literalPart = GetLiteral(segment.Substring(startIndex, nextParameterStart - startIndex));
if (literalPart == null)
{
exception = Error.Argument("routeTemplate", SRResources.Route_MismatchedParameter, segment);
return null;
}
if (literalPart.Length > 0)
{
pathSubsegments.Add(new PathLiteralSubsegment(literalPart));
}
string parameterName = segment.Substring(nextParameterStart + 1, nextParameterEnd - nextParameterStart - 1);
pathSubsegments.Add(new PathParameterSubsegment(parameterName));
startIndex = nextParameterEnd + 1;
}
exception = null;
return pathSubsegments;
}
private static IList<PathSegment> SplitUriToPathSegments(IList<string> uriParts)
{
List<PathSegment> pathSegments = new List<PathSegment>();
foreach (string pathSegment in uriParts)
{
bool isCurrentPartSeparator = IsSeparator(pathSegment);
if (isCurrentPartSeparator)
{
pathSegments.Add(new PathSeparatorSegment());
}
else
{
Exception exception;
IList<PathSubsegment> subsegments = ParseUriSegment(pathSegment, out exception);
Contract.Assert(exception == null, "This only gets called after the path has been validated, so there should never be an exception here");
pathSegments.Add(new PathContentSegment(subsegments));
}
}
return pathSegments;
}
internal static IList<string> SplitUriToPathSegmentStrings(string uri)
{
List<string> parts = new List<string>();
if (String.IsNullOrEmpty(uri))
{
return parts;
}
int currentIndex = 0;
// Split the incoming URI into individual parts
while (currentIndex < uri.Length)
{
int indexOfNextSeparator = uri.IndexOf('/', currentIndex);
if (indexOfNextSeparator == -1)
{
// If there are no more separators, the rest of the string is the last part
string finalPart = uri.Substring(currentIndex);
if (finalPart.Length > 0)
{
parts.Add(finalPart);
}
break;
}
string nextPart = uri.Substring(currentIndex, indexOfNextSeparator - currentIndex);
if (nextPart.Length > 0)
{
parts.Add(nextPart);
}
Contract.Assert(uri[indexOfNextSeparator] == '/', "The separator char itself should always be a '/'.");
parts.Add("/");
currentIndex = indexOfNextSeparator + 1;
}
return parts;
}
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Not changing original algorithm")]
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly",
Justification = "The exceptions are just constructed here, but they are thrown from a method that does have those parameter names.")]
private static Exception ValidateUriParts(IList<string> pathSegments)
{
Contract.Assert(pathSegments != null, "The value should always come from SplitUri(), and that function should never return null.");
HashSet<string> usedParameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
bool? isPreviousPartSeparator = null;
bool foundCatchAllParameter = false;
foreach (string pathSegment in pathSegments)
{
if (foundCatchAllParameter)
{
// If we ever start an iteration of the loop and we've already found a
// catchall parameter then we have an invalid URI format.
return Error.Argument("routeTemplate", SRResources.Route_CatchAllMustBeLast, "routeTemplate");
}
bool isCurrentPartSeparator;
if (isPreviousPartSeparator == null)
{
// Prime the loop with the first value
isPreviousPartSeparator = IsSeparator(pathSegment);
isCurrentPartSeparator = isPreviousPartSeparator.Value;
}
else
{
isCurrentPartSeparator = IsSeparator(pathSegment);
// If both the previous part and the current part are separators, it's invalid
if (isCurrentPartSeparator && isPreviousPartSeparator.Value)
{
return Error.Argument("routeTemplate", SRResources.Route_CannotHaveConsecutiveSeparators);
}
Contract.Assert(isCurrentPartSeparator != isPreviousPartSeparator.Value, "This assert should only happen if both the current and previous parts are non-separators. This should never happen because consecutive non-separators are always parsed as a single part.");
isPreviousPartSeparator = isCurrentPartSeparator;
}
// If it's not a separator, parse the segment for parameters and validate it
if (!isCurrentPartSeparator)
{
Exception exception;
IList<PathSubsegment> subsegments = ParseUriSegment(pathSegment, out exception);
if (exception != null)
{
return exception;
}
exception = ValidateUriSegment(subsegments, usedParameterNames);
if (exception != null)
{
return exception;
}
foundCatchAllParameter = subsegments.Any<PathSubsegment>(seg => (seg is PathParameterSubsegment) && ((PathParameterSubsegment)seg).IsCatchAll);
}
}
return null;
}
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly",
Justification = "The exceptions are just constructed here, but they are thrown from a method that does have those parameter names.")]
private static Exception ValidateUriSegment(IList<PathSubsegment> pathSubsegments, HashSet<string> usedParameterNames)
{
bool segmentContainsCatchAll = false;
Type previousSegmentType = null;
foreach (PathSubsegment subsegment in pathSubsegments)
{
if (previousSegmentType != null)
{
if (previousSegmentType == subsegment.GetType())
{
return Error.Argument("routeTemplate", SRResources.Route_CannotHaveConsecutiveParameters);
}
}
previousSegmentType = subsegment.GetType();
PathLiteralSubsegment literalSubsegment = subsegment as PathLiteralSubsegment;
if (literalSubsegment != null)
{
// Nothing to validate for literals - everything is valid
}
else
{
PathParameterSubsegment parameterSubsegment = subsegment as PathParameterSubsegment;
if (parameterSubsegment != null)
{
string parameterName = parameterSubsegment.ParameterName;
if (parameterSubsegment.IsCatchAll)
{
segmentContainsCatchAll = true;
}
// Check for valid characters in the parameter name
if (!IsValidParameterName(parameterName))
{
return Error.Argument("routeTemplate", SRResources.Route_InvalidParameterName, parameterName);
}
if (usedParameterNames.Contains(parameterName))
{
return Error.Argument("routeTemplate", SRResources.Route_RepeatedParameter, parameterName);
}
else
{
usedParameterNames.Add(parameterName);
}
}
else
{
Contract.Assert(false, "Invalid path subsegment type");
}
}
}
if (segmentContainsCatchAll && (pathSubsegments.Count != 1))
{
return Error.Argument("routeTemplate", SRResources.Route_CannotHaveCatchAllInMultiSegment);
}
return null;
}
}
}

View File

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
namespace System.Web.Http.Routing
{
[SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable", Justification = "This class will never be serialized.")]
public class HttpRouteValueDictionary : Dictionary<string, object>
{
public HttpRouteValueDictionary()
: base(StringComparer.OrdinalIgnoreCase)
{
}
public HttpRouteValueDictionary(IDictionary<string, object> dictionary)
: base(dictionary, StringComparer.OrdinalIgnoreCase)
{
}
public HttpRouteValueDictionary(object values)
: base(StringComparer.OrdinalIgnoreCase)
{
if (values == null)
{
throw Error.ArgumentNull("values");
}
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(values);
foreach (PropertyDescriptor prop in properties)
{
object val = prop.GetValue(values);
Add(prop.Name, val);
}
}
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.Routing
{
public class HttpVirtualPathData : IHttpVirtualPathData
{
public HttpVirtualPathData(IHttpRoute route, string virtualPath)
{
if (route == null)
{
throw Error.ArgumentNull("route");
}
if (virtualPath == null)
{
throw Error.ArgumentNull("virtualPath");
}
Route = route;
VirtualPath = virtualPath;
}
public IHttpRoute Route { get; private set; }
public string VirtualPath { get; private set; }
}
}

View File

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http;
using System.Web.Http.Controllers;
namespace System.Web.Http.Routing
{
/// <summary>
/// <see cref="IHttpRoute"/> defines the interface for a route expressing how to map an incoming <see cref="HttpRequestMessage"/> to a particular controller
/// and action.
/// </summary>
public interface IHttpRoute
{
/// <summary>
/// Gets the route template describing the URI pattern to match against.
/// </summary>
string RouteTemplate { get; }
/// <summary>
/// Gets the default values for route parameters if not provided by the incoming <see cref="HttpRequestMessage"/>.
/// </summary>
IDictionary<string, object> Defaults { get; }
/// <summary>
/// Gets the constraints for the route parameters.
/// </summary>
IDictionary<string, object> Constraints { get; }
/// <summary>
/// Gets any additional data tokens not used directly to determine whether a route matches an incoming <see cref="HttpRequestMessage"/>.
/// </summary>
IDictionary<string, object> DataTokens { get; }
/// <summary>
/// Determine whether this route is a match for the incoming request by looking up the <see cref="IHttpRouteData"/> for the route.
/// </summary>
/// <param name="virtualPathRoot">The virtual path root.</param>
/// <param name="request">The request.</param>
/// <returns>The <see cref="IHttpRouteData"/> for a route if matches; otherwise <c>null</c>.</returns>
IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request);
/// <summary>
/// Compute a URI based on the route and the values provided.
/// </summary>
/// <param name="controllerContext">The controller context.</param>
/// <param name="values">The values.</param>
/// <returns></returns>
IHttpVirtualPathData GetVirtualPath(HttpControllerContext controllerContext, IDictionary<string, object> values);
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Net.Http;
namespace System.Web.Http.Routing
{
public interface IHttpRouteConstraint
{
bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection);
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace System.Web.Http.Routing
{
public interface IHttpRouteData
{
IHttpRoute Route { get; }
IDictionary<string, object> Values { get; }
}
}

View File

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.Routing
{
public interface IHttpVirtualPathData
{
IHttpRoute Route { get; }
string VirtualPath { get; }
}
}

View File

@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http.Headers;
using System.Web.Http.Routing;
namespace System.Net.Http.Formatting
{
/// <summary>
/// Extensions for adding <see cref="MediaTypeMapping"/> items to a <see cref="MediaTypeFormatter"/>.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class MediaTypeFormatterExtensions
{
/// <summary>
/// Updates the given <paramref name="formatter"/>'s set of <see cref="MediaTypeMapping"/> elements
/// so that it associates the <paramref name="mediaType"/> with <see cref="HttpRequestMessage"/> whose <see cref="IHttpRouteData"/> contains a URL Parameter {ext}
/// with the given <paramref name="uriPathExtension"/>.
/// </summary>
/// <param name="formatter">The <see cref="MediaTypeFormatter"/> to receive the new <see cref="UriPathExtensionMapping"/> item.</param>
/// <param name="uriPathExtension">The string of the <see cref="Uri"/> path extension.</param>
/// <param name="mediaType">The <see cref="MediaTypeHeaderValue"/> to associate with.</param>
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", Justification = "There is no meaningful System.Uri representation for a path suffix such as '.xml'")]
public static void AddUriPathExtensionMapping(
this MediaTypeFormatter formatter,
string uriPathExtension,
MediaTypeHeaderValue mediaType)
{
if (formatter == null)
{
throw new ArgumentNullException("formatter");
}
UriPathExtensionMapping mapping = new UriPathExtensionMapping(uriPathExtension, mediaType);
formatter.MediaTypeMappings.Add(mapping);
}
/// <summary>
/// Updates the given <paramref name="formatter"/>'s set of <see cref="MediaTypeMapping"/> elements
/// so that it associates the <paramref name="mediaType"/> with <see cref="HttpRequestMessage"/> whose <see cref="IHttpRouteData"/> contains a URL Parameter {ext}
/// with the given <paramref name="uriPathExtension"/>.
/// </summary>
/// <param name="formatter">The <see cref="MediaTypeFormatter"/> to receive the new <see cref="UriPathExtensionMapping"/> item.</param>
/// <param name="uriPathExtension">The string of the <see cref="Uri"/> path extension.</param>
/// <param name="mediaType">The string media type to associate with.</param>
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", Justification = "There is no meaningful System.Uri representation for a path suffix such as '.xml'")]
public static void AddUriPathExtensionMapping(this MediaTypeFormatter formatter, string uriPathExtension, string mediaType)
{
if (formatter == null)
{
throw new ArgumentNullException("formatter");
}
UriPathExtensionMapping mapping = new UriPathExtensionMapping(uriPathExtension, mediaType);
formatter.MediaTypeMappings.Add(mapping);
}
}
}

View File

@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace System.Web.Http.Routing
{
// Represents a segment of a URI that is not a separator. It contains subsegments such as literals and parameters.
internal sealed class PathContentSegment : PathSegment
{
public PathContentSegment(IList<PathSubsegment> subsegments)
{
Subsegments = subsegments;
}
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Not changing original algorithm.")]
public bool IsCatchAll
{
get
{
// TODO: Verify this is correct. Maybe add an assert.
return Subsegments.Any<PathSubsegment>(seg => (seg is PathParameterSubsegment) && ((PathParameterSubsegment)seg).IsCatchAll);
}
}
public IList<PathSubsegment> Subsegments { get; private set; }
#if ROUTE_DEBUGGING
public override string LiteralText
{
get
{
List<string> s = new List<string>();
foreach (PathSubsegment subsegment in Subsegments)
{
s.Add(subsegment.LiteralText);
}
return String.Join(String.Empty, s.ToArray());
}
}
public override string ToString()
{
List<string> s = new List<string>();
foreach (PathSubsegment subsegment in Subsegments)
{
s.Add(subsegment.ToString());
}
return "[ " + String.Join(", ", s.ToArray()) + " ]";
}
#endif
}
}

View File

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.Routing
{
// Represents a literal subsegment of a ContentPathSegment
internal sealed class PathLiteralSubsegment : PathSubsegment
{
public PathLiteralSubsegment(string literal)
{
Literal = literal;
}
public string Literal { get; private set; }
#if ROUTE_DEBUGGING
public override string LiteralText
{
get
{
return Literal;
}
}
public override string ToString()
{
return "\"" + Literal + "\"";
}
#endif
}
}

View File

@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.Routing
{
// Represents a parameter subsegment of a ContentPathSegment
internal sealed class PathParameterSubsegment : PathSubsegment
{
public PathParameterSubsegment(string parameterName)
{
if (parameterName.StartsWith("*", StringComparison.Ordinal))
{
ParameterName = parameterName.Substring(1);
IsCatchAll = true;
}
else
{
ParameterName = parameterName;
}
}
public bool IsCatchAll { get; private set; }
public string ParameterName { get; private set; }
#if ROUTE_DEBUGGING
public override string LiteralText
{
get
{
return "{" + (IsCatchAll ? "*" : String.Empty) + ParameterName + "}";
}
}
public override string ToString()
{
return "{" + (IsCatchAll ? "*" : String.Empty) + ParameterName + "}";
}
#endif
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.Routing
{
// Represents a segment of a URI such as a separator or content
internal abstract class PathSegment
{
#if ROUTE_DEBUGGING
public abstract string LiteralText
{
get;
}
#endif
}
}

View File

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.Routing
{
// Represents a "/" separator in a URI
internal sealed class PathSeparatorSegment : PathSegment
{
#if ROUTE_DEBUGGING
public override string LiteralText
{
get
{
return "/";
}
}
public override string ToString()
{
return "\"/\"";
}
#endif
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
namespace System.Web.Http.Routing
{
// Represents a subsegment of a ContentPathSegment such as a parameter or a literal.
internal abstract class PathSubsegment
{
#if ROUTE_DEBUGGING
public abstract string LiteralText
{
get;
}
#endif
}
}

Some files were not shown because too many files have changed in this diff Show More