| File: RouteConstraintBuilder.cs | Web Access |
| Project: src\aspnetcore\src\Http\Routing\src\Microsoft.AspNetCore.Routing.csproj (Microsoft.AspNetCore.Routing) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Routing.Constraints; #if COMPONENTS using Microsoft.AspNetCore.Components.Routing; #endif namespace Microsoft.AspNetCore.Routing; #if !COMPONENTS /// <summary> /// A builder for produding a mapping of keys to see <see cref="IRouteConstraint"/>. /// </summary> /// <remarks> /// <see cref="RouteConstraintBuilder"/> allows iterative building a set of route constraints, and will /// merge multiple entries for the same key. /// </remarks> public class RouteConstraintBuilder #else internal class RouteConstraintBuilder #endif { private readonly IInlineConstraintResolver _inlineConstraintResolver; private readonly string _displayName; private readonly Dictionary<string, List<IRouteConstraint>> _constraints; private readonly HashSet<string> _optionalParameters; /// <summary> /// Creates a new instance of <see cref="RouteConstraintBuilder"/> instance. /// </summary> /// <param name="inlineConstraintResolver">The <see cref="IInlineConstraintResolver"/>.</param> /// <param name="displayName">The display name (for use in error messages).</param> public RouteConstraintBuilder( IInlineConstraintResolver inlineConstraintResolver, string displayName) { ArgumentNullException.ThrowIfNull(inlineConstraintResolver); ArgumentNullException.ThrowIfNull(displayName); _inlineConstraintResolver = inlineConstraintResolver; _displayName = displayName; _constraints = new Dictionary<string, List<IRouteConstraint>>(StringComparer.OrdinalIgnoreCase); _optionalParameters = new HashSet<string>(StringComparer.OrdinalIgnoreCase); } /// <summary> /// Builds a mapping of constraints. /// </summary> /// <returns>An <see cref="IDictionary{String, IRouteConstraint}"/> of the constraints.</returns> public IDictionary<string, IRouteConstraint> Build() { var constraints = new Dictionary<string, IRouteConstraint>(StringComparer.OrdinalIgnoreCase); foreach (var kvp in _constraints) { IRouteConstraint constraint; if (kvp.Value.Count == 1) { constraint = kvp.Value[0]; } else { constraint = new CompositeRouteConstraint(kvp.Value.ToArray()); } if (_optionalParameters.Contains(kvp.Key)) { var optionalConstraint = new OptionalRouteConstraint(constraint); constraints.Add(kvp.Key, optionalConstraint); } else { constraints.Add(kvp.Key, constraint); } } return constraints; } #if !COMPONENTS /// <summary> /// Adds a constraint instance for the given key. /// </summary> /// <param name="key">The key.</param> /// <param name="value"> /// The constraint instance. Must either be a string or an instance of <see cref="IRouteConstraint"/>. /// </param> /// <remarks> /// If the <paramref name="value"/> is a string, it will be converted to a <see cref="RegexRouteConstraint"/>. /// /// For example, the string <c>Product[0-9]+</c> will be converted to the regular expression /// <c>^(Product[0-9]+)</c>. See <see cref="System.Text.RegularExpressions.Regex"/> for more details. /// </remarks> public void AddConstraint(string key, object value) { ArgumentNullException.ThrowIfNull(key); ArgumentNullException.ThrowIfNull(value); var constraint = value as IRouteConstraint; if (constraint == null) { var regexPattern = value as string; if (regexPattern == null) { throw new RouteCreationException( Resources.FormatRouteConstraintBuilder_ValidationMustBeStringOrCustomConstraint( key, value, _displayName, typeof(IRouteConstraint))); } var constraintsRegEx = "^(" + regexPattern + ")$"; constraint = new RegexRouteConstraint(constraintsRegEx); } Add(key, constraint); } #endif /// <summary> /// Adds a constraint for the given key, resolved by the <see cref="IInlineConstraintResolver"/>. /// </summary> /// <param name="key">The key.</param> /// <param name="constraintText">The text to be resolved by <see cref="IInlineConstraintResolver"/>.</param> /// <remarks> /// The <see cref="IInlineConstraintResolver"/> can create <see cref="IRouteConstraint"/> instances /// based on <paramref name="constraintText"/>. See <see cref="RouteOptions.ConstraintMap"/> to register /// custom constraint types. /// </remarks> public void AddResolvedConstraint(string key, string constraintText) { ArgumentNullException.ThrowIfNull(key); ArgumentNullException.ThrowIfNull(constraintText); var constraint = _inlineConstraintResolver.ResolveConstraint(constraintText); if (constraint == null) { throw new InvalidOperationException( Resources.FormatRouteConstraintBuilder_CouldNotResolveConstraint( key, constraintText, _displayName, _inlineConstraintResolver.GetType().Name)); } else if (constraint == NullRouteConstraint.Instance) { // A null route constraint can be returned for other parameter policy types return; } Add(key, constraint); } /// <summary> /// Sets the given key as optional. /// </summary> /// <param name="key">The key.</param> public void SetOptional(string key) { ArgumentNullException.ThrowIfNull(key); _optionalParameters.Add(key); } private void Add(string key, IRouteConstraint constraint) { if (!_constraints.TryGetValue(key, out var list)) { list = new List<IRouteConstraint>(); _constraints.Add(key, list); } list.Add(constraint); } }