Skip to content

Commit 0df0b58

Browse files
Chris Martinezcommonsensesoftware
authored andcommitted
Added extension points for OData API explorer
1 parent 812f5bd commit 0df0b58

File tree

3 files changed

+65
-36
lines changed

3 files changed

+65
-36
lines changed

src/Microsoft.AspNet.WebApi.Versioning.ApiExplorer/Description/VersionedApiExplorer.cs

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public VersionedApiExplorer( HttpConfiguration configuration )
8484
/// <value>The <see cref="IDocumentationProvider">documentation provider</see> used to document APIs.</value>
8585
public IDocumentationProvider DocumentationProvider
8686
{
87-
get => documentationProvider ?? Configuration.Services.GetDocumentationProvider();
87+
get => documentationProvider ?? ( documentationProvider = Configuration.Services.GetDocumentationProvider() );
8888
set => documentationProvider = value;
8989
}
9090

@@ -258,7 +258,7 @@ protected virtual ApiDescriptionGroupCollection InitializeApiDescriptions()
258258
var directRouteController = GetDirectRouteController( directRouteCandidates, apiVersion );
259259
var apiDescriptionGroup = newApiDescriptions.GetOrAdd( apiVersion, GetGroupName );
260260
var descriptionsFromRoute = ( directRouteController != null && directRouteCandidates != null ) ?
261-
ExploreDirectRoute( directRouteController, directRouteCandidates, route, apiVersion ) :
261+
ExploreDirectRouteControllers( directRouteController, directRouteCandidates.Select( c => c.ActionDescriptor ).ToArray(), route, apiVersion ) :
262262
ExploreRouteControllers( controllerMappings, route, apiVersion );
263263

264264
// Remove ApiDescription that will lead to ambiguous action matching.
@@ -532,12 +532,24 @@ static HttpControllerDescriptor GetDirectRouteController( CandidateAction[] dire
532532
return controllerDescriptor;
533533
}
534534

535-
Collection<VersionedApiDescription> ExploreDirectRoute( HttpControllerDescriptor controllerDescriptor, CandidateAction[] candidates, IHttpRoute route, ApiVersion apiVersion )
535+
/// <summary>
536+
/// Explores a controller that uses direct routes (aka "attribute" routing).
537+
/// </summary>
538+
/// <param name="controllerDescriptor">The <see cref="HttpControllerDescriptor">controller</see> to explore.</param>
539+
/// <param name="candidateActionDescriptors">The <see cref="IReadOnlyList{T}">read-only list</see> of candidate <see cref="HttpActionDescriptor">actions</see> to explore.</param>
540+
/// <param name="route">The <see cref="IHttpRoute">route</see> to explore.</param>
541+
/// <param name="apiVersion">The <see cref="ApiVersion">API version</see> to explore.</param>
542+
/// <returns>The <see cref="Collection{T}">collection</see> of discovered <see cref="VersionedApiDescription">API descriptions</see>.</returns>
543+
protected virtual Collection<VersionedApiDescription> ExploreDirectRouteControllers(
544+
HttpControllerDescriptor controllerDescriptor,
545+
IReadOnlyList<HttpActionDescriptor> candidateActionDescriptors,
546+
IHttpRoute route,
547+
ApiVersion apiVersion )
536548
{
537-
Contract.Requires( controllerDescriptor != null );
538-
Contract.Requires( candidates != null );
539-
Contract.Requires( route != null );
540-
Contract.Requires( apiVersion != null );
549+
Arg.NotNull( controllerDescriptor, nameof( controllerDescriptor ) );
550+
Arg.NotNull( candidateActionDescriptors, nameof( candidateActionDescriptors ) );
551+
Arg.NotNull( route, nameof( route ) );
552+
Arg.NotNull( apiVersion, nameof( apiVersion ) );
541553
Contract.Ensures( Contract.Result<Collection<VersionedApiDescription>>() != null );
542554

543555
var descriptions = new Collection<VersionedApiDescription>();
@@ -547,9 +559,8 @@ Collection<VersionedApiDescription> ExploreDirectRoute( HttpControllerDescriptor
547559
return descriptions;
548560
}
549561

550-
foreach ( var action in candidates )
562+
foreach ( var actionDescriptor in candidateActionDescriptors )
551563
{
552-
var actionDescriptor = action.ActionDescriptor;
553564
var actionName = actionDescriptor.ActionName;
554565

555566
if ( !ShouldExploreAction( actionName, actionDescriptor, route, apiVersion ) )
@@ -571,11 +582,18 @@ Collection<VersionedApiDescription> ExploreDirectRoute( HttpControllerDescriptor
571582
return descriptions;
572583
}
573584

574-
Collection<VersionedApiDescription> ExploreRouteControllers( IDictionary<string, HttpControllerDescriptor> controllerMappings, IHttpRoute route, ApiVersion apiVersion )
585+
/// <summary>
586+
/// Explores controllers that do not use direct routes (aka "attribute" routing)
587+
/// </summary>
588+
/// <param name="controllerMappings">The <see cref="IDictionary{TKey, TValue}">collection</see> of controller mappings.</param>
589+
/// <param name="route">The <see cref="IHttpRoute">route</see> to explore.</param>
590+
/// <param name="apiVersion">The <see cref="ApiVersion">API version</see> to explore.</param>
591+
/// <returns>The <see cref="Collection{T}">collection</see> of discovered <see cref="VersionedApiDescription">API descriptions</see>.</returns>
592+
protected virtual Collection<VersionedApiDescription> ExploreRouteControllers( IDictionary<string, HttpControllerDescriptor> controllerMappings, IHttpRoute route, ApiVersion apiVersion )
575593
{
576-
Contract.Requires( controllerMappings != null );
577-
Contract.Requires( route != null );
578-
Contract.Requires( apiVersion != null );
594+
Arg.NotNull( controllerMappings, nameof( controllerMappings ) );
595+
Arg.NotNull( route, nameof( route ) );
596+
Arg.NotNull( apiVersion, nameof( apiVersion ) );
579597
Contract.Ensures( Contract.Result<Collection<VersionedApiDescription>>() != null );
580598

581599
var apiDescriptions = new Collection<VersionedApiDescription>();
@@ -706,7 +724,6 @@ void PopulateActionDescriptions(
706724
Contract.Requires( apiDescriptions != null );
707725
Contract.Requires( apiVersion != null );
708726

709-
var apiDocumentation = DocumentationProvider?.GetResponseDocumentation( actionDescriptor );
710727
var parsedRoute = RouteParser.Parse( localPath );
711728
var parameterDescriptions = CreateParameterDescriptions( actionDescriptor, parsedRoute, route.Defaults );
712729

@@ -715,14 +732,17 @@ void PopulateActionDescriptions(
715732
return;
716733
}
717734

735+
var documentation = DocumentationProvider?.GetDocumentation( actionDescriptor );
718736
var bodyParameter = parameterDescriptions.FirstOrDefault( description => description.Source == FromBody );
719-
var supportedRequestBodyFormatters = bodyParameter != null ?
737+
var supportedRequestBodyFormatters =
738+
bodyParameter != null ?
720739
Configuration.Formatters.Where( f => f.CanReadType( bodyParameter.ParameterDescriptor.ParameterType ) ) :
721740
Enumerable.Empty<MediaTypeFormatter>();
722741

723742
var responseDescription = CreateResponseDescription( actionDescriptor );
724743
var returnType = responseDescription.ResponseType ?? responseDescription.DeclaredType;
725-
var supportedResponseFormatters = ( returnType != null && returnType != typeof( void ) ) ?
744+
var supportedResponseFormatters =
745+
( returnType != null && returnType != typeof( void ) ) ?
726746
Configuration.Formatters.Where( f => f.CanWriteType( returnType ) ) :
727747
Enumerable.Empty<MediaTypeFormatter>();
728748

@@ -736,7 +756,7 @@ void PopulateActionDescriptions(
736756
{
737757
var apiDescription = new VersionedApiDescription()
738758
{
739-
Documentation = apiDocumentation,
759+
Documentation = documentation,
740760
HttpMethod = method,
741761
RelativePath = finalPath,
742762
ActionDescriptor = actionDescriptor,
@@ -753,9 +773,14 @@ void PopulateActionDescriptions(
753773
}
754774
}
755775

756-
ResponseDescription CreateResponseDescription( HttpActionDescriptor actionDescriptor )
776+
/// <summary>
777+
/// Creates a description for the response of the action.
778+
/// </summary>
779+
/// <param name="actionDescriptor">The <see cref="HttpActionDescriptor">action</see> to create a response description for.</param>
780+
/// <returns>A new <see cref="ResponseDescription">response description</see>.</returns>
781+
protected virtual ResponseDescription CreateResponseDescription( HttpActionDescriptor actionDescriptor )
757782
{
758-
Contract.Requires( actionDescriptor != null );
783+
Arg.NotNull( actionDescriptor, nameof( actionDescriptor ) );
759784
Contract.Ensures( Contract.Result<ResponseDescription>() != null );
760785

761786
var responseType = actionDescriptor.GetCustomAttributes<ResponseTypeAttribute>().FirstOrDefault()?.ResponseType;
@@ -857,7 +882,7 @@ IList<ApiParameterDescription> CreateParameterDescriptions( HttpActionDescriptor
857882
{
858883
foreach ( var parameter in parameters )
859884
{
860-
parameterDescriptions.Add( CreateParameterDescriptionFromDescriptor( parameter ) );
885+
parameterDescriptions.Add( CreateParameterDescription( parameter ) );
861886
}
862887
}
863888
}
@@ -894,16 +919,21 @@ static void AddUndeclaredRouteParameters( IParsedRoute parsedRoute, IDictionary<
894919
}
895920
}
896921

897-
ApiParameterDescription CreateParameterDescriptionFromDescriptor( HttpParameterDescriptor parameter )
922+
/// <summary>
923+
/// Creates a parameter description from the speicfied descriptor.
924+
/// </summary>
925+
/// <param name="parameterDescriptor">The <see cref="HttpParameterDescriptor">parameter descriptor</see> to create a description from.</param>
926+
/// <returns>A new <see cref="ApiParameterDescription">parameter description</see>.</returns>
927+
protected virtual ApiParameterDescription CreateParameterDescription( HttpParameterDescriptor parameterDescriptor )
898928
{
899-
Contract.Requires( parameter != null );
929+
Arg.NotNull( parameterDescriptor, nameof( parameterDescriptor ) );
900930
Contract.Ensures( Contract.Result<ApiParameterDescription>() != null );
901931

902932
return new ApiParameterDescription()
903933
{
904-
ParameterDescriptor = parameter,
905-
Name = parameter.Prefix ?? parameter.ParameterName,
906-
Documentation = DocumentationProvider?.GetDocumentation( parameter ),
934+
ParameterDescriptor = parameterDescriptor,
935+
Name = parameterDescriptor.Prefix ?? parameterDescriptor.ParameterName,
936+
Documentation = DocumentationProvider?.GetDocumentation( parameterDescriptor ),
907937
Source = Unknown,
908938
};
909939
}
@@ -913,7 +943,7 @@ ApiParameterDescription CreateParameterDescriptionFromBinding( HttpParameterBind
913943
Contract.Requires( parameterBinding != null );
914944
Contract.Ensures( Contract.Result<ApiParameterDescription>() != null );
915945

916-
var parameterDescription = CreateParameterDescriptionFromDescriptor( parameterBinding.Descriptor );
946+
var parameterDescription = CreateParameterDescription( parameterBinding.Descriptor );
917947

918948
if ( parameterBinding.WillReadBody )
919949
{
@@ -927,10 +957,6 @@ ApiParameterDescription CreateParameterDescriptionFromBinding( HttpParameterBind
927957
return parameterDescription;
928958
}
929959

930-
string GetApiDocumentation( HttpActionDescriptor actionDescriptor ) => DocumentationProvider?.GetDocumentation( actionDescriptor );
931-
932-
string GetApiResponseDocumentation( HttpActionDescriptor actionDescriptor ) => DocumentationProvider?.GetResponseDocumentation( actionDescriptor );
933-
934960
static Collection<VersionedApiDescription> RemoveInvalidApiDescriptions( Collection<VersionedApiDescription> apiDescriptions )
935961
{
936962
Contract.Requires( apiDescriptions != null );

src/Microsoft.AspNet.WebApi.Versioning.ApiExplorer/Microsoft.AspNet.WebApi.Versioning.ApiExplorer.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
<PackageReleaseNotes></PackageReleaseNotes>
1414
</PropertyGroup>
1515

16+
<ItemGroup>
17+
<Compile Include="..\Shared\SharedAssemblyInfo.cs" Link="SharedAssemblyInfo.cs" />
18+
</ItemGroup>
19+
1620
<ItemGroup>
1721
<ProjectReference Include="..\Microsoft.AspNet.WebApi.Versioning\Microsoft.AspNet.WebApi.Versioning.csproj" />
1822
</ItemGroup>

test/Microsoft.AspNet.WebApi.Versioning.ApiExplorer.Tests/Microsoft.AspNet.WebApi.Versioning.ApiExplorer.Tests.csproj

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
<PropertyGroup>
44
<TargetFramework>net452</TargetFramework>
5-
<DefineConstants>$(DefineConstants);WEBAPI</DefineConstants>
65
<AssemblyName>Microsoft.AspNet.WebApi.Versioning.ApiExplorer.Tests</AssemblyName>
76
<RootNamespace>Microsoft.Web.Http</RootNamespace>
87
</PropertyGroup>
@@ -12,15 +11,15 @@
1211
</ItemGroup>
1312

1413
<ItemGroup>
15-
<PackageReference Include="more.xunit" Version="2.2.2" />
16-
<PackageReference Include="more.xunit.runner.visualstudio" Version="2.2.1" />
1714
<PackageReference Include="FluentAssertions" Version="4.19.2" />
1815
<PackageReference Include="Moq" Version="4.7.0" />
19-
</ItemGroup>
20-
21-
<ItemGroup Condition=" '$(TargetFramework)' == 'net452' ">
16+
<PackageReference Include="more.xunit" Version="2.2.2" />
17+
<PackageReference Include="more.xunit.runner.visualstudio" Version="2.2.1" />
2218
<PackageReference Include="System.Runtime" Version="4.1.0" />
2319
<PackageReference Include="System.Threading.Tasks" Version="4.0.11" />
20+
</ItemGroup>
21+
22+
<ItemGroup>
2423
<Reference Include="System" />
2524
<Reference Include="Microsoft.CSharp" />
2625
</ItemGroup>

0 commit comments

Comments
 (0)