diff --git a/eng/TrimmableProjects.props b/eng/TrimmableProjects.props
index 1d9abef1fe58..c2dc91c7263d 100644
--- a/eng/TrimmableProjects.props
+++ b/eng/TrimmableProjects.props
@@ -19,6 +19,7 @@
+
diff --git a/src/Http/Http/src/Microsoft.AspNetCore.Http.csproj b/src/Http/Http/src/Microsoft.AspNetCore.Http.csproj
index a0018a4018c8..e5e1ce15a3d9 100644
--- a/src/Http/Http/src/Microsoft.AspNetCore.Http.csproj
+++ b/src/Http/Http/src/Microsoft.AspNetCore.Http.csproj
@@ -8,7 +8,6 @@
true
aspnetcore
false
- enable
true
diff --git a/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs b/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs
index d8990104cb12..d0946793248e 100644
--- a/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs
+++ b/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
@@ -18,6 +19,8 @@ namespace Microsoft.AspNetCore.Builder;
///
public static class EndpointRouteBuilderExtensions
{
+ internal const string MapEndpointTrimmerWarning = "This API may perform reflection on the supplied delegate and its parameters. These types may be trimmed if not directly referenced.";
+
// Avoid creating a new array every call
private static readonly string[] GetVerb = new[] { HttpMethods.Get };
private static readonly string[] PostVerb = new[] { HttpMethods.Post };
@@ -33,6 +36,7 @@ public static class EndpointRouteBuilderExtensions
/// The route pattern.
/// The delegate executed when the endpoint is matched.
/// A that can be used to further customize the endpoint.
+ [RequiresUnreferencedCode(EndpointRouteBuilderExtensions.MapEndpointTrimmerWarning)]
public static IEndpointConventionBuilder MapGet(
this IEndpointRouteBuilder endpoints,
string pattern,
@@ -220,6 +224,7 @@ public static IEndpointConventionBuilder Map(
/// The route pattern.
/// The delegate executed when the endpoint is matched.
/// A that can be used to further customize the endpoint.
+ [RequiresUnreferencedCode(MapEndpointTrimmerWarning)]
public static RouteHandlerBuilder MapGet(
this IEndpointRouteBuilder endpoints,
string pattern,
@@ -236,6 +241,7 @@ public static RouteHandlerBuilder MapGet(
/// The route pattern.
/// The delegate executed when the endpoint is matched.
/// A that can be used to further customize the endpoint.
+ [RequiresUnreferencedCode(MapEndpointTrimmerWarning)]
public static RouteHandlerBuilder MapPost(
this IEndpointRouteBuilder endpoints,
string pattern,
@@ -252,6 +258,7 @@ public static RouteHandlerBuilder MapPost(
/// The route pattern.
/// The delegate executed when the endpoint is matched.
/// A that can be used to further customize the endpoint.
+ [RequiresUnreferencedCode(MapEndpointTrimmerWarning)]
public static RouteHandlerBuilder MapPut(
this IEndpointRouteBuilder endpoints,
string pattern,
@@ -268,6 +275,7 @@ public static RouteHandlerBuilder MapPut(
/// The route pattern.
/// The delegate executed when the endpoint is matched.
/// A that can be used to further customize the endpoint.
+ [RequiresUnreferencedCode(MapEndpointTrimmerWarning)]
public static RouteHandlerBuilder MapDelete(
this IEndpointRouteBuilder endpoints,
string pattern,
@@ -284,6 +292,7 @@ public static RouteHandlerBuilder MapDelete(
/// The route pattern.
/// The executed when the endpoint is matched.
/// A that can be used to further customize the endpoint.
+ [RequiresUnreferencedCode(MapEndpointTrimmerWarning)]
public static RouteHandlerBuilder MapPatch(
this IEndpointRouteBuilder endpoints,
string pattern,
@@ -301,6 +310,7 @@ public static RouteHandlerBuilder MapPatch(
/// The delegate executed when the endpoint is matched.
/// HTTP methods that the endpoint will match.
/// A that can be used to further customize the endpoint.
+ [RequiresUnreferencedCode(MapEndpointTrimmerWarning)]
public static RouteHandlerBuilder MapMethods(
this IEndpointRouteBuilder endpoints,
string pattern,
@@ -348,6 +358,7 @@ static bool ShouldDisableInferredBody(string method)
/// The route pattern.
/// The delegate executed when the endpoint is matched.
/// A that can be used to further customize the endpoint.
+ [RequiresUnreferencedCode(MapEndpointTrimmerWarning)]
public static RouteHandlerBuilder Map(
this IEndpointRouteBuilder endpoints,
string pattern,
@@ -364,6 +375,7 @@ public static RouteHandlerBuilder Map(
/// The route pattern.
/// The delegate executed when the endpoint is matched.
/// A that can be used to further customize the endpoint.
+ [RequiresUnreferencedCode(MapEndpointTrimmerWarning)]
public static RouteHandlerBuilder Map(
this IEndpointRouteBuilder endpoints,
RoutePattern pattern,
@@ -391,6 +403,7 @@ public static RouteHandlerBuilder Map(
/// {*path:nonfile}. The order of the registered endpoint will be int.MaxValue.
///
///
+ [RequiresUnreferencedCode(MapEndpointTrimmerWarning)]
public static RouteHandlerBuilder MapFallback(this IEndpointRouteBuilder endpoints, Delegate handler)
{
if (endpoints == null)
@@ -427,6 +440,7 @@ public static RouteHandlerBuilder MapFallback(this IEndpointRouteBuilder endpoin
/// to exclude requests for static files.
///
///
+ [RequiresUnreferencedCode(MapEndpointTrimmerWarning)]
public static RouteHandlerBuilder MapFallback(
this IEndpointRouteBuilder endpoints,
string pattern,
@@ -453,6 +467,7 @@ public static RouteHandlerBuilder MapFallback(
return conventionBuilder;
}
+ [RequiresUnreferencedCode(MapEndpointTrimmerWarning)]
private static RouteHandlerBuilder Map(
this IEndpointRouteBuilder endpoints,
RoutePattern pattern,
@@ -515,7 +530,11 @@ private static RouteHandlerBuilder Map(
}
var routeHandlerBuilder = new RouteHandlerBuilder(dataSource.AddEndpointBuilder(builder));
- routeHandlerBuilder.Add(endpointBuilder =>
+ routeHandlerBuilder.Add(RouteHandlerBuilderConvention);
+
+ [UnconditionalSuppressMessage("Trimmer", "IL2026", Justification = "We surface a RequireUnreferencedCode in the call to enclosing Map method. " +
+ "The trimmer is unable to infer this on the nested lambda.")]
+ void RouteHandlerBuilderConvention(EndpointBuilder endpointBuilder)
{
var options = new RequestDelegateFactoryOptions
{
@@ -545,7 +564,7 @@ private static RouteHandlerBuilder Map(
}
}
endpointBuilder.RequestDelegate = filteredRequestDelegateResult.RequestDelegate;
- });
+ }
return routeHandlerBuilder;
}
diff --git a/src/Http/Routing/src/DefaultInlineConstraintResolver.cs b/src/Http/Routing/src/DefaultInlineConstraintResolver.cs
index f065ef9b1cfe..ad7ba5948ab3 100644
--- a/src/Http/Routing/src/DefaultInlineConstraintResolver.cs
+++ b/src/Http/Routing/src/DefaultInlineConstraintResolver.cs
@@ -34,7 +34,7 @@ public DefaultInlineConstraintResolver(IOptions routeOptions, ISer
throw new ArgumentNullException(nameof(serviceProvider));
}
- _inlineConstraintMap = routeOptions.Value.ConstraintMap;
+ _inlineConstraintMap = routeOptions.Value.TrimmerSafeConstraintMap;
_serviceProvider = serviceProvider;
}
diff --git a/src/Http/Routing/src/DefaultParameterPolicyFactory.cs b/src/Http/Routing/src/DefaultParameterPolicyFactory.cs
index f9c9636b35e0..965549816bbf 100644
--- a/src/Http/Routing/src/DefaultParameterPolicyFactory.cs
+++ b/src/Http/Routing/src/DefaultParameterPolicyFactory.cs
@@ -43,7 +43,7 @@ public override IParameterPolicy Create(RoutePatternParameterPart? parameter, st
}
var parameterPolicy = ParameterPolicyActivator.ResolveParameterPolicy(
- _options.ConstraintMap,
+ _options.TrimmerSafeConstraintMap,
_serviceProvider,
inlineText,
out var parameterPolicyKey);
@@ -51,9 +51,9 @@ public override IParameterPolicy Create(RoutePatternParameterPart? parameter, st
if (parameterPolicy == null)
{
throw new InvalidOperationException(Resources.FormatRoutePattern_ConstraintReferenceNotFound(
- parameterPolicyKey,
- typeof(RouteOptions),
- nameof(RouteOptions.ConstraintMap)));
+ parameterPolicyKey,
+ typeof(RouteOptions),
+ nameof(RouteOptions.ConstraintMap)));
}
if (parameterPolicy is IRouteConstraint constraint)
diff --git a/src/Http/Routing/src/EndpointRoutingMiddleware.cs b/src/Http/Routing/src/EndpointRoutingMiddleware.cs
index dadfb4530130..ff2b382fdc85 100644
--- a/src/Http/Routing/src/EndpointRoutingMiddleware.cs
+++ b/src/Http/Routing/src/EndpointRoutingMiddleware.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Matching;
@@ -96,14 +97,21 @@ private Task SetRoutingAndContinue(HttpContext httpContext)
// Raise an event if the route matched
if (_diagnosticListener.IsEnabled() && _diagnosticListener.IsEnabled(DiagnosticsEndpointMatchedKey))
{
- // We're just going to send the HttpContext since it has all of the relevant information
- _diagnosticListener.Write(DiagnosticsEndpointMatchedKey, httpContext);
+ Write(_diagnosticListener, httpContext);
}
Log.MatchSuccess(_logger, endpoint);
}
return _next(httpContext);
+
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern",
+ Justification = "The values being passed into Write are being consumed by the application already.")]
+ static void Write(DiagnosticListener diagnosticListener, HttpContext httpContext)
+ {
+ // We're just going to send the HttpContext since it has all of the relevant information
+ diagnosticListener.Write(DiagnosticsEndpointMatchedKey, httpContext);
+ }
}
// Initialization is async to avoid blocking threads while reflection and things
diff --git a/src/Http/Routing/src/LinkGeneratorEndpointNameAddressExtensions.cs b/src/Http/Routing/src/LinkGeneratorEndpointNameAddressExtensions.cs
index f493a45752d2..3ede96df1e54 100644
--- a/src/Http/Routing/src/LinkGeneratorEndpointNameAddressExtensions.cs
+++ b/src/Http/Routing/src/LinkGeneratorEndpointNameAddressExtensions.cs
@@ -27,7 +27,7 @@ public static class LinkGeneratorEndpointNameAddressExtensions
/// names from RouteOptions.
///
/// A URI with an absolute path, or null.
- [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static string? GetPathByName(
this LinkGenerator generator,
HttpContext httpContext,
@@ -66,9 +66,12 @@ public static class LinkGeneratorEndpointNameAddressExtensions
/// Generates a URI with an absolute path based on the provided values.
///
/// The .
+ /// The associated with the current request.
/// The endpoint name. Used to resolve endpoints.
/// The route values. Used to expand parameters in the route template. Optional.
- /// An optional URI path base. Prepended to the path in the resulting URI.
+ ///
+ /// An optional URI path base. Prepended to the path in the resulting URI. If not provided, the value of will be used.
+ ///
/// An optional URI fragment. Appended to the resulting URI.
///
/// An optional . Settings on provided object override the settings with matching
@@ -76,6 +79,54 @@ public static class LinkGeneratorEndpointNameAddressExtensions
///
/// A URI with an absolute path, or null.
[SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ public static string? GetPathByName(
+ this LinkGenerator generator,
+ HttpContext httpContext,
+ string endpointName,
+ RouteValueDictionary? values = default,
+ PathString? pathBase = default,
+ FragmentString fragment = default,
+ LinkOptions? options = default)
+ {
+ if (generator == null)
+ {
+ throw new ArgumentNullException(nameof(generator));
+ }
+
+ if (httpContext == null)
+ {
+ throw new ArgumentNullException(nameof(httpContext));
+ }
+
+ if (endpointName == null)
+ {
+ throw new ArgumentNullException(nameof(endpointName));
+ }
+
+ return generator.GetPathByAddress(
+ httpContext,
+ endpointName,
+ values ?? new(),
+ ambientValues: null,
+ pathBase,
+ fragment,
+ options);
+ }
+
+ ///
+ /// Generates a URI with an absolute path based on the provided values.
+ ///
+ /// The .
+ /// The endpoint name. Used to resolve endpoints.
+ /// The route values. Used to expand parameters in the route template. Optional.
+ /// An optional URI path base. Prepended to the path in the resulting URI.
+ /// An optional URI fragment. Appended to the resulting URI.
+ ///
+ /// An optional . Settings on provided object override the settings with matching
+ /// names from RouteOptions.
+ ///
+ /// A URI with an absolute path, or null.
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static string? GetPathByName(
this LinkGenerator generator,
string endpointName,
@@ -97,6 +148,41 @@ public static class LinkGeneratorEndpointNameAddressExtensions
return generator.GetPathByAddress(endpointName, new RouteValueDictionary(values), pathBase, fragment, options);
}
+ ///
+ /// Generates a URI with an absolute path based on the provided values.
+ ///
+ /// The .
+ /// The endpoint name. Used to resolve endpoints.
+ /// The route values. Used to expand parameters in the route template. Optional.
+ /// An optional URI path base. Prepended to the path in the resulting URI.
+ /// An optional URI fragment. Appended to the resulting URI.
+ ///
+ /// An optional . Settings on provided object override the settings with matching
+ /// names from RouteOptions.
+ ///
+ /// A URI with an absolute path, or null.
+ [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ public static string? GetPathByName(
+ this LinkGenerator generator,
+ string endpointName,
+ RouteValueDictionary? values = default,
+ PathString pathBase = default,
+ FragmentString fragment = default,
+ LinkOptions? options = default)
+ {
+ if (generator == null)
+ {
+ throw new ArgumentNullException(nameof(generator));
+ }
+
+ if (endpointName == null)
+ {
+ throw new ArgumentNullException(nameof(endpointName));
+ }
+
+ return generator.GetPathByAddress(endpointName, values ?? new(), pathBase, fragment, options);
+ }
+
///
/// Generates an absolute URI based on the provided values.
///
@@ -128,7 +214,7 @@ public static class LinkGeneratorEndpointNameAddressExtensions
/// your deployment environment.
///
///
- [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static string? GetUriByName(
this LinkGenerator generator,
HttpContext httpContext,
@@ -167,6 +253,76 @@ public static class LinkGeneratorEndpointNameAddressExtensions
options);
}
+ ///
+ /// Generates an absolute URI based on the provided values.
+ ///
+ /// The .
+ /// The associated with the current request.
+ /// The endpoint name. Used to resolve endpoints.
+ /// The route values. Used to expand parameters in the route template. Optional.
+ ///
+ /// The URI scheme, applied to the resulting URI. Optional. If not provided, the value of will be used.
+ ///
+ ///
+ /// The URI host/authority, applied to the resulting URI. Optional. If not provided, the value will be used.
+ /// See the remarks section for details about the security implications of the .
+ ///
+ ///
+ /// An optional URI path base. Prepended to the path in the resulting URI. If not provided, the value of will be used.
+ ///
+ /// An optional URI fragment. Appended to the resulting URI.
+ ///
+ /// An optional . Settings on provided object override the settings with matching
+ /// names from RouteOptions.
+ ///
+ /// A URI with an absolute path, or null.
+ ///
+ ///
+ /// The value of should be a trusted value. Relying on the value of the current request
+ /// can allow untrusted input to influence the resulting URI unless the Host header has been validated.
+ /// See the deployment documentation for instructions on how to properly validate the Host header in
+ /// your deployment environment.
+ ///
+ ///
+ [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ public static string? GetUriByName(
+ this LinkGenerator generator,
+ HttpContext httpContext,
+ string endpointName,
+ RouteValueDictionary? values = default,
+ string? scheme = default,
+ HostString? host = default,
+ PathString? pathBase = default,
+ FragmentString fragment = default,
+ LinkOptions? options = default)
+ {
+ if (generator == null)
+ {
+ throw new ArgumentNullException(nameof(generator));
+ }
+
+ if (httpContext == null)
+ {
+ throw new ArgumentNullException(nameof(httpContext));
+ }
+
+ if (endpointName == null)
+ {
+ throw new ArgumentNullException(nameof(endpointName));
+ }
+
+ return generator.GetUriByAddress(
+ httpContext,
+ endpointName,
+ values ?? new(),
+ ambientValues: null,
+ scheme,
+ host,
+ pathBase,
+ fragment,
+ options);
+ }
+
///
/// Generates an absolute URI based on the provided values.
///
@@ -193,7 +349,7 @@ public static class LinkGeneratorEndpointNameAddressExtensions
/// your deployment environment.
///
///
- [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static string? GetUriByName(
this LinkGenerator generator,
string endpointName,
@@ -226,4 +382,64 @@ public static class LinkGeneratorEndpointNameAddressExtensions
return generator.GetUriByAddress(endpointName, new RouteValueDictionary(values), scheme, host, pathBase, fragment, options);
}
+
+ ///
+ /// Generates an absolute URI based on the provided values.
+ ///
+ /// The .
+ /// The endpoint name. Used to resolve endpoints.
+ /// The route values. Used to expand parameters in the route template.
+ /// The URI scheme, applied to the resulting URI.
+ ///
+ /// The URI host/authority, applied to the resulting URI.
+ /// See the remarks section for details about the security implications of the .
+ ///
+ /// An optional URI path base. Prepended to the path in the resulting URI.
+ /// An optional URI fragment. Appended to the resulting URI.
+ ///
+ /// An optional . Settings on provided object override the settings with matching
+ /// names from RouteOptions.
+ ///
+ /// An absolute URI, or null.
+ ///
+ ///
+ /// The value of should be a trusted value. Relying on the value of the current request
+ /// can allow untrusted input to influence the resulting URI unless the Host header has been validated.
+ /// See the deployment documentation for instructions on how to properly validate the Host header in
+ /// your deployment environment.
+ ///
+ ///
+ [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ public static string? GetUriByName(
+ this LinkGenerator generator,
+ string endpointName,
+ RouteValueDictionary values,
+ string scheme,
+ HostString host,
+ PathString pathBase = default,
+ FragmentString fragment = default,
+ LinkOptions? options = default)
+ {
+ if (generator == null)
+ {
+ throw new ArgumentNullException(nameof(generator));
+ }
+
+ if (endpointName == null)
+ {
+ throw new ArgumentNullException(nameof(endpointName));
+ }
+
+ if (string.IsNullOrEmpty(scheme))
+ {
+ throw new ArgumentException("A scheme must be provided.", nameof(scheme));
+ }
+
+ if (!host.HasValue)
+ {
+ throw new ArgumentException("A host must be provided.", nameof(host));
+ }
+
+ return generator.GetUriByAddress(endpointName, values, scheme, host, pathBase, fragment, options);
+ }
}
diff --git a/src/Http/Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs b/src/Http/Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs
index fe4be092481e..c83ab68d1e4e 100644
--- a/src/Http/Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs
+++ b/src/Http/Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs
@@ -27,7 +27,7 @@ public static class LinkGeneratorRouteValuesAddressExtensions
/// names from RouteOptions.
///
/// A URI with an absolute path, or null.
- [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static string? GetPathByRouteValues(
this LinkGenerator generator,
HttpContext httpContext,
@@ -47,6 +47,53 @@ public static class LinkGeneratorRouteValuesAddressExtensions
throw new ArgumentNullException(nameof(httpContext));
}
+ var address = CreateAddress(httpContext, routeName, new(values));
+ return generator.GetPathByAddress(
+ httpContext,
+ address,
+ address.ExplicitValues,
+ address.AmbientValues,
+ pathBase,
+ fragment,
+ options);
+ }
+
+ ///
+ /// Generates a URI with an absolute path based on the provided values.
+ ///
+ /// The .
+ /// The associated with the current request.
+ /// The route name. Used to resolve endpoints. Optional.
+ /// The route values. Used to resolve endpoints and expand parameters in the route template. Optional.
+ ///
+ /// An optional URI path base. Prepended to the path in the resulting URI. If not provided, the value of will be used.
+ ///
+ /// An optional URI fragment. Appended to the resulting URI.
+ ///
+ /// An optional . Settings on provided object override the settings with matching
+ /// names from RouteOptions.
+ ///
+ /// A URI with an absolute path, or null.
+ [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ public static string? GetPathByRouteValues(
+ this LinkGenerator generator,
+ HttpContext httpContext,
+ string? routeName,
+ RouteValueDictionary? values = default,
+ PathString? pathBase = default,
+ FragmentString fragment = default,
+ LinkOptions? options = default)
+ {
+ if (generator == null)
+ {
+ throw new ArgumentNullException(nameof(generator));
+ }
+
+ if (httpContext == null)
+ {
+ throw new ArgumentNullException(nameof(httpContext));
+ }
+
var address = CreateAddress(httpContext, routeName, values);
return generator.GetPathByAddress(
httpContext,
@@ -71,7 +118,7 @@ public static class LinkGeneratorRouteValuesAddressExtensions
/// names from RouteOptions.
///
/// A URI with an absolute path, or null.
- [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static string? GetPathByRouteValues(
this LinkGenerator generator,
string? routeName,
@@ -85,6 +132,37 @@ public static class LinkGeneratorRouteValuesAddressExtensions
throw new ArgumentNullException(nameof(generator));
}
+ var address = CreateAddress(httpContext: null, routeName, new(values));
+ return generator.GetPathByAddress(address, address.ExplicitValues, pathBase, fragment, options);
+ }
+
+ ///
+ /// Generates a URI with an absolute path based on the provided values.
+ ///
+ /// The .
+ /// The route name. Used to resolve endpoints. Optional.
+ /// The route values. Used to resolve endpoints and expand parameters in the route template. Optional.
+ /// An optional URI path base. Prepended to the path in the resulting URI.
+ /// An optional URI fragment. Appended to the resulting URI.
+ ///
+ /// An optional . Settings on provided object override the settings with matching
+ /// names from RouteOptions.
+ ///
+ /// A URI with an absolute path, or null.
+ [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ public static string? GetPathByRouteValues(
+ this LinkGenerator generator,
+ string? routeName,
+ RouteValueDictionary? values = default,
+ PathString pathBase = default,
+ FragmentString fragment = default,
+ LinkOptions? options = default)
+ {
+ if (generator == null)
+ {
+ throw new ArgumentNullException(nameof(generator));
+ }
+
var address = CreateAddress(httpContext: null, routeName, values);
return generator.GetPathByAddress(address, address.ExplicitValues, pathBase, fragment, options);
}
@@ -120,7 +198,7 @@ public static class LinkGeneratorRouteValuesAddressExtensions
/// your deployment environment.
///
///
- [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static string? GetUriByRouteValues(
this LinkGenerator generator,
HttpContext httpContext,
@@ -142,6 +220,72 @@ public static class LinkGeneratorRouteValuesAddressExtensions
throw new ArgumentNullException(nameof(httpContext));
}
+ var address = CreateAddress(httpContext, routeName, new(values));
+ return generator.GetUriByAddress(
+ httpContext,
+ address,
+ address.ExplicitValues,
+ address.AmbientValues,
+ scheme,
+ host,
+ pathBase,
+ fragment,
+ options);
+ }
+
+ ///
+ /// Generates an absolute URI based on the provided values.
+ ///
+ /// The .
+ /// The associated with the current request.
+ /// The route name. Used to resolve endpoints. Optional.
+ /// The route values. Used to resolve endpoints and expand parameters in the route template. Optional.
+ ///
+ /// The URI scheme, applied to the resulting URI. Optional. If not provided, the value of will be used.
+ ///
+ ///
+ /// The URI host/authority, applied to the resulting URI. Optional. If not provided, the value will be used.
+ /// See the remarks section for details about the security implications of the .
+ ///
+ ///
+ /// An optional URI path base. Prepended to the path in the resulting URI. If not provided, the value of will be used.
+ ///
+ /// An optional URI fragment. Appended to the resulting URI.
+ ///
+ /// An optional . Settings on provided object override the settings with matching
+ /// names from RouteOptions.
+ ///
+ /// A URI with an absolute path, or null.
+ ///
+ ///
+ /// The value of should be a trusted value. Relying on the value of the current request
+ /// can allow untrusted input to influence the resulting URI unless the Host header has been validated.
+ /// See the deployment documentation for instructions on how to properly validate the Host header in
+ /// your deployment environment.
+ ///
+ ///
+ [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ public static string? GetUriByRouteValues(
+ this LinkGenerator generator,
+ HttpContext httpContext,
+ string? routeName,
+ RouteValueDictionary? values = default,
+ string? scheme = default,
+ HostString? host = default,
+ PathString? pathBase = default,
+ FragmentString fragment = default,
+ LinkOptions? options = default)
+ {
+ if (generator == null)
+ {
+ throw new ArgumentNullException(nameof(generator));
+ }
+
+ if (httpContext == null)
+ {
+ throw new ArgumentNullException(nameof(httpContext));
+ }
+
var address = CreateAddress(httpContext, routeName, values);
return generator.GetUriByAddress(
httpContext,
@@ -181,7 +325,7 @@ public static class LinkGeneratorRouteValuesAddressExtensions
/// your deployment environment.
///
///
- [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static string? GetUriByRouteValues(
this LinkGenerator generator,
string? routeName,
@@ -197,16 +341,62 @@ public static class LinkGeneratorRouteValuesAddressExtensions
throw new ArgumentNullException(nameof(generator));
}
+ var address = CreateAddress(httpContext: null, routeName, new(values));
+ return generator.GetUriByAddress(address, address.ExplicitValues, scheme, host, pathBase, fragment, options);
+ }
+
+ ///
+ /// Generates an absolute URI based on the provided values.
+ ///
+ /// The .
+ /// The route name. Used to resolve endpoints. Optional.
+ /// The route values. Used to resolve endpoints and expand parameters in the route template.
+ /// The URI scheme, applied to the resulting URI.
+ ///
+ /// The URI host/authority, applied to the resulting URI.
+ /// See the remarks section for details about the security implications of the .
+ ///
+ /// An optional URI path base. Prepended to the path in the resulting URI.
+ /// An optional URI fragment. Appended to the resulting URI.
+ ///
+ /// An optional . Settings on provided object override the settings with matching
+ /// names from RouteOptions.
+ ///
+ /// An absolute URI, or null.
+ ///
+ ///
+ /// The value of should be a trusted value. Relying on the value of the current request
+ /// can allow untrusted input to influence the resulting URI unless the Host header has been validated.
+ /// See the deployment documentation for instructions on how to properly validate the Host header in
+ /// your deployment environment.
+ ///
+ ///
+ [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")]
+ public static string? GetUriByRouteValues(
+ this LinkGenerator generator,
+ string? routeName,
+ RouteValueDictionary values,
+ string scheme,
+ HostString host,
+ PathString pathBase = default,
+ FragmentString fragment = default,
+ LinkOptions? options = default)
+ {
+ if (generator == null)
+ {
+ throw new ArgumentNullException(nameof(generator));
+ }
+
var address = CreateAddress(httpContext: null, routeName, values);
return generator.GetUriByAddress(address, address.ExplicitValues, scheme, host, pathBase, fragment, options);
}
- private static RouteValuesAddress CreateAddress(HttpContext? httpContext, string? routeName, object? values)
+ private static RouteValuesAddress CreateAddress(HttpContext? httpContext, string? routeName, RouteValueDictionary? values)
{
return new RouteValuesAddress()
{
AmbientValues = DefaultLinkGenerator.GetAmbientValues(httpContext),
- ExplicitValues = new RouteValueDictionary(values),
+ ExplicitValues = values ?? new(),
RouteName = routeName,
};
}
diff --git a/src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs b/src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs
index e5c0b1892b8b..3e608cc6aa09 100644
--- a/src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs
+++ b/src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs
@@ -3,6 +3,7 @@
#nullable enable
+using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Constraints;
using Microsoft.Extensions.DependencyInjection;
@@ -21,6 +22,7 @@ public static class MapRouteRouteBuilderExtensions
/// The name of the route.
/// The URL pattern of the route.
/// A reference to this instance after the operation has completed.
+ [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")]
public static IRouteBuilder MapRoute(
this IRouteBuilder routeBuilder,
string? name,
@@ -41,6 +43,7 @@ public static IRouteBuilder MapRoute(
/// and values of the default values.
///
/// A reference to this instance after the operation has completed.
+ [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")]
public static IRouteBuilder MapRoute(
this IRouteBuilder routeBuilder,
string? name,
@@ -66,6 +69,7 @@ public static IRouteBuilder MapRoute(
/// of the constraints.
///
/// A reference to this instance after the operation has completed.
+ [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")]
public static IRouteBuilder MapRoute(
this IRouteBuilder routeBuilder,
string? name,
@@ -96,6 +100,7 @@ public static IRouteBuilder MapRoute(
/// of the data tokens.
///
/// A reference to this instance after the operation has completed.
+ [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")]
public static IRouteBuilder MapRoute(
this IRouteBuilder routeBuilder,
string? name,
diff --git a/src/Http/Routing/src/Matching/ILEmitTrieFactory.cs b/src/Http/Routing/src/Matching/ILEmitTrieFactory.cs
index 325b8c216ad8..90c3bbd93f33 100644
--- a/src/Http/Routing/src/Matching/ILEmitTrieFactory.cs
+++ b/src/Http/Routing/src/Matching/ILEmitTrieFactory.cs
@@ -477,7 +477,7 @@ private class Labels
public Label ReturnNotAscii { get; set; }
}
- private class Methods
+ private sealed class Methods
{
// Caching because the methods won't change, if we're being called once we're likely to
// be called again.
@@ -485,28 +485,30 @@ private class Methods
private Methods()
{
- // Can't use GetMethod because the parameter is a generic method parameters.
- Add = typeof(Unsafe)
- .GetMethods(BindingFlags.Public | BindingFlags.Static)
- .Where(m => m.Name == nameof(Unsafe.Add))
- .Where(m => m.GetGenericArguments().Length == 1)
- .Where(m => m.GetParameters().Length == 2)
- .FirstOrDefault()
+ Add = typeof(Unsafe).GetMethod(
+ nameof(Unsafe.Add),
+ genericParameterCount: 1,
+ BindingFlags.Public | BindingFlags.Static,
+ binder: null,
+ types: new[] { Type.MakeGenericMethodParameter(0).MakeByRefType(), typeof(int), },
+ modifiers: null)
?.MakeGenericMethod(typeof(byte));
- if (Add == null)
+
+ if (Add is null)
{
throw new InvalidOperationException("Failed to find Unsafe.Add{T}(ref T, int)");
}
- // Can't use GetMethod because the parameter is a generic method parameters.
- As = typeof(Unsafe)
- .GetMethods(BindingFlags.Public | BindingFlags.Static)
- .Where(m => m.Name == nameof(Unsafe.As))
- .Where(m => m.GetGenericArguments().Length == 2)
- .Where(m => m.GetParameters().Length == 1)
- .FirstOrDefault()
- ?.MakeGenericMethod(typeof(char), typeof(byte));
- if (Add == null)
+ As = typeof(Unsafe).GetMethod(
+ nameof(Unsafe.As),
+ genericParameterCount: 2,
+ BindingFlags.Public | BindingFlags.Static,
+ binder: null,
+ types: new[] { Type.MakeGenericMethodParameter(0).MakeByRefType(), },
+ modifiers: null)
+ ?.MakeGenericMethod(typeof(char), typeof(byte));
+
+ if (As is null)
{
throw new InvalidOperationException("Failed to find Unsafe.As{TFrom, TTo}(ref TFrom)");
}
@@ -522,16 +524,15 @@ private Methods()
throw new InvalidOperationException("Failed to find MemoryExtensions.AsSpan(string, int, int)");
}
- // Can't use GetMethod because the parameter is a generic method parameters.
- GetReference = typeof(MemoryMarshal)
- .GetMethods(BindingFlags.Public | BindingFlags.Static)
- .Where(m => m.Name == nameof(MemoryMarshal.GetReference))
- .Where(m => m.GetGenericArguments().Length == 1)
- .Where(m => m.GetParameters().Length == 1)
- // Disambiguate between ReadOnlySpan<> and Span<> - this method is overloaded.
- .Where(m => m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(ReadOnlySpan<>))
- .FirstOrDefault()
+ GetReference = typeof(MemoryMarshal).GetMethod(
+ nameof(MemoryMarshal.GetReference),
+ genericParameterCount: 1,
+ BindingFlags.Public | BindingFlags.Static,
+ binder: null,
+ types: new[] { typeof(ReadOnlySpan<>).MakeGenericType(Type.MakeGenericMethodParameter(0)), },
+ modifiers: null)
?.MakeGenericMethod(typeof(char));
+
if (GetReference == null)
{
throw new InvalidOperationException("Failed to find MemoryMarshal.GetReference{T}(ReadOnlySpan{T})");
diff --git a/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj b/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj
index c11ec20e5a8f..0e1f47d2ab67 100644
--- a/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj
+++ b/src/Http/Routing/src/Microsoft.AspNetCore.Routing.csproj
@@ -13,7 +13,7 @@
true
aspnetcore;routing
false
- enable
+ true
diff --git a/src/Http/Routing/src/ParameterPolicyActivator.cs b/src/Http/Routing/src/ParameterPolicyActivator.cs
index c843c34634b0..7ce467f9a059 100644
--- a/src/Http/Routing/src/ParameterPolicyActivator.cs
+++ b/src/Http/Routing/src/ParameterPolicyActivator.cs
@@ -86,6 +86,7 @@ public static T ResolveParameterPolicy(
}
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2006:UnrecognizedReflectionPattern", Justification = "This type comes from the ConstraintMap.")]
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070", Justification = "We ensure the constructor is preserved when the constraint map is added.")]
private static IParameterPolicy CreateParameterPolicy(IServiceProvider serviceProvider, Type parameterPolicyType, string argumentString)
{
ConstructorInfo activationConstructor = null;
diff --git a/src/Http/Routing/src/Patterns/DefaultRoutePatternTransformer.cs b/src/Http/Routing/src/Patterns/DefaultRoutePatternTransformer.cs
index de56b5e06c2c..84e425b55a96 100644
--- a/src/Http/Routing/src/Patterns/DefaultRoutePatternTransformer.cs
+++ b/src/Http/Routing/src/Patterns/DefaultRoutePatternTransformer.cs
@@ -3,6 +3,8 @@
#nullable disable
+using System.Diagnostics.CodeAnalysis;
+
namespace Microsoft.AspNetCore.Routing.Patterns;
internal class DefaultRoutePatternTransformer : RoutePatternTransformer
@@ -19,6 +21,8 @@ public DefaultRoutePatternTransformer(ParameterPolicyFactory policyFactory)
_policyFactory = policyFactory;
}
+ [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly." +
+ "Consider using a different overload to avoid this issue.")]
public override RoutePattern SubstituteRequiredValues(RoutePattern original, object requiredValues)
{
if (original == null)
@@ -26,11 +30,16 @@ public override RoutePattern SubstituteRequiredValues(RoutePattern original, obj
throw new ArgumentNullException(nameof(original));
}
- return SubstituteRequiredValuesCore(original, new RouteValueDictionary(requiredValues));
+ return SubstituteRequiredValues(original, new RouteValueDictionary(requiredValues));
}
- private RoutePattern SubstituteRequiredValuesCore(RoutePattern original, RouteValueDictionary requiredValues)
+ public override RoutePattern SubstituteRequiredValues(RoutePattern original, RouteValueDictionary requiredValues)
{
+ if (original is null)
+ {
+ throw new ArgumentNullException(nameof(original));
+ }
+
// Process each required value in sequence. Bail if we find any rejection criteria. The goal
// of rejection is to avoid creating RoutePattern instances that can't *ever* match.
//
diff --git a/src/Http/Routing/src/Patterns/RoutePatternFactory.cs b/src/Http/Routing/src/Patterns/RoutePatternFactory.cs
index da7e1e8740fd..22123f60ac4f 100644
--- a/src/Http/Routing/src/Patterns/RoutePatternFactory.cs
+++ b/src/Http/Routing/src/Patterns/RoutePatternFactory.cs
@@ -3,6 +3,7 @@
using System.Collections;
using System.Collections.ObjectModel;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.AspNetCore.Routing.Constraints;
@@ -53,6 +54,7 @@ public static RoutePattern Parse(string pattern)
/// Multiple policies can be specified for a key by providing a collection as the value.
///
/// The .
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static RoutePattern Parse(string pattern, object? defaults, object? parameterPolicies)
{
if (pattern == null)
@@ -64,6 +66,34 @@ public static RoutePattern Parse(string pattern, object? defaults, object? param
return PatternCore(original.RawText, Wrap(defaults), Wrap(parameterPolicies), requiredValues: null, original.PathSegments);
}
+ ///
+ /// Creates a from its string representation along
+ /// with provided default values and parameter policies.
+ ///
+ /// The route pattern string to parse.
+ ///
+ /// Additional default values to associated with the route pattern. May be null.
+ /// The provided object will be converted to key-value pairs using
+ /// and then merged into the parsed route pattern.
+ ///
+ ///
+ /// Additional parameter policies to associated with the route pattern. May be null.
+ /// The provided object will be converted to key-value pairs using
+ /// and then merged into the parsed route pattern.
+ /// Multiple policies can be specified for a key by providing a collection as the value.
+ ///
+ /// The .
+ public static RoutePattern Parse(string pattern, RouteValueDictionary? defaults, RouteValueDictionary? parameterPolicies)
+ {
+ if (pattern == null)
+ {
+ throw new ArgumentNullException(nameof(pattern));
+ }
+
+ var original = RoutePatternParser.Parse(pattern);
+ return PatternCore(original.RawText, defaults, parameterPolicies, requiredValues: null, original.PathSegments);
+ }
+
///
/// Creates a from its string representation along
/// with provided default values and parameter policies.
@@ -84,6 +114,7 @@ public static RoutePattern Parse(string pattern, object? defaults, object? param
/// Route values that can be substituted for parameters in the route pattern. See remarks on .
///
/// The .
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static RoutePattern Parse(string pattern, object? defaults, object? parameterPolicies, object? requiredValues)
{
if (pattern == null)
@@ -95,6 +126,37 @@ public static RoutePattern Parse(string pattern, object? defaults, object? param
return PatternCore(original.RawText, Wrap(defaults), Wrap(parameterPolicies), Wrap(requiredValues), original.PathSegments);
}
+ ///
+ /// Creates a from its string representation along
+ /// with provided default values and parameter policies.
+ ///
+ /// The route pattern string to parse.
+ ///
+ /// Additional default values to associated with the route pattern. May be null.
+ /// The provided object will be converted to key-value pairs using
+ /// and then merged into the parsed route pattern.
+ ///
+ ///
+ /// Additional parameter policies to associated with the route pattern. May be null.
+ /// The provided object will be converted to key-value pairs using
+ /// and then merged into the parsed route pattern.
+ /// Multiple policies can be specified for a key by providing a collection as the value.
+ ///
+ ///
+ /// Route values that can be substituted for parameters in the route pattern. See remarks on .
+ ///
+ /// The .
+ public static RoutePattern Parse(string pattern, RouteValueDictionary? defaults, RouteValueDictionary? parameterPolicies, RouteValueDictionary? requiredValues)
+ {
+ if (pattern is null)
+ {
+ throw new ArgumentNullException(nameof(pattern));
+ }
+
+ var original = RoutePatternParser.Parse(pattern);
+ return PatternCore(original.RawText, defaults, parameterPolicies, requiredValues, original.PathSegments);
+ }
+
///
/// Creates a new instance of from a collection of segments.
///
@@ -143,6 +205,7 @@ public static RoutePattern Pattern(string? rawText, IEnumerable
/// The collection of segments.
/// The .
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static RoutePattern Pattern(
object? defaults,
object? parameterPolicies,
@@ -156,6 +219,36 @@ public static RoutePattern Pattern(
return PatternCore(null, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), requiredValues: null, segments);
}
+ ///
+ /// Creates a from a collection of segments along
+ /// with provided default values and parameter policies.
+ ///
+ ///
+ /// Additional default values to associated with the route pattern. May be null.
+ /// The provided object will be converted to key-value pairs using
+ /// and then merged into the route pattern.
+ ///
+ ///
+ /// Additional parameter policies to associated with the route pattern. May be null.
+ /// The provided object will be converted to key-value pairs using
+ /// and then merged into the route pattern.
+ /// Multiple policies can be specified for a key by providing a collection as the value.
+ ///
+ /// The collection of segments.
+ /// The .
+ public static RoutePattern Pattern(
+ RouteValueDictionary? defaults,
+ RouteValueDictionary? parameterPolicies,
+ IEnumerable segments)
+ {
+ if (segments is null)
+ {
+ throw new ArgumentNullException(nameof(segments));
+ }
+
+ return PatternCore(null, defaults, parameterPolicies, requiredValues: null, segments);
+ }
+
///
/// Creates a from a collection of segments along
/// with provided default values and parameter policies.
@@ -174,6 +267,7 @@ public static RoutePattern Pattern(
///
/// The collection of segments.
/// The .
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static RoutePattern Pattern(
string? rawText,
object? defaults,
@@ -188,6 +282,38 @@ public static RoutePattern Pattern(
return PatternCore(rawText, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), requiredValues: null, segments);
}
+ ///
+ /// Creates a from a collection of segments along
+ /// with provided default values and parameter policies.
+ ///
+ /// The raw text to associate with the route pattern. May be null.
+ ///
+ /// Additional default values to associated with the route pattern. May be null.
+ /// The provided object will be converted to key-value pairs using
+ /// and then merged into the route pattern.
+ ///
+ ///
+ /// Additional parameter policies to associated with the route pattern. May be null.
+ /// The provided object will be converted to key-value pairs using
+ /// and then merged into the route pattern.
+ /// Multiple policies can be specified for a key by providing a collection as the value.
+ ///
+ /// The collection of segments.
+ /// The .
+ public static RoutePattern Pattern(
+ string? rawText,
+ RouteValueDictionary? defaults,
+ RouteValueDictionary? parameterPolicies,
+ IEnumerable segments)
+ {
+ if (segments == null)
+ {
+ throw new ArgumentNullException(nameof(segments));
+ }
+
+ return PatternCore(rawText, defaults, parameterPolicies, requiredValues: null, segments);
+ }
+
///
/// Creates a new instance of from a collection of segments.
///
@@ -236,6 +362,7 @@ public static RoutePattern Pattern(string rawText, params RoutePatternPathSegmen
///
/// The collection of segments.
/// The .
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static RoutePattern Pattern(
object? defaults,
object? parameterPolicies,
@@ -249,6 +376,36 @@ public static RoutePattern Pattern(
return PatternCore(null, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), requiredValues: null, segments);
}
+ ///
+ /// Creates a from a collection of segments along
+ /// with provided default values and parameter policies.
+ ///
+ ///
+ /// Additional default values to associated with the route pattern. May be null.
+ /// The provided object will be converted to key-value pairs using
+ /// and then merged into the route pattern.
+ ///
+ ///
+ /// Additional parameter policies to associated with the route pattern. May be null.
+ /// The provided object will be converted to key-value pairs using
+ /// and then merged into the route pattern.
+ /// Multiple policies can be specified for a key by providing a collection as the value.
+ ///
+ /// The collection of segments.
+ /// The .
+ public static RoutePattern Pattern(
+ RouteValueDictionary? defaults,
+ RouteValueDictionary? parameterPolicies,
+ params RoutePatternPathSegment[] segments)
+ {
+ if (segments == null)
+ {
+ throw new ArgumentNullException(nameof(segments));
+ }
+
+ return PatternCore(null, defaults, parameterPolicies, requiredValues: null, segments);
+ }
+
///
/// Creates a from a collection of segments along
/// with provided default values and parameter policies.
@@ -267,6 +424,7 @@ public static RoutePattern Pattern(
///
/// The collection of segments.
/// The .
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
public static RoutePattern Pattern(
string? rawText,
object? defaults,
@@ -281,6 +439,38 @@ public static RoutePattern Pattern(
return PatternCore(rawText, new RouteValueDictionary(defaults), new RouteValueDictionary(parameterPolicies), requiredValues: null, segments);
}
+ ///
+ /// Creates a from a collection of segments along
+ /// with provided default values and parameter policies.
+ ///
+ /// The raw text to associate with the route pattern.
+ ///
+ /// Additional default values to associated with the route pattern. May be null.
+ /// The provided object will be converted to key-value pairs using
+ /// and then merged into the route pattern.
+ ///
+ ///
+ /// Additional parameter policies to associated with the route pattern. May be null.
+ /// The provided object will be converted to key-value pairs using
+ /// and then merged into the route pattern.
+ /// Multiple policies can be specified for a key by providing a collection as the value.
+ ///
+ /// The collection of segments.
+ /// The .
+ public static RoutePattern Pattern(
+ string? rawText,
+ RouteValueDictionary? defaults,
+ RouteValueDictionary? parameterPolicies,
+ params RoutePatternPathSegment[] segments)
+ {
+ if (segments == null)
+ {
+ throw new ArgumentNullException(nameof(segments));
+ }
+
+ return PatternCore(rawText, defaults, parameterPolicies, requiredValues: null, segments);
+ }
+
private static RoutePattern PatternCore(
string? rawText,
RouteValueDictionary? defaults,
@@ -904,8 +1094,9 @@ private static RoutePatternParameterPolicyReference ParameterPolicyCore(IParamet
return new RoutePatternParameterPolicyReference(parameterPolicy);
}
+ [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
private static RouteValueDictionary? Wrap(object? values)
{
- return values == null ? null : new RouteValueDictionary(values);
+ return values is null ? null : new RouteValueDictionary(values);
}
}
diff --git a/src/Http/Routing/src/Patterns/RoutePatternTransformer.cs b/src/Http/Routing/src/Patterns/RoutePatternTransformer.cs
index bd642fa6c0d3..943b19c36a3e 100644
--- a/src/Http/Routing/src/Patterns/RoutePatternTransformer.cs
+++ b/src/Http/Routing/src/Patterns/RoutePatternTransformer.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Diagnostics.CodeAnalysis;
+
namespace Microsoft.AspNetCore.Routing.Patterns;
///
@@ -30,5 +32,32 @@ public abstract class RoutePatternTransformer
/// return null if any required value cannot be substituted.
///
///
+ [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly." +
+ "Consider using a different overload to avoid this issue.")]
public abstract RoutePattern? SubstituteRequiredValues(RoutePattern original, object requiredValues);
+
+ ///
+ /// Attempts to substitute the provided into the provided
+ /// .
+ ///
+ /// The original .
+ /// The required values to substitute.
+ ///
+ /// A new if substitution succeeds, otherwise null.
+ ///
+ ///
+ ///
+ /// Substituting required values into a route pattern is intended for us with a general-purpose
+ /// parameterize route specification that can match many logical endpoints. Calling
+ /// can produce a derived route pattern
+ /// for each set of route values that corresponds to an endpoint.
+ ///
+ ///
+ /// The substitution process considers default values and implementations
+ /// when examining a required value. will
+ /// return null if any required value cannot be substituted.
+ ///
+ ///
+ public virtual RoutePattern? SubstituteRequiredValues(RoutePattern original, RouteValueDictionary requiredValues)
+ => throw new NotSupportedException("This API is not supported.");
}
diff --git a/src/Http/Routing/src/PublicAPI.Unshipped.txt b/src/Http/Routing/src/PublicAPI.Unshipped.txt
index 432fe7abd574..3d9ca9b5f48f 100644
--- a/src/Http/Routing/src/PublicAPI.Unshipped.txt
+++ b/src/Http/Routing/src/PublicAPI.Unshipped.txt
@@ -13,3 +13,18 @@ static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddFilter(this Mic
static Microsoft.AspNetCore.Http.RouteHandlerFilterExtensions.AddFilter(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder!
static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.WithDescription(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, string! description) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder!
static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.WithSummary(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, string! summary) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder!
+static Microsoft.AspNetCore.Routing.LinkGeneratorEndpointNameAddressExtensions.GetPathByName(this Microsoft.AspNetCore.Routing.LinkGenerator! generator, Microsoft.AspNetCore.Http.HttpContext! httpContext, string! endpointName, Microsoft.AspNetCore.Routing.RouteValueDictionary? values = null, Microsoft.AspNetCore.Http.PathString? pathBase = null, Microsoft.AspNetCore.Http.FragmentString fragment = default(Microsoft.AspNetCore.Http.FragmentString), Microsoft.AspNetCore.Routing.LinkOptions? options = null) -> string?
+static Microsoft.AspNetCore.Routing.LinkGeneratorEndpointNameAddressExtensions.GetPathByName(this Microsoft.AspNetCore.Routing.LinkGenerator! generator, string! endpointName, Microsoft.AspNetCore.Routing.RouteValueDictionary? values = null, Microsoft.AspNetCore.Http.PathString pathBase = default(Microsoft.AspNetCore.Http.PathString), Microsoft.AspNetCore.Http.FragmentString fragment = default(Microsoft.AspNetCore.Http.FragmentString), Microsoft.AspNetCore.Routing.LinkOptions? options = null) -> string?
+static Microsoft.AspNetCore.Routing.LinkGeneratorEndpointNameAddressExtensions.GetUriByName(this Microsoft.AspNetCore.Routing.LinkGenerator! generator, Microsoft.AspNetCore.Http.HttpContext! httpContext, string! endpointName, Microsoft.AspNetCore.Routing.RouteValueDictionary? values = null, string? scheme = null, Microsoft.AspNetCore.Http.HostString? host = null, Microsoft.AspNetCore.Http.PathString? pathBase = null, Microsoft.AspNetCore.Http.FragmentString fragment = default(Microsoft.AspNetCore.Http.FragmentString), Microsoft.AspNetCore.Routing.LinkOptions? options = null) -> string?
+static Microsoft.AspNetCore.Routing.LinkGeneratorEndpointNameAddressExtensions.GetUriByName(this Microsoft.AspNetCore.Routing.LinkGenerator! generator, string! endpointName, Microsoft.AspNetCore.Routing.RouteValueDictionary! values, string! scheme, Microsoft.AspNetCore.Http.HostString host, Microsoft.AspNetCore.Http.PathString pathBase = default(Microsoft.AspNetCore.Http.PathString), Microsoft.AspNetCore.Http.FragmentString fragment = default(Microsoft.AspNetCore.Http.FragmentString), Microsoft.AspNetCore.Routing.LinkOptions? options = null) -> string?
+static Microsoft.AspNetCore.Routing.LinkGeneratorRouteValuesAddressExtensions.GetPathByRouteValues(this Microsoft.AspNetCore.Routing.LinkGenerator! generator, Microsoft.AspNetCore.Http.HttpContext! httpContext, string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? values = null, Microsoft.AspNetCore.Http.PathString? pathBase = null, Microsoft.AspNetCore.Http.FragmentString fragment = default(Microsoft.AspNetCore.Http.FragmentString), Microsoft.AspNetCore.Routing.LinkOptions? options = null) -> string?
+static Microsoft.AspNetCore.Routing.LinkGeneratorRouteValuesAddressExtensions.GetPathByRouteValues(this Microsoft.AspNetCore.Routing.LinkGenerator! generator, string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? values = null, Microsoft.AspNetCore.Http.PathString pathBase = default(Microsoft.AspNetCore.Http.PathString), Microsoft.AspNetCore.Http.FragmentString fragment = default(Microsoft.AspNetCore.Http.FragmentString), Microsoft.AspNetCore.Routing.LinkOptions? options = null) -> string?
+static Microsoft.AspNetCore.Routing.LinkGeneratorRouteValuesAddressExtensions.GetUriByRouteValues(this Microsoft.AspNetCore.Routing.LinkGenerator! generator, Microsoft.AspNetCore.Http.HttpContext! httpContext, string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary? values = null, string? scheme = null, Microsoft.AspNetCore.Http.HostString? host = null, Microsoft.AspNetCore.Http.PathString? pathBase = null, Microsoft.AspNetCore.Http.FragmentString fragment = default(Microsoft.AspNetCore.Http.FragmentString), Microsoft.AspNetCore.Routing.LinkOptions? options = null) -> string?
+static Microsoft.AspNetCore.Routing.LinkGeneratorRouteValuesAddressExtensions.GetUriByRouteValues(this Microsoft.AspNetCore.Routing.LinkGenerator! generator, string? routeName, Microsoft.AspNetCore.Routing.RouteValueDictionary! values, string! scheme, Microsoft.AspNetCore.Http.HostString host, Microsoft.AspNetCore.Http.PathString pathBase = default(Microsoft.AspNetCore.Http.PathString), Microsoft.AspNetCore.Http.FragmentString fragment = default(Microsoft.AspNetCore.Http.FragmentString), Microsoft.AspNetCore.Routing.LinkOptions? options = null) -> string?
+static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory.Parse(string! pattern, Microsoft.AspNetCore.Routing.RouteValueDictionary? defaults, Microsoft.AspNetCore.Routing.RouteValueDictionary? parameterPolicies) -> Microsoft.AspNetCore.Routing.Patterns.RoutePattern!
+static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory.Parse(string! pattern, Microsoft.AspNetCore.Routing.RouteValueDictionary? defaults, Microsoft.AspNetCore.Routing.RouteValueDictionary? parameterPolicies, Microsoft.AspNetCore.Routing.RouteValueDictionary? requiredValues) -> Microsoft.AspNetCore.Routing.Patterns.RoutePattern!
+static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory.Pattern(Microsoft.AspNetCore.Routing.RouteValueDictionary? defaults, Microsoft.AspNetCore.Routing.RouteValueDictionary? parameterPolicies, System.Collections.Generic.IEnumerable! segments) -> Microsoft.AspNetCore.Routing.Patterns.RoutePattern!
+static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory.Pattern(Microsoft.AspNetCore.Routing.RouteValueDictionary? defaults, Microsoft.AspNetCore.Routing.RouteValueDictionary? parameterPolicies, params Microsoft.AspNetCore.Routing.Patterns.RoutePatternPathSegment![]! segments) -> Microsoft.AspNetCore.Routing.Patterns.RoutePattern!
+static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory.Pattern(string? rawText, Microsoft.AspNetCore.Routing.RouteValueDictionary? defaults, Microsoft.AspNetCore.Routing.RouteValueDictionary? parameterPolicies, System.Collections.Generic.IEnumerable! segments) -> Microsoft.AspNetCore.Routing.Patterns.RoutePattern!
+static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory.Pattern(string? rawText, Microsoft.AspNetCore.Routing.RouteValueDictionary? defaults, Microsoft.AspNetCore.Routing.RouteValueDictionary? parameterPolicies, params Microsoft.AspNetCore.Routing.Patterns.RoutePatternPathSegment![]! segments) -> Microsoft.AspNetCore.Routing.Patterns.RoutePattern!
+virtual Microsoft.AspNetCore.Routing.Patterns.RoutePatternTransformer.SubstituteRequiredValues(Microsoft.AspNetCore.Routing.Patterns.RoutePattern! original, Microsoft.AspNetCore.Routing.RouteValueDictionary! requiredValues) -> Microsoft.AspNetCore.Routing.Patterns.RoutePattern?
diff --git a/src/Http/Routing/src/RequestDelegateRouteBuilderExtensions.cs b/src/Http/Routing/src/RequestDelegateRouteBuilderExtensions.cs
index 69ebe30b1693..88c8f5807ab0 100644
--- a/src/Http/Routing/src/RequestDelegateRouteBuilderExtensions.cs
+++ b/src/Http/Routing/src/RequestDelegateRouteBuilderExtensions.cs
@@ -256,11 +256,16 @@ public static IRouteBuilder MapVerb(
string template,
RequestDelegate handler)
{
+ var constraints = new RouteValueDictionary
+ {
+ ["httpMethod"] = new HttpMethodRouteConstraint(verb),
+ };
+
var route = new Route(
new RouteHandler(handler),
template,
defaults: null,
- constraints: new RouteValueDictionary(new { httpMethod = new HttpMethodRouteConstraint(verb) })!,
+ constraints: constraints!,
dataTokens: null,
inlineConstraintResolver: GetConstraintResolver(builder));
diff --git a/src/Http/Routing/src/RouteOptions.cs b/src/Http/Routing/src/RouteOptions.cs
index b54487f1c000..ce7474c3808c 100644
--- a/src/Http/Routing/src/RouteOptions.cs
+++ b/src/Http/Routing/src/RouteOptions.cs
@@ -86,6 +86,12 @@ public IDictionary ConstraintMap
}
}
+ ///
+ /// ensures that types are added to the constraint map in a trimmer safe way.
+ /// This API allows reading the map without encountering a trimmer warning within the framework.
+ ///
+ internal IDictionary TrimmerSafeConstraintMap => _constraintTypeMap;
+
private static IDictionary GetDefaultConstraintMap()
{
var defaults = new Dictionary(StringComparer.OrdinalIgnoreCase);
@@ -130,7 +136,7 @@ private static IDictionary GetDefaultConstraintMap()
/// The route token used to apply the parameter policy.
public void SetParameterPolicy<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(string token) where T : IParameterPolicy
{
- ConstraintMap[token] = typeof(T);
+ _constraintTypeMap[token] = typeof(T);
}
///
@@ -146,7 +152,7 @@ public void SetParameterPolicy(string token, [DynamicallyAccessedMembers(Dynamic
throw new InvalidOperationException($"{type} must implement {typeof(IParameterPolicy)}");
}
- ConstraintMap[token] = type;
+ _constraintTypeMap[token] = type;
}
private static void AddConstraint<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TConstraint>(Dictionary constraintMap, string text) where TConstraint : IRouteConstraint
diff --git a/src/Http/Routing/src/RouteValueDictionaryTrimmerWarning.cs b/src/Http/Routing/src/RouteValueDictionaryTrimmerWarning.cs
new file mode 100644
index 000000000000..020ad001f110
--- /dev/null
+++ b/src/Http/Routing/src/RouteValueDictionaryTrimmerWarning.cs
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.Routing;
+
+internal static class RouteValueDictionaryTrimmerWarning
+{
+ public const string Warning = "This API may perform reflection on supplied parameters which may be trimmed if not referenced directly. " +
+ "Consider using a different overload to avoid this issue.";
+}
diff --git a/src/Servers/Connections.Abstractions/src/Microsoft.AspNetCore.Connections.Abstractions.csproj b/src/Servers/Connections.Abstractions/src/Microsoft.AspNetCore.Connections.Abstractions.csproj
index 721ffadc6613..691b85d69717 100644
--- a/src/Servers/Connections.Abstractions/src/Microsoft.AspNetCore.Connections.Abstractions.csproj
+++ b/src/Servers/Connections.Abstractions/src/Microsoft.AspNetCore.Connections.Abstractions.csproj
@@ -7,7 +7,6 @@
true
true
aspnetcore
- enable
true
diff --git a/src/Tools/Tools.slnf b/src/Tools/Tools.slnf
index 17832a576e33..b419c1d28c6e 100644
--- a/src/Tools/Tools.slnf
+++ b/src/Tools/Tools.slnf
@@ -23,6 +23,7 @@
"src\\Http\\Http\\src\\Microsoft.AspNetCore.Http.csproj",
"src\\Http\\Metadata\\src\\Microsoft.AspNetCore.Metadata.csproj",
"src\\Http\\Routing.Abstractions\\src\\Microsoft.AspNetCore.Routing.Abstractions.csproj",
+ "src\\Http\\Routing\\src\\Microsoft.AspNetCore.Routing.csproj",
"src\\Http\\WebUtilities\\src\\Microsoft.AspNetCore.WebUtilities.csproj",
"src\\JSInterop\\Microsoft.JSInterop\\src\\Microsoft.JSInterop.csproj",
"src\\Middleware\\HttpOverrides\\src\\Microsoft.AspNetCore.HttpOverrides.csproj",