Add documentation for Routing surface area (#26513)
* Add documentation for Routing surface area * Apply suggestions from code review Co-authored-by: James Newton-King <james@newtonking.com> * Fix up some doc strings Co-authored-by: James Newton-King <james@newtonking.com>
This commit is contained in:
parent
63baa06482
commit
cd94cd63b7
|
|
@ -4,7 +4,7 @@
|
|||
namespace Microsoft.AspNetCore.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of contants for HTTP status codes.
|
||||
/// A collection of constants for HTTP status codes.
|
||||
///
|
||||
/// Status Codes listed at http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Http;
|
|||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a contract for a handler of a route.
|
||||
/// Defines a contract for a handler of a route.
|
||||
/// </summary>
|
||||
public interface IRouteHandler
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
|||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// Interface for implementing a router.
|
||||
/// </summary>
|
||||
public interface IRouter
|
||||
{
|
||||
|
|
|
|||
|
|
@ -205,4 +205,4 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ using Microsoft.Extensions.Options;
|
|||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Constains extensions for configuring routing on an <see cref="IApplicationBuilder"/>.
|
||||
/// </summary>
|
||||
public static class EndpointRoutingApplicationBuilderExtensions
|
||||
{
|
||||
private const string EndpointRouteBuilder = "__EndpointRouteBuilder";
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@ namespace Microsoft.AspNetCore.Routing
|
|||
_dataSources = dataSources;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a <see cref="CompositeEndpointDataSource"/> object from <paramref name="endpointDataSources"/>.
|
||||
/// </summary>
|
||||
/// <param name="endpointDataSources">An collection of <see cref="EndpointDataSource" /> objects.</param>
|
||||
/// <returns>A <see cref="CompositeEndpointDataSource"/> </returns>
|
||||
public CompositeEndpointDataSource(IEnumerable<EndpointDataSource> endpointDataSources) : this()
|
||||
{
|
||||
_dataSources = new List<EndpointDataSource>();
|
||||
|
|
@ -62,6 +67,9 @@ namespace Microsoft.AspNetCore.Routing
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the collection of <see cref="EndpointDataSource"/> instances associated with the object.
|
||||
/// </summary>
|
||||
public IEnumerable<EndpointDataSource> DataSources => _dataSources;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -123,12 +131,12 @@ namespace Microsoft.AspNetCore.Routing
|
|||
// Refresh the endpoints from datasource so that callbacks can get the latest endpoints
|
||||
_endpoints = _dataSources.SelectMany(d => d.Endpoints).ToArray();
|
||||
|
||||
// Prevent consumers from re-registering callback to inflight events as that can
|
||||
// Prevent consumers from re-registering callback to inflight events as that can
|
||||
// cause a stackoverflow
|
||||
// Example:
|
||||
// 1. B registers A
|
||||
// 2. A fires event causing B's callback to get called
|
||||
// 3. B executes some code in its callback, but needs to re-register callback
|
||||
// 3. B executes some code in its callback, but needs to re-register callback
|
||||
// in the same callback
|
||||
var oldTokenSource = _cts;
|
||||
var oldToken = _consumerChangeToken;
|
||||
|
|
|
|||
|
|
@ -7,10 +7,14 @@ using Microsoft.AspNetCore.Http;
|
|||
namespace Microsoft.AspNetCore.Routing.Constraints
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a constraint on an optional parameter. If the parameter is present, then it is constrained by InnerConstraint.
|
||||
/// Defines a constraint on an optional parameter. If the parameter is present, then it is constrained by InnerConstraint.
|
||||
/// </summary>
|
||||
public class OptionalRouteConstraint : IRouteConstraint
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="OptionalRouteConstraint"/> instance given the <paramref name="innerConstraint"/>.
|
||||
/// </summary>
|
||||
/// <param name="innerConstraint"></param>
|
||||
public OptionalRouteConstraint(IRouteConstraint innerConstraint)
|
||||
{
|
||||
if (innerConstraint == null)
|
||||
|
|
@ -21,8 +25,12 @@ namespace Microsoft.AspNetCore.Routing.Constraints
|
|||
InnerConstraint = innerConstraint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IRouteConstraint"/> associated with the optional parameter.
|
||||
/// </summary>
|
||||
public IRouteConstraint InnerConstraint { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Match(
|
||||
HttpContext? httpContext,
|
||||
IRouter? route,
|
||||
|
|
|
|||
|
|
@ -8,10 +8,17 @@ using Microsoft.AspNetCore.Http;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing.Constraints
|
||||
{
|
||||
/// <summary>
|
||||
/// Constrains a route parameter to match a regular expression.
|
||||
/// </summary>
|
||||
public class RegexRouteConstraint : IRouteConstraint
|
||||
{
|
||||
private static readonly TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for a <see cref="RegexRouteConstraint"/> given a <paramref name="regex"/>.
|
||||
/// </summary>
|
||||
/// <param name="regex">A <see cref="Regex"/> instance to use as a constraint.</param>
|
||||
public RegexRouteConstraint(Regex regex)
|
||||
{
|
||||
if (regex == null)
|
||||
|
|
@ -22,6 +29,10 @@ namespace Microsoft.AspNetCore.Routing.Constraints
|
|||
Constraint = regex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for a <see cref="RegexRouteConstraint"/> given a <paramref name="regexPattern"/>.
|
||||
/// </summary>
|
||||
/// <param name="regexPattern">A string containing the regex pattern.</param>
|
||||
public RegexRouteConstraint(string regexPattern)
|
||||
{
|
||||
if (regexPattern == null)
|
||||
|
|
@ -35,8 +46,12 @@ namespace Microsoft.AspNetCore.Routing.Constraints
|
|||
RegexMatchTimeout);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the regular expression used in the route constraint.
|
||||
/// </summary>
|
||||
public Regex Constraint { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Match(
|
||||
HttpContext? httpContext,
|
||||
IRouter? route,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ namespace Microsoft.AspNetCore.Routing
|
|||
/// </summary>
|
||||
public sealed class DataTokensMetadata : IDataTokensMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor for a new <see cref="DataTokensMetadata"/> given <paramref name="dataTokens"/>.
|
||||
/// </summary>
|
||||
/// <param name="dataTokens">The data tokens.</param>
|
||||
public DataTokensMetadata(IReadOnlyDictionary<string, object> dataTokens)
|
||||
{
|
||||
if (dataTokens == null)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,14 @@
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface for an <see cref="IRouter"/> with a name.
|
||||
/// </summary>
|
||||
public interface INamedRouter : IRouter
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the router. Can be null.
|
||||
/// </summary>
|
||||
string? Name { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,15 @@
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for a router that supports appending new routes.
|
||||
/// </summary>
|
||||
public interface IRouteCollection : IRouter
|
||||
{
|
||||
/// <summary>
|
||||
/// Appends the collection of routes defined in <paramref name="router"/>.
|
||||
/// </summary>
|
||||
/// <param name="router">A <see cref="IRouter"/> instance.</param>
|
||||
void Add(IRouter router);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,16 @@ using Microsoft.AspNetCore.Routing.Template;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains methods for parsing processing constraints from a route definition.
|
||||
/// </summary>
|
||||
public static class InlineRouteParameterParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses a string representing the provided <paramref name="routeParameter"/> into a <see cref="TemplatePart"/>.
|
||||
/// </summary>
|
||||
/// <param name="routeParameter">A string representation of the route parameter.</param>
|
||||
/// <returns>A <see cref="TemplatePart"/> instance.</returns>
|
||||
public static TemplatePart ParseRouteParameter(string routeParameter)
|
||||
{
|
||||
if (routeParameter == null)
|
||||
|
|
|
|||
|
|
@ -26,11 +26,20 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
{
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for a <see cref="DfaGraphWriter"/> given <paramref name="services"/>.
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
|
||||
public DfaGraphWriter(IServiceProvider services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays a graph representation of <paramref name="dataSource"/> in DOT.
|
||||
/// </summary>
|
||||
/// <param name="dataSource">The <see cref="EndpointDataSource"/> to extract routes from.</param>
|
||||
/// <param name="writer">The <see cref="TextWriter"/> to which the content is written.</param>
|
||||
public void Write(EndpointDataSource dataSource, TextWriter writer)
|
||||
{
|
||||
var builder = _services.GetRequiredService<DfaMatcherBuilder>();
|
||||
|
|
@ -46,7 +55,7 @@ namespace Microsoft.AspNetCore.Routing.Internal
|
|||
|
||||
// Assign each node a sequential index.
|
||||
var visited = new Dictionary<DfaNode, int>();
|
||||
|
||||
|
||||
var tree = builder.BuildDfaTree(includeLabel: true);
|
||||
|
||||
writer.WriteLine("digraph DFA {");
|
||||
|
|
|
|||
|
|
@ -87,6 +87,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
/// </typeparam>
|
||||
public abstract class EndpointMetadataComparer<TMetadata> : IComparer<Endpoint> where TMetadata : class
|
||||
{
|
||||
/// <summary>
|
||||
/// A default instance of the <see cref="EndpointMetadataComparer"/>.
|
||||
/// </summary>
|
||||
public static readonly EndpointMetadataComparer<TMetadata> Default = new DefaultComparer<TMetadata>();
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
private const string WildcardPrefix = "*.";
|
||||
|
||||
// Run after HTTP methods, but before 'default'.
|
||||
/// <inheritdoc />
|
||||
public override int Order { get; } = -100;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IComparer<Endpoint> Comparer { get; } = new HostMetadataEndpointComparer();
|
||||
|
||||
bool INodeBuilderPolicy.AppliesToEndpoints(IReadOnlyList<Endpoint> endpoints)
|
||||
|
|
@ -70,6 +72,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
|
||||
{
|
||||
if (httpContext == null)
|
||||
|
|
@ -189,6 +192,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
throw new InvalidOperationException($"Could not parse host: {host}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<PolicyNodeEdge> GetEdges(IReadOnlyList<Endpoint> endpoints)
|
||||
{
|
||||
if (endpoints == null)
|
||||
|
|
@ -273,6 +277,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
|||
.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public PolicyJumpTable BuildJumpTable(int exitDestination, IReadOnlyList<PolicyJumpTableEdge> edges)
|
||||
{
|
||||
if (edges == null)
|
||||
|
|
|
|||
|
|
@ -6,12 +6,31 @@ using Microsoft.AspNetCore.Http;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing.Matching
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements an interface for a matcher policy with support for generating graph representations of the endpoints.
|
||||
/// </summary>
|
||||
public interface INodeBuilderPolicy
|
||||
{
|
||||
/// <summary>
|
||||
/// Evaluates if the policy matches any of the endpoints provided in <paramref name="endpoints"/>.
|
||||
/// </summary>
|
||||
/// <param name="endpoints">A list of <see cref="Endpoint"/>.</param>
|
||||
/// <returns><see langword="true"/> if the policy applies to any of the provided <paramref name="endpoints"/>.</returns>
|
||||
bool AppliesToEndpoints(IReadOnlyList<Endpoint> endpoints);
|
||||
|
||||
/// <summary>
|
||||
/// Generates a graph that representations the relationship between endpoints and hosts.
|
||||
/// </summary>
|
||||
/// <param name="endpoints">A list of <see cref="Endpoint"/>.</param>
|
||||
/// <returns>A graph representing the relationship between endpoints and hosts.</returns>
|
||||
IReadOnlyList<PolicyNodeEdge> GetEdges(IReadOnlyList<Endpoint> endpoints);
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a jump table given the a set of <paramref name="edges"/>.
|
||||
/// </summary>
|
||||
/// <param name="exitDestination">The default destination for lookups.</param>
|
||||
/// <param name="edges">A list of <see cref="PolicyJumpTableEdge"/>.</param>
|
||||
/// <returns>A <see cref="PolicyJumpTable"/> instance.</returns>
|
||||
PolicyJumpTable BuildJumpTable(int exitDestination, IReadOnlyList<PolicyJumpTableEdge> edges);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,15 @@ using Microsoft.AspNetCore.Http;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing.Matching
|
||||
{
|
||||
/// <summary>
|
||||
/// Supports retrieving endpoints that fulfill a certain matcher policy.
|
||||
/// </summary>
|
||||
public abstract class PolicyJumpTable
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the destination for a given <paramref name="httpContext"/> in the current jump table.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||
public abstract int GetDestination(HttpContext httpContext);
|
||||
|
||||
internal virtual string DebuggerToString()
|
||||
|
|
|
|||
|
|
@ -3,16 +3,31 @@
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing.Matching
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an entry in a <see cref="PolicyJumpTable"/>.
|
||||
/// </summary>
|
||||
public readonly struct PolicyJumpTableEdge
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="PolicyJumpTableEdge"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="state">Represents the match heuristic of the policy.</param>
|
||||
/// <param name="destination"></param>
|
||||
public PolicyJumpTableEdge(object state, int destination)
|
||||
{
|
||||
State = state ?? throw new System.ArgumentNullException(nameof(state));
|
||||
Destination = destination;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object used to represent the match heuristic. Can be a host, HTTP method, etc.
|
||||
/// depending on the matcher policy.
|
||||
/// </summary>
|
||||
public object State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the destination of the current entry.
|
||||
/// </summary>
|
||||
public int Destination { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,16 +6,31 @@ using Microsoft.AspNetCore.Http;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing.Matching
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an edge in a matcher policy graph.
|
||||
/// </summary>
|
||||
public readonly struct PolicyNodeEdge
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="PolicyNodeEdge"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="state">Represents the match heuristic of the policy.</param>
|
||||
/// <param name="endpoints">Represents the endpoints that match the policy</param>
|
||||
public PolicyNodeEdge(object state, IReadOnlyList<Endpoint> endpoints)
|
||||
{
|
||||
State = state ?? throw new System.ArgumentNullException(nameof(state));
|
||||
Endpoints = endpoints ?? throw new System.ArgumentNullException(nameof(endpoints));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the endpoints that match the policy defined by <see cref="State"/>.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Endpoint> Endpoints { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object used to represent the match heuristic. Can be a host, HTTP method, etc.
|
||||
/// depending on the matcher policy.
|
||||
/// </summary>
|
||||
public object State { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Microsoft.AspNetCore.Routing.Route
|
|||
Microsoft.AspNetCore.Routing.RouteCollection</Description>
|
||||
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
||||
<IsAspNetCoreApp>true</IsAspNetCoreApp>
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
<NoWarn>$(NoWarn.Replace('1591', ''))</NoWarn>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>aspnetcore;routing</PackageTags>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for adding new handlers to a <see cref="IRouteBuilder"/>.
|
||||
/// </summary>
|
||||
public static class RequestDelegateRouteBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -7,10 +7,19 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an instance of a route.
|
||||
/// </summary>
|
||||
public class Route : RouteBase
|
||||
{
|
||||
private readonly IRouter _target;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Route"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="target">An <see cref="IRouter"/> instance associated with the component.</param>
|
||||
/// <param name="routeTemplate">A string representation of the route template.</param>
|
||||
/// <param name="inlineConstraintResolver">An <see cref="IInlineConstraintResolver"/> used for resolving inline constraints.</param>
|
||||
public Route(
|
||||
IRouter target,
|
||||
string routeTemplate,
|
||||
|
|
@ -25,6 +34,15 @@ namespace Microsoft.AspNetCore.Routing
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Route"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="target">An <see cref="IRouter"/> instance associated with the component.</param>
|
||||
/// <param name="routeTemplate">A string representation of the route template.</param>
|
||||
/// <param name="defaults">The default values for parameters in the route.</param>
|
||||
/// <param name="constraints">The constraints for the route.</param>
|
||||
/// <param name="dataTokens">The data tokens for the route.</param>
|
||||
/// <param name="inlineConstraintResolver">An <see cref="IInlineConstraintResolver"/> used for resolving inline constraints.</param>
|
||||
public Route(
|
||||
IRouter target,
|
||||
string routeTemplate,
|
||||
|
|
@ -36,6 +54,16 @@ namespace Microsoft.AspNetCore.Routing
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Route"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="target">An <see cref="IRouter"/> instance associated with the component.</param>
|
||||
/// <param name="routeName">The name of the route.</param>
|
||||
/// <param name="routeTemplate">A string representation of the route template.</param>
|
||||
/// <param name="defaults">The default values for parameters in the route.</param>
|
||||
/// <param name="constraints">The constraints for the route.</param>
|
||||
/// <param name="dataTokens">The data tokens for the route.</param>
|
||||
/// <param name="inlineConstraintResolver">An <see cref="IInlineConstraintResolver"/> used for resolving inline constraints.</param>
|
||||
public Route(
|
||||
IRouter target,
|
||||
string? routeName,
|
||||
|
|
@ -45,11 +73,11 @@ namespace Microsoft.AspNetCore.Routing
|
|||
RouteValueDictionary? dataTokens,
|
||||
IInlineConstraintResolver inlineConstraintResolver)
|
||||
: base(
|
||||
routeTemplate,
|
||||
routeName,
|
||||
inlineConstraintResolver,
|
||||
defaults,
|
||||
constraints,
|
||||
routeTemplate,
|
||||
routeName,
|
||||
inlineConstraintResolver,
|
||||
defaults,
|
||||
constraints,
|
||||
dataTokens)
|
||||
{
|
||||
if (target == null)
|
||||
|
|
@ -60,14 +88,19 @@ namespace Microsoft.AspNetCore.Routing
|
|||
_target = target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representation of the route template.
|
||||
/// </summary>
|
||||
public string? RouteTemplate => ParsedTemplate.TemplateText;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task OnRouteMatched(RouteContext context)
|
||||
{
|
||||
context.RouteData.Routers.Add(_target);
|
||||
return _target.RouteAsync(context);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override VirtualPathData? OnVirtualPathGenerated(VirtualPathContext context)
|
||||
{
|
||||
return _target.GetVirtualPath(context);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ using Microsoft.Extensions.Logging;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class implementation of an <see cref="IRouter"/>.
|
||||
/// </summary>
|
||||
public abstract class RouteBase : IRouter, INamedRouter
|
||||
{
|
||||
private readonly object _loggersLock = new object();
|
||||
|
|
@ -23,6 +26,15 @@ namespace Microsoft.AspNetCore.Routing
|
|||
private ILogger? _logger;
|
||||
private ILogger? _constraintLogger;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="RouteBase"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="template">The route template.</param>
|
||||
/// <param name="name">The name of the route.</param>
|
||||
/// <param name="constraintResolver">An <see cref="IInlineConstraintResolver"/> used for resolving inline constraints.</param>
|
||||
/// <param name="defaults">The default values for parameters in the route.</param>
|
||||
/// <param name="constraints">The constraints for the route.</param>
|
||||
/// <param name="dataTokens">The data tokens for the route.</param>
|
||||
public RouteBase(
|
||||
string? template,
|
||||
string? name,
|
||||
|
|
@ -56,20 +68,45 @@ namespace Microsoft.AspNetCore.Routing
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of constraints associated with each route.
|
||||
/// </summary>
|
||||
public virtual IDictionary<string, IRouteConstraint> Constraints { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the resolver used for resolving inline constraints.
|
||||
/// </summary>
|
||||
protected virtual IInlineConstraintResolver ConstraintResolver { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data tokens associated with the route.
|
||||
/// </summary>
|
||||
public virtual RouteValueDictionary DataTokens { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default values for each route parameter.
|
||||
/// </summary>
|
||||
public virtual RouteValueDictionary Defaults { get; protected set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string? Name { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="RouteTemplate"/> associated with the route.
|
||||
/// </summary>
|
||||
public virtual RouteTemplate ParsedTemplate { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Executes asynchronously whenever routing occurs.
|
||||
/// </summary>
|
||||
/// <param name="context">A <see cref="RouteContext"/> instance.</param>
|
||||
protected abstract Task OnRouteMatched(RouteContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Executes whenever a virtual path is dervied from a <paramref name="context"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">A <see cref="VirtualPathContext"/> instance.</param>
|
||||
/// <returns>A <see cref="VirtualPathData"/> instance.</returns>
|
||||
protected abstract VirtualPathData? OnVirtualPathGenerated(VirtualPathContext context);
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -168,6 +205,12 @@ namespace Microsoft.AspNetCore.Routing
|
|||
return pathData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts constatins from a given <see cref="RouteTemplate"/>.
|
||||
/// </summary>
|
||||
/// <param name="inlineConstraintResolver">An <see cref="IInlineConstraintResolver"/> used for resolving inline constraints.</param>
|
||||
/// <param name="parsedTemplate">A <see cref="RouteTemplate"/> instance.</param>
|
||||
/// <param name="constraints">A collection of constraints on the route template.</param>
|
||||
protected static IDictionary<string, IRouteConstraint> GetConstraints(
|
||||
IInlineConstraintResolver inlineConstraintResolver,
|
||||
RouteTemplate parsedTemplate,
|
||||
|
|
@ -199,6 +242,11 @@ namespace Microsoft.AspNetCore.Routing
|
|||
return constraintBuilder.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default values for parameters in a templates.
|
||||
/// </summary>
|
||||
/// <param name="parsedTemplate">A <see cref="RouteTemplate"/> instance.</param>
|
||||
/// <param name="defaults">A collection of defaults for each parameter.</param>
|
||||
protected static RouteValueDictionary GetDefaults(
|
||||
RouteTemplate parsedTemplate,
|
||||
RouteValueDictionary? defaults)
|
||||
|
|
@ -296,6 +344,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return ParsedTemplate.TemplateText!;
|
||||
|
|
|
|||
|
|
@ -10,13 +10,26 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides support for specifying routes in an application.
|
||||
/// </summary>
|
||||
public class RouteBuilder : IRouteBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="RouteBuilder"/> instance given an <paramref name="applicationBuilder"/>.
|
||||
/// </summary>
|
||||
/// <param name="applicationBuilder">An <see cref="IApplicationBuilder"/> instance.</param>
|
||||
public RouteBuilder(IApplicationBuilder applicationBuilder)
|
||||
: this(applicationBuilder, defaultHandler: null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="RouteBuilder"/> instance given an <paramref name="applicationBuilder"/>
|
||||
/// and <paramref name="defaultHandler"/>.
|
||||
/// </summary>
|
||||
/// <param name="applicationBuilder">An <see cref="IApplicationBuilder"/> instance.</param>
|
||||
/// <param name="defaultHandler">The default <see cref="IRouter"/> used if a new route is added without a handler.</param>
|
||||
public RouteBuilder(IApplicationBuilder applicationBuilder, IRouter? defaultHandler)
|
||||
{
|
||||
if (applicationBuilder == null)
|
||||
|
|
@ -39,14 +52,19 @@ namespace Microsoft.AspNetCore.Routing
|
|||
Routes = new List<IRouter>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IApplicationBuilder ApplicationBuilder { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRouter? DefaultHandler { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<IRouter> Routes { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRouter Build()
|
||||
{
|
||||
var routeCollection = new RouteCollection();
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ using Microsoft.Extensions.Options;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Supports managing a collection fo multiple routes.
|
||||
/// </summary>
|
||||
public class RouteCollection : IRouteCollection
|
||||
{
|
||||
private readonly static char[] UrlQueryDelimiters = new char[] { '?', '#' };
|
||||
|
|
@ -24,16 +27,24 @@ namespace Microsoft.AspNetCore.Routing
|
|||
|
||||
private RouteOptions? _options;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the route at a given index.
|
||||
/// </summary>
|
||||
/// <value>The route at the given index.</value>
|
||||
public IRouter this[int index]
|
||||
{
|
||||
get { return _routes[index]; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of routes registered in the collection.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get { return _routes.Count; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Add(IRouter router)
|
||||
{
|
||||
if (router == null)
|
||||
|
|
@ -57,6 +68,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
_routes.Add(router);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async virtual Task RouteAsync(RouteContext context)
|
||||
{
|
||||
// Perf: We want to avoid allocating a new RouteData for each route we need to process.
|
||||
|
|
@ -88,6 +100,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual VirtualPathData? GetVirtualPath(VirtualPathContext context)
|
||||
{
|
||||
EnsureOptions(context.HttpContext);
|
||||
|
|
|
|||
|
|
@ -9,8 +9,24 @@ using Microsoft.Extensions.Logging;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Use to evaluate if all route parameter values match their constraints.
|
||||
/// </summary>
|
||||
public static class RouteConstraintMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines if <paramref name="routeValues"/> match the provided <paramref name="constraints"/>.
|
||||
/// </summary>
|
||||
/// <param name="constraints">The constraints for the route.</param>
|
||||
/// <param name="routeValues">The route parameter values extracted from the matched route.</param>
|
||||
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||
/// <param name="route">The router that this constraint belongs to.</param>
|
||||
/// <param name="routeDirection">
|
||||
/// Indicates whether the constraint check is performed
|
||||
/// when the incoming request is handled or when a URL is generated.
|
||||
/// </param>
|
||||
/// <param name="logger">The <see cref="ILogger{TCategoryName}"/>.</param>
|
||||
/// <returns><see langword="true"/> if the all route values match their constraints.</returns>
|
||||
public static bool Match(
|
||||
IDictionary<string, IRouteConstraint> constraints,
|
||||
RouteValueDictionary routeValues,
|
||||
|
|
|
|||
|
|
@ -8,12 +8,27 @@ using Microsoft.AspNetCore.Routing.Patterns;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Supports building a new <see cref="RouteEndpoint"/>.
|
||||
/// </summary>
|
||||
public sealed class RouteEndpointBuilder : EndpointBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="RoutePattern"/> associated with this endpoint.
|
||||
/// </summary>
|
||||
public RoutePattern RoutePattern { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order assigned to the endpoint.
|
||||
/// </summary>
|
||||
public int Order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="RouteEndpointBuilder"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="requestDelegate">The delegate used to process requests for the endpoint.</param>
|
||||
/// <param name="routePattern">The <see cref="RoutePattern"/> to use in URL matching.</param>
|
||||
/// <param name="order">The order assigned to the endpoint.</param>
|
||||
public RouteEndpointBuilder(
|
||||
RequestDelegate requestDelegate,
|
||||
RoutePattern routePattern,
|
||||
|
|
@ -24,6 +39,7 @@ namespace Microsoft.AspNetCore.Routing
|
|||
Order = order;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Endpoint Build()
|
||||
{
|
||||
if (RequestDelegate is null)
|
||||
|
|
|
|||
|
|
@ -8,26 +8,36 @@ using Microsoft.AspNetCore.Http;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Supports implementing a handler that executes for a given route.
|
||||
/// </summary>
|
||||
public class RouteHandler : IRouteHandler, IRouter
|
||||
{
|
||||
private readonly RequestDelegate _requestDelegate;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="RouteHandler"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="requestDelegate">The delegate used to process requests.</param>
|
||||
public RouteHandler(RequestDelegate requestDelegate)
|
||||
{
|
||||
_requestDelegate = requestDelegate;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestDelegate GetRequestHandler(HttpContext httpContext, RouteData routeData)
|
||||
{
|
||||
return _requestDelegate;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public VirtualPathData? GetVirtualPath(VirtualPathContext context)
|
||||
{
|
||||
// Nothing to do.
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RouteAsync(RouteContext context)
|
||||
{
|
||||
context.Handler = _requestDelegate;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ using Microsoft.AspNetCore.Routing.Constraints;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the configurable options on a route.
|
||||
/// </summary>
|
||||
public class RouteOptions
|
||||
{
|
||||
private IDictionary<string, Type> _constraintTypeMap = GetDefaultConstraintMap();
|
||||
|
|
@ -64,6 +67,9 @@ namespace Microsoft.AspNetCore.Routing
|
|||
/// </remarks>
|
||||
public bool SuppressCheckForUnhandledSecurityMetadata { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a collection of constraints on the current route.
|
||||
/// </summary>
|
||||
public IDictionary<string, Type> ConstraintMap
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ namespace Microsoft.AspNetCore.Routing
|
|||
/// </remarks>
|
||||
public class RouteValueEqualityComparer : IEqualityComparer<object?>
|
||||
{
|
||||
/// <summary>
|
||||
/// A default instance of the <see cref="RouteValueEqualityComparer"/>.
|
||||
/// </summary>
|
||||
public static readonly RouteValueEqualityComparer Default = new RouteValueEqualityComparer();
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -9,12 +9,21 @@ using Microsoft.Extensions.Logging;
|
|||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Middleware responsible for routing.
|
||||
/// </summary>
|
||||
public class RouterMiddleware
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IRouter _router;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="RouterMiddleware"/> instance with a given <paramref name="router"/>.
|
||||
/// </summary>
|
||||
/// <param name="next">The delegate representing the remaining middleware in the request pipeline.</param>
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
|
||||
/// <param name="router">The <see cref="IRouter"/> to use for routing requests.</param>
|
||||
public RouterMiddleware(
|
||||
RequestDelegate next,
|
||||
ILoggerFactory loggerFactory,
|
||||
|
|
@ -26,6 +35,11 @@ namespace Microsoft.AspNetCore.Builder
|
|||
_logger = loggerFactory.CreateLogger<RouterMiddleware>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the handler associated with the <see cref="RouteContext"/>
|
||||
/// derived from <paramref name="httpContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">A <see cref="HttpContext"/> instance.</param>
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var context = new RouteContext(httpContext);
|
||||
|
|
|
|||
|
|
@ -3,8 +3,12 @@
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// A feature for routing functionality.
|
||||
/// </summary>
|
||||
public class RoutingFeature : IRoutingFeature
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public RouteData? RouteData { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
Constraint = constraint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="InlineConstraint"/> instance given a <see cref="RoutePatternParameterPolicyReference"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">A <see cref="RoutePatternParameterPolicyReference"/> instance.</param>
|
||||
public InlineConstraint(RoutePatternParameterPolicyReference other)
|
||||
{
|
||||
if (other == null)
|
||||
|
|
|
|||
|
|
@ -15,11 +15,17 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
/// </summary>
|
||||
public static class RoutePrecedence
|
||||
{
|
||||
// Compute the precedence for matching a provided url
|
||||
// e.g.: /api/template == 1.1
|
||||
// /api/template/{id} == 1.13
|
||||
// /api/{id:int} == 1.2
|
||||
// /api/template/{id:int} == 1.12
|
||||
/// <summary>
|
||||
/// Compute the precedence for matching a provided url
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// e.g.: /api/template == 1.1
|
||||
/// /api/template/{id} == 1.13
|
||||
/// /api/{id:int} == 1.2
|
||||
/// /api/template/{id:int} == 1.12
|
||||
/// </example>
|
||||
/// <param name="template">The <see cref="RouteTemplate"/> to compute precendence for.</param>
|
||||
/// <returns>A <see cref="decimal"/> representing the route's precendence.</returns>
|
||||
public static decimal ComputeInbound(RouteTemplate template)
|
||||
{
|
||||
ValidateSegementLength(template.Segments.Count);
|
||||
|
|
@ -61,11 +67,17 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
return precedence;
|
||||
}
|
||||
|
||||
// Compute the precedence for generating a url
|
||||
// e.g.: /api/template == 5.5
|
||||
// /api/template/{id} == 5.53
|
||||
// /api/{id:int} == 5.4
|
||||
// /api/template/{id:int} == 5.54
|
||||
/// <summary>
|
||||
/// Compute the precedence for generating a url.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// e.g.: /api/template == 5.5
|
||||
/// /api/template/{id} == 5.53
|
||||
/// /api/{id:int} == 5.4
|
||||
/// /api/template/{id:int} == 5.54
|
||||
/// </example>
|
||||
/// <param name="template">The <see cref="RouteTemplate"/> to compute precendence for.</param>
|
||||
/// <returns>A <see cref="decimal"/> representing the route's precendence.</returns>
|
||||
public static decimal ComputeOutbound(RouteTemplate template)
|
||||
{
|
||||
ValidateSegementLength(template.Segments.Count);
|
||||
|
|
|
|||
|
|
@ -11,11 +11,18 @@ using Microsoft.AspNetCore.Routing.Patterns;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing.Template
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the template for a route.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{DebuggerToString()}")]
|
||||
public class RouteTemplate
|
||||
{
|
||||
private const string SeparatorString = "/";
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="RouteTemplate"/> instance given <paramref name="other"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">A <see cref="RoutePattern"/> instance.</param>
|
||||
public RouteTemplate(RoutePattern other)
|
||||
{
|
||||
if (other == null)
|
||||
|
|
@ -42,6 +49,12 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a a new <see cref="RouteTemplate" /> instance given the <paramref name="template"/> string
|
||||
/// and a list of <paramref name="segments"/>. Computes the parameters in the route template.
|
||||
/// </summary>
|
||||
/// <param name="template">A string representation of the route template.</param>
|
||||
/// <param name="segments">A list of <see cref="TemplateSegment"/>.</param>
|
||||
public RouteTemplate(string template, List<TemplateSegment> segments)
|
||||
{
|
||||
if (segments == null)
|
||||
|
|
@ -68,12 +81,26 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the string representation of the route template.
|
||||
/// </summary>
|
||||
public string? TemplateText { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of <see cref="TemplatePart"/> that represent that parameters defined in the route template.
|
||||
/// </summary>
|
||||
public IList<TemplatePart> Parameters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of <see cref="TemplateSegment"/> that compromise the route template.
|
||||
/// </summary>
|
||||
public IList<TemplateSegment> Segments { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="TemplateSegment"/> at a given index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the element to retrieve.</param>
|
||||
/// <returns>A <see cref="TemplateSegment"/> instance.</returns>
|
||||
public TemplateSegment? GetSegment(int index)
|
||||
{
|
||||
if (index < 0)
|
||||
|
|
@ -109,7 +136,7 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="RouteTemplate"/> to the equivalent
|
||||
/// Converts the <see cref="RouteTemplate"/> to the equivalent
|
||||
/// <see cref="RoutePattern"/>
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="RoutePattern"/>.</returns>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ using Microsoft.Extensions.ObjectPool;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing.Template
|
||||
{
|
||||
/// <summary>
|
||||
/// Supports processing and binding parameter values in a route template.
|
||||
/// </summary>
|
||||
public class TemplateBinder
|
||||
{
|
||||
private readonly UrlEncoder _urlEncoder;
|
||||
|
|
@ -159,7 +162,12 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
_slots = AssignSlots(_pattern, _filters);
|
||||
}
|
||||
|
||||
// Step 1: Get the list of values we're going to try to use to match and generate this URI
|
||||
/// <summary>
|
||||
/// Generates the parameter values in the route.
|
||||
/// </summary>
|
||||
/// <param name="ambientValues">The values associated with the current request.</param>
|
||||
/// <param name="values">The route values to process.</param>
|
||||
/// <returns>A <see cref="TemplateValuesResult"/> instance. Can be null.</returns>
|
||||
public TemplateValuesResult? GetValues(RouteValueDictionary? ambientValues, RouteValueDictionary values)
|
||||
{
|
||||
// Make a new copy of the slots array, we'll use this as 'scratch' space
|
||||
|
|
@ -424,10 +432,14 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
}
|
||||
|
||||
// Step 1.5: Process constraints
|
||||
//
|
||||
// Processes the constraints **if** they were passed in to the TemplateBinder constructor.
|
||||
// Returns true on success
|
||||
// Returns false + sets the name/constraint for logging on failure.
|
||||
/// <summary>
|
||||
/// Processes the constraints **if** they were passed in to the TemplateBinder constructor.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||
/// <param name="combinedValues">A dictionary that contains the parameters for the route.</param>
|
||||
/// <param name="parameterName">The name of the parameter.</param>
|
||||
/// <param name="constraint">The constraint object.</param>
|
||||
/// <returns><see langword="true"/> if constraints were processed succesfully and false otherwise.</returns>
|
||||
public bool TryProcessConstraints(HttpContext? httpContext, RouteValueDictionary combinedValues, out string? parameterName, out IRouteConstraint? constraint)
|
||||
{
|
||||
var constraints = _constraints;
|
||||
|
|
@ -447,6 +459,11 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
}
|
||||
|
||||
// Step 2: If the route is a match generate the appropriate URI
|
||||
/// <summary>
|
||||
/// Returns a string representation of the URI associated with the route.
|
||||
/// </summary>
|
||||
/// <param name="acceptedValues">A dictionary that contains the parameters for the route.</param>
|
||||
/// <returns>The string representation of the route.</returns>
|
||||
public string? BindValues(RouteValueDictionary acceptedValues)
|
||||
{
|
||||
var context = _pool.Get();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ using Microsoft.AspNetCore.Http;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing.Template
|
||||
{
|
||||
/// <summary>
|
||||
/// Supports matching paths to route templates and extracting parameter values.
|
||||
/// </summary>
|
||||
public class TemplateMatcher
|
||||
{
|
||||
private const string SeparatorString = "/";
|
||||
|
|
@ -21,6 +24,11 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
private static readonly char[] Delimiters = new char[] { SeparatorChar };
|
||||
private RoutePatternMatcher _routePatternMatcher;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="TemplateMatcher"/> instance given a <paramref name="template"/> and <paramref name="defaults"/>.
|
||||
/// </summary>
|
||||
/// <param name="template">The <see cref="RouteTemplate"/> to compare against.</param>
|
||||
/// <param name="defaults">The default values for parameters in the <paramref name="template"/>.</param>
|
||||
public TemplateMatcher(
|
||||
RouteTemplate template,
|
||||
RouteValueDictionary defaults)
|
||||
|
|
@ -62,10 +70,23 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
_routePatternMatcher = new RoutePatternMatcher(routePattern, Defaults);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default values for parameters in the <see cref="Template"/>.
|
||||
/// </summary>
|
||||
public RouteValueDictionary Defaults { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="RouteTemplate"/> to match against.
|
||||
/// </summary>
|
||||
public RouteTemplate Template { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates if the provided <paramref name="path"/> matches the <see cref="Template"/>. Populates
|
||||
/// <paramref name="values"/> with parameter values.
|
||||
/// </summary>
|
||||
/// <param name="path">A <see cref="PathString"/> representing the route to match.</param>
|
||||
/// <param name="values">A <see cref="RouteValueDictionary"/> to populate with parameter values.</param>
|
||||
/// <returns><see langword="true"/> if <paramref name="path"/> matches <see cref="Template"/>.</returns>
|
||||
public bool TryMatch(PathString path, RouteValueDictionary values)
|
||||
{
|
||||
if (values == null)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,16 @@ using Microsoft.AspNetCore.Routing.Patterns;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing.Template
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for parsing route template strings.
|
||||
/// </summary>
|
||||
public static class TemplateParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="RouteTemplate"/> for a given <paramref name="routeTemplate"/> string.
|
||||
/// </summary>
|
||||
/// <param name="routeTemplate">A string representation of the route template.</param>
|
||||
/// <returns>A <see cref="RouteTemplate"/> instance.</returns>
|
||||
public static RouteTemplate Parse(string routeTemplate)
|
||||
{
|
||||
if (routeTemplate == null)
|
||||
|
|
|
|||
|
|
@ -10,13 +10,23 @@ using Microsoft.AspNetCore.Routing.Patterns;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing.Template
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a part of a route template segment.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{DebuggerToString()}")]
|
||||
public class TemplatePart
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="TemplatePart"/> instance.
|
||||
/// </summary>
|
||||
public TemplatePart()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="TemplatePart"/> instance given a <paramref name="other"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">A <see cref="RoutePatternPart"/> instance representing the route part.</param>
|
||||
public TemplatePart(RoutePatternPart other)
|
||||
{
|
||||
IsLiteral = other.IsLiteral || other.IsSeparator;
|
||||
|
|
@ -47,6 +57,11 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="TemplatePart"/> representing a literal route part.
|
||||
/// </summary>
|
||||
/// <param name="text">The text of the literate route part.</param>
|
||||
/// <returns>A <see cref="TemplatePart"/> instance.</returns>
|
||||
public static TemplatePart CreateLiteral(string text)
|
||||
{
|
||||
return new TemplatePart()
|
||||
|
|
@ -56,6 +71,15 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="TemplatePart"/> representing a paramter part.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the parameter.</param>
|
||||
/// <param name="isCatchAll"><see langword="true"/> if the parameter is a catch-all parameter.</param>
|
||||
/// <param name="isOptional"><see langword="true"/> if the parameter is an optional parameter.</param>
|
||||
/// <param name="defaultValue">The default value of the parameter.</param>
|
||||
/// <param name="inlineConstraints">A collection of constraints associated with the parameter.</param>
|
||||
/// <returns>A <see cref="TemplatePart"/> instance.</returns>
|
||||
public static TemplatePart CreateParameter(
|
||||
string name,
|
||||
bool isCatchAll,
|
||||
|
|
@ -79,14 +103,41 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see langword="true"/> if the route part is is a catch-all part (e.g. /*).
|
||||
/// </summary>
|
||||
public bool IsCatchAll { get; private set; }
|
||||
/// <summary>
|
||||
/// <see langword="true"/> if the route part is represents a literal value.
|
||||
/// </summary>
|
||||
public bool IsLiteral { get; private set; }
|
||||
/// <summary>
|
||||
/// <see langword="true"/> if the route part represents a parameterized value.
|
||||
/// </summary>
|
||||
public bool IsParameter { get; private set; }
|
||||
/// <summary>
|
||||
/// <see langword="true"/> if the route part represents an optional part.
|
||||
/// </summary>
|
||||
public bool IsOptional { get; private set; }
|
||||
/// <summary>
|
||||
/// <see langword="true"/> if the route part represents an optional seperator.
|
||||
/// </summary>
|
||||
public bool IsOptionalSeperator { get; set; }
|
||||
/// <summary>
|
||||
/// The name of the route parameter. Can be null.
|
||||
/// </summary>
|
||||
public string? Name { get; private set; }
|
||||
/// <summary>
|
||||
/// The textual representation of the route paramter. Can be null. Used to represent route seperators and literal parts.
|
||||
/// </summary>
|
||||
public string? Text { get; private set; }
|
||||
/// <summary>
|
||||
/// The default value for route paramters. Can be null.
|
||||
/// </summary>
|
||||
public object? DefaultValue { get; private set; }
|
||||
/// <summary>
|
||||
/// The constraints associates with a route paramter.
|
||||
/// </summary>
|
||||
public IEnumerable<InlineConstraint> InlineConstraints { get; private set; } = Enumerable.Empty<InlineConstraint>();
|
||||
|
||||
internal string? DebuggerToString()
|
||||
|
|
@ -101,6 +152,10 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="RoutePatternPart"/> for the route part designated by the <see cref="TemplatePart"/>.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="RoutePatternPart"/> instance.</returns>
|
||||
public RoutePatternPart ToRoutePatternPart()
|
||||
{
|
||||
if (IsLiteral && IsOptionalSeperator)
|
||||
|
|
|
|||
|
|
@ -9,14 +9,24 @@ using Microsoft.AspNetCore.Routing.Patterns;
|
|||
|
||||
namespace Microsoft.AspNetCore.Routing.Template
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a segment of a route template.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{DebuggerToString()}")]
|
||||
public class TemplateSegment
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="TemplateSegment"/> instance.
|
||||
/// </summary>
|
||||
public TemplateSegment()
|
||||
{
|
||||
Parts = new List<TemplatePart>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="TemplateSegment"/> instance given another <see cref="RoutePatternPathSegment"/>.
|
||||
/// </summary>
|
||||
/// <param name="other">A <see cref="RoutePatternPathSegment"/> instance.</param>
|
||||
public TemplateSegment(RoutePatternPathSegment other)
|
||||
{
|
||||
if (other == null)
|
||||
|
|
@ -32,8 +42,14 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see langword="true"/> if the segment contains a single entry.
|
||||
/// </summary>
|
||||
public bool IsSimple => Parts.Count == 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of individual parts in the template segment.
|
||||
/// </summary>
|
||||
public List<TemplatePart> Parts { get; }
|
||||
|
||||
internal string DebuggerToString()
|
||||
|
|
@ -41,6 +57,10 @@ namespace Microsoft.AspNetCore.Routing.Template
|
|||
return string.Join(string.Empty, Parts.Select(p => p.DebuggerToString()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="RoutePatternPathSegment"/> for the template segment.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="RoutePatternPathSegment"/> instance.</returns>
|
||||
public RoutePatternPathSegment ToRoutePatternPathSegment()
|
||||
{
|
||||
var parts = Parts.Select(p => p.ToRoutePatternPart());
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
|||
/// </summary>
|
||||
public class TreeRouter : IRouter
|
||||
{
|
||||
// Key used by routing and action selection to match an attribute route entry to a
|
||||
// group of action descriptors.
|
||||
/// <summary>
|
||||
/// Key used by routing and action selection to match an attribute
|
||||
/// route entry to agroup of action descriptors.
|
||||
/// </summary>
|
||||
public static readonly string RouteGroupKey = "!__route_group";
|
||||
|
||||
private readonly LinkGenerationDecisionTree _linkGenerationTree;
|
||||
|
|
|
|||
Loading…
Reference in New Issue