diff --git a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/DefaultApiDescriptionProvider.cs b/src/Microsoft.AspNetCore.Mvc.ApiExplorer/DefaultApiDescriptionProvider.cs
index a9d644e2a8..46fa05fb5a 100644
--- a/src/Microsoft.AspNetCore.Mvc.ApiExplorer/DefaultApiDescriptionProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.ApiExplorer/DefaultApiDescriptionProvider.cs
@@ -26,6 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
private readonly MvcOptions _mvcOptions;
private readonly IActionResultTypeMapper _mapper;
private readonly ApiResponseTypeProvider _responseTypeProvider;
+ private readonly RouteOptions _routeOptions;
private readonly IInlineConstraintResolver _constraintResolver;
private readonly IModelMetadataProvider _modelMetadataProvider;
@@ -53,6 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
/// constraints.
/// The .
/// The .
+ [Obsolete("This constructor is obsolete and will be removed in a future release.")]
public DefaultApiDescriptionProvider(
IOptions optionsAccessor,
IInlineConstraintResolver constraintResolver,
@@ -66,6 +68,30 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
_responseTypeProvider = new ApiResponseTypeProvider(modelMetadataProvider, mapper, _mvcOptions);
}
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The accessor for .
+ /// The used for resolving inline
+ /// constraints.
+ /// The .
+ /// The .
+ /// The accessor for .
+ public DefaultApiDescriptionProvider(
+ IOptions optionsAccessor,
+ IInlineConstraintResolver constraintResolver,
+ IModelMetadataProvider modelMetadataProvider,
+ IActionResultTypeMapper mapper,
+ IOptions routeOptions)
+ {
+ _mvcOptions = optionsAccessor.Value;
+ _constraintResolver = constraintResolver;
+ _modelMetadataProvider = modelMetadataProvider;
+ _mapper = mapper;
+ _responseTypeProvider = new ApiResponseTypeProvider(modelMetadataProvider, mapper, _mvcOptions);
+ _routeOptions = routeOptions.Value;
+ }
+
///
public int Order => -1000;
@@ -383,7 +409,9 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
{
if (part.IsLiteral)
{
- currentSegment += part.Text;
+ currentSegment += _routeOptions.LowercaseUrls ?
+ part.Text.ToLowerInvariant() :
+ part.Text;
}
else if (part.IsParameter)
{
diff --git a/test/Microsoft.AspNetCore.Mvc.ApiExplorer.Test/DefaultApiDescriptionProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.ApiExplorer.Test/DefaultApiDescriptionProviderTest.cs
index 5caceeaa02..224a6b1276 100644
--- a/test/Microsoft.AspNetCore.Mvc.ApiExplorer.Test/DefaultApiDescriptionProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.ApiExplorer.Test/DefaultApiDescriptionProviderTest.cs
@@ -387,6 +387,25 @@ namespace Microsoft.AspNetCore.Mvc.Description
Assert.Single(description.ParameterDescriptions, p => p.Name == "id5");
}
+ [Fact]
+ public void GetApiDescription_ProducesLowerCaseRelativePaths()
+ {
+ // Arrange
+ var action = CreateActionDescriptor();
+ action.AttributeRouteInfo = new AttributeRouteInfo
+ {
+ Template = "api/Products/UpdateProduct/{productId}"
+ };
+ var routeOptions = new RouteOptions { LowercaseUrls = true };
+
+ // Act
+ var descriptions = GetApiDescriptions(action, routeOptions: routeOptions);
+
+ // Assert
+ var description = Assert.Single(descriptions);
+ Assert.Equal("api/products/updateproduct/{productId}", description.RelativePath);
+ }
+
[Fact]
public void GetApiDescription_PopulatesResponseType_WithProduct()
{
@@ -1797,7 +1816,8 @@ namespace Microsoft.AspNetCore.Mvc.Description
ActionDescriptor action,
List inputFormatters = null,
List outputFormatters = null,
- bool allowValidatingTopLevelNodes = true)
+ bool allowValidatingTopLevelNodes = true,
+ RouteOptions routeOptions = null)
{
var context = new ApiDescriptionProviderContext(new ActionDescriptor[] { action });
@@ -1827,7 +1847,8 @@ namespace Microsoft.AspNetCore.Mvc.Description
optionsAccessor,
constraintResolver.Object,
modelMetadataProvider,
- new ActionResultTypeMapper());
+ new ActionResultTypeMapper(),
+ Options.Create(routeOptions ?? new RouteOptions()));
provider.OnProvidersExecuting(context);
provider.OnProvidersExecuted(context);