Skip to content

Commit 385fff7

Browse files
Chris Martinezcommonsensesoftware
authored andcommitted
Baseline support API explorer support for ASP.NET Core
1 parent 96ef223 commit 385fff7

35 files changed

+1677
-1
lines changed

ApiVersioning.sln

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNet.WebApi.Ver
5050
EndProject
5151
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNet.WebApi.Versioning.ApiExplorer.Tests", "test\Microsoft.AspNet.WebApi.Versioning.ApiExplorer.Tests\Microsoft.AspNet.WebApi.Versioning.ApiExplorer.Tests.csproj", "{AAFE5030-DF0A-4156-9AF5-1AAA53CA1FF9}"
5252
EndProject
53+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer", "src\Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer\Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer.csproj", "{FE19225D-6564-4FCF-BA1C-0ED5CA336C44}"
54+
EndProject
55+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer.Tests", "test\Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer.Tests\Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer.Tests.csproj", "{C8D29CB1-C541-4579-A1B8-AFD4B4F5F4A3}"
56+
EndProject
5357
Global
5458
GlobalSection(SharedMSBuildProjectFiles) = preSolution
5559
test\Acceptance.Test.Shared\Acceptance.Test.Shared.projitems*{6cdfb878-2642-4f98-ae35-621bac581181}*SharedItemsImports = 13
@@ -102,6 +106,14 @@ Global
102106
{AAFE5030-DF0A-4156-9AF5-1AAA53CA1FF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
103107
{AAFE5030-DF0A-4156-9AF5-1AAA53CA1FF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
104108
{AAFE5030-DF0A-4156-9AF5-1AAA53CA1FF9}.Release|Any CPU.Build.0 = Release|Any CPU
109+
{FE19225D-6564-4FCF-BA1C-0ED5CA336C44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
110+
{FE19225D-6564-4FCF-BA1C-0ED5CA336C44}.Debug|Any CPU.Build.0 = Debug|Any CPU
111+
{FE19225D-6564-4FCF-BA1C-0ED5CA336C44}.Release|Any CPU.ActiveCfg = Release|Any CPU
112+
{FE19225D-6564-4FCF-BA1C-0ED5CA336C44}.Release|Any CPU.Build.0 = Release|Any CPU
113+
{C8D29CB1-C541-4579-A1B8-AFD4B4F5F4A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
114+
{C8D29CB1-C541-4579-A1B8-AFD4B4F5F4A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
115+
{C8D29CB1-C541-4579-A1B8-AFD4B4F5F4A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
116+
{C8D29CB1-C541-4579-A1B8-AFD4B4F5F4A3}.Release|Any CPU.Build.0 = Release|Any CPU
105117
EndGlobalSection
106118
GlobalSection(SolutionProperties) = preSolution
107119
HideSolutionNode = FALSE
@@ -121,5 +133,7 @@ Global
121133
{6CDFB878-2642-4F98-AE35-621BAC581181} = {0987757E-4D09-4523-B9C9-65B1E8832AA1}
122134
{271C9E76-06EF-4D72-847A-AA27D793FE55} = {4D5F5F21-0CB7-4B4E-A42F-732BD4AFD0FF}
123135
{AAFE5030-DF0A-4156-9AF5-1AAA53CA1FF9} = {0987757E-4D09-4523-B9C9-65B1E8832AA1}
136+
{FE19225D-6564-4FCF-BA1C-0ED5CA336C44} = {4D5F5F21-0CB7-4B4E-A42F-732BD4AFD0FF}
137+
{C8D29CB1-C541-4579-A1B8-AFD4B4F5F4A3} = {0987757E-4D09-4523-B9C9-65B1E8832AA1}
124138
EndGlobalSection
125139
EndGlobal

ApiVersioningWithSamples.sln

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 15
4-
VisualStudioVersion = 15.0.26228.4
4+
VisualStudioVersion = 15.0.26228.9
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4D5F5F21-0CB7-4B4E-A42F-732BD4AFD0FF}"
77
EndProject
@@ -80,6 +80,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.WebApi.Ver
8080
EndProject
8181
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.WebApi.Versioning.ApiExplorer.Tests", "test\Microsoft.AspNet.WebApi.Versioning.ApiExplorer.Tests\Microsoft.AspNet.WebApi.Versioning.ApiExplorer.Tests.csproj", "{19A2C130-46B4-4CA3-B655-B7547BC414AC}"
8282
EndProject
83+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer", "src\Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer\Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer.csproj", "{F7784C3A-5569-4590-AE28-B721C0426045}"
84+
EndProject
85+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer.Tests", "test\Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer.Tests\Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer.Tests.csproj", "{15461DBB-95AD-4CA7-AF41-E70F54860FE3}"
86+
EndProject
87+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwaggerSample", "samples\aspnetcore\SwaggerSample\SwaggerSample.csproj", "{D95BC932-50F7-4014-970E-0C6E8400BE25}"
88+
EndProject
8389
Global
8490
GlobalSection(SharedMSBuildProjectFiles) = preSolution
8591
test\Acceptance.Test.Shared\Acceptance.Test.Shared.projitems*{6cdfb878-2642-4f98-ae35-621bac581181}*SharedItemsImports = 13
@@ -168,6 +174,18 @@ Global
168174
{19A2C130-46B4-4CA3-B655-B7547BC414AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
169175
{19A2C130-46B4-4CA3-B655-B7547BC414AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
170176
{19A2C130-46B4-4CA3-B655-B7547BC414AC}.Release|Any CPU.Build.0 = Release|Any CPU
177+
{F7784C3A-5569-4590-AE28-B721C0426045}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
178+
{F7784C3A-5569-4590-AE28-B721C0426045}.Debug|Any CPU.Build.0 = Debug|Any CPU
179+
{F7784C3A-5569-4590-AE28-B721C0426045}.Release|Any CPU.ActiveCfg = Release|Any CPU
180+
{F7784C3A-5569-4590-AE28-B721C0426045}.Release|Any CPU.Build.0 = Release|Any CPU
181+
{15461DBB-95AD-4CA7-AF41-E70F54860FE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
182+
{15461DBB-95AD-4CA7-AF41-E70F54860FE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
183+
{15461DBB-95AD-4CA7-AF41-E70F54860FE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
184+
{15461DBB-95AD-4CA7-AF41-E70F54860FE3}.Release|Any CPU.Build.0 = Release|Any CPU
185+
{D95BC932-50F7-4014-970E-0C6E8400BE25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
186+
{D95BC932-50F7-4014-970E-0C6E8400BE25}.Debug|Any CPU.Build.0 = Debug|Any CPU
187+
{D95BC932-50F7-4014-970E-0C6E8400BE25}.Release|Any CPU.ActiveCfg = Release|Any CPU
188+
{D95BC932-50F7-4014-970E-0C6E8400BE25}.Release|Any CPU.Build.0 = Release|Any CPU
171189
EndGlobalSection
172190
GlobalSection(SolutionProperties) = preSolution
173191
HideSolutionNode = FALSE
@@ -198,5 +216,8 @@ Global
198216
{83B21A5B-0779-4391-9700-58AEFEBFA615} = {900DD210-8500-4D89-A05D-C9526935A719}
199217
{91E1F0B5-905D-446C-A2DD-4C1EDABFAF6C} = {4D5F5F21-0CB7-4B4E-A42F-732BD4AFD0FF}
200218
{19A2C130-46B4-4CA3-B655-B7547BC414AC} = {0987757E-4D09-4523-B9C9-65B1E8832AA1}
219+
{F7784C3A-5569-4590-AE28-B721C0426045} = {4D5F5F21-0CB7-4B4E-A42F-732BD4AFD0FF}
220+
{15461DBB-95AD-4CA7-AF41-E70F54860FE3} = {0987757E-4D09-4523-B9C9-65B1E8832AA1}
221+
{D95BC932-50F7-4014-970E-0C6E8400BE25} = {900DD210-8500-4D89-A05D-C9526935A719}
201222
EndGlobalSection
202223
EndGlobal
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
namespace Microsoft.Examples
2+
{
3+
using Microsoft.AspNetCore.Mvc.ApiExplorer;
4+
using Swashbuckle.AspNetCore.Swagger;
5+
using Swashbuckle.AspNetCore.SwaggerGen;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
9+
/// <summary>
10+
/// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter.
11+
/// </summary>
12+
public class ImplicitApiVersionParameter : IOperationFilter
13+
{
14+
/// <summary>
15+
/// Applies the filter to the specified operation using the given context.
16+
/// </summary>
17+
/// <param name="operation">The operation to apply the filter to.</param>
18+
/// <param name="context">The current operation filter context.</param>
19+
public void Apply( Operation operation, OperationFilterContext context )
20+
{
21+
var apiVersion = context.ApiDescription.GetApiVersion();
22+
23+
// if the api explorer did not capture an API version for this operation
24+
// then the action must be API version-neutral; there's nothing to add
25+
if ( apiVersion == null )
26+
{
27+
return;
28+
}
29+
30+
var parameters = operation.Parameters;
31+
32+
if ( parameters == null )
33+
{
34+
operation.Parameters = parameters = new List<IParameter>();
35+
}
36+
37+
// note: in most applications, service authors will choose a single, consistent
38+
// approach to how API versioning is applied. this sample uses a:
39+
//
40+
// 1. query string parameter method with the name "api-version"
41+
// 2. url path segement with the route parameter name "api-version"
42+
//
43+
// unless you allow multiple API versioning methods in your application,
44+
// your implementation should be even simpler.
45+
46+
// consider the url path segment parameter first
47+
var parameter = parameters.SingleOrDefault( p => p.Name == "api-version" );
48+
49+
if ( parameter == null )
50+
{
51+
// the only other method in this sample is by query string
52+
parameter = new NonBodyParameter()
53+
{
54+
Name = "api-version",
55+
Required = true,
56+
Default = apiVersion.ToString(),
57+
Description = "The requested API version",
58+
In = "query",
59+
Type = "string"
60+
};
61+
62+
parameters.Add( parameter );
63+
}
64+
else if ( parameter is NonBodyParameter pathParameter )
65+
{
66+
// update the default value with the current API version so that
67+
// the route can be invoked in the "Try It!" feature
68+
pathParameter.Default = apiVersion.ToString();
69+
}
70+
}
71+
}
72+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace Microsoft.Examples
2+
{
3+
using Microsoft.AspNetCore.Builder;
4+
using Microsoft.AspNetCore.Hosting;
5+
using System.IO;
6+
7+
/// <summary>
8+
/// Represents the current application.
9+
/// </summary>
10+
public class Program
11+
{
12+
/// <summary>
13+
/// The main entry point to the application.
14+
/// </summary>
15+
/// <param name="args">The arguments provides at start-up, if any.</param>
16+
public static void Main( string[] args )
17+
{
18+
var host = new WebHostBuilder()
19+
.UseKestrel()
20+
.UseContentRoot( Directory.GetCurrentDirectory() )
21+
.UseIISIntegration()
22+
.UseStartup<Startup>()
23+
.Build();
24+
25+
host.Run();
26+
}
27+
}
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"iisSettings": {
3+
"windowsAuthentication": false,
4+
"anonymousAuthentication": true,
5+
"iisExpress": {
6+
"applicationUrl": "http://localhost:3712/",
7+
"sslPort": 0
8+
}
9+
},
10+
"profiles": {
11+
"IIS Express": {
12+
"commandName": "IISExpress",
13+
"launchBrowser": true,
14+
"launchUrl": "swagger",
15+
"environmentVariables": {
16+
"ASPNETCORE_ENVIRONMENT": "Development"
17+
}
18+
},
19+
"BasicSample": {
20+
"commandName": "Project",
21+
"launchBrowser": true,
22+
"launchUrl": "http://localhost:5000/api/values",
23+
"environmentVariables": {
24+
"ASPNETCORE_ENVIRONMENT": "Development"
25+
}
26+
}
27+
}
28+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
namespace Microsoft.Examples
2+
{
3+
using Microsoft.AspNetCore.Builder;
4+
using Microsoft.AspNetCore.Hosting;
5+
using Microsoft.AspNetCore.Mvc.ApiExplorer;
6+
using Microsoft.Extensions.Configuration;
7+
using Microsoft.Extensions.DependencyInjection;
8+
using Microsoft.Extensions.Logging;
9+
using Microsoft.Extensions.PlatformAbstractions;
10+
using Swashbuckle.AspNetCore.Swagger;
11+
using System.IO;
12+
using System.Reflection;
13+
14+
/// <summary>
15+
/// Represents the startup process for the application.
16+
/// </summary>
17+
public class Startup
18+
{
19+
/// <summary>
20+
/// Initializes a new instance of the <see cref="Startup"/> class.
21+
/// </summary>
22+
/// <param name="env">The current hosting environment.</param>
23+
public Startup( IHostingEnvironment env )
24+
{
25+
var builder = new ConfigurationBuilder()
26+
.SetBasePath( env.ContentRootPath )
27+
.AddJsonFile( "appsettings.json", optional: true, reloadOnChange: true )
28+
.AddJsonFile( $"appsettings.{env.EnvironmentName}.json", optional: true )
29+
.AddEnvironmentVariables();
30+
31+
Configuration = builder.Build();
32+
}
33+
34+
/// <summary>
35+
/// Gets the current configuration.
36+
/// </summary>
37+
/// <value>The current application configuration.</value>
38+
public IConfigurationRoot Configuration { get; }
39+
40+
/// <summary>
41+
/// Configures services for the application.
42+
/// </summary>
43+
/// <param name="services">The collection of services to configure the application with.</param>
44+
public void ConfigureServices( IServiceCollection services )
45+
{
46+
// add the versioned api explorer, which also adds the following services:
47+
//
48+
// * IApiVersionDescriptionProvider
49+
// * IApiVersionGroupNameFormatter
50+
services.AddMvcCore().AddVersionedApiExplorer();
51+
52+
services.AddMvc();
53+
services.AddApiVersioning( o => o.ReportApiVersions = true );
54+
services.AddSwaggerGen(
55+
options =>
56+
{
57+
// resolve the IApiVersionDescriptionProvider service
58+
// note: that we have to build a temporary service provider here because one has not been created yet
59+
var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
60+
61+
// add a swagger document for each discovered API version
62+
// note: you might choose to skip or document deprecated API versions differently
63+
foreach ( var description in provider.ApiVersionDescriptions )
64+
{
65+
options.SwaggerDoc( description.GroupName, CreateInfoForApiVersion( description ) );
66+
}
67+
68+
// add a custom operation filter which documents the implicit API version parameter
69+
options.OperationFilter<ImplicitApiVersionParameter>();
70+
71+
// integrate xml comments
72+
options.IncludeXmlComments( XmlCommentsFilePath );
73+
} );
74+
}
75+
76+
/// <summary>
77+
/// Configures the application using the provided builder, hosting environment, and logging factory.
78+
/// </summary>
79+
/// <param name="app">The current application builder.</param>
80+
/// <param name="env">The current hosting environment.</param>
81+
/// <param name="loggerFactory">The logging factory used for instrumentation.</param>
82+
public void Configure( IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory )
83+
{
84+
loggerFactory.AddConsole( Configuration.GetSection( "Logging" ) );
85+
loggerFactory.AddDebug();
86+
87+
app.UseMvc();
88+
app.UseSwagger();
89+
app.UseSwaggerUI(
90+
options =>
91+
{
92+
// resolve the IApiVersionDescriptionProvider service
93+
var provider = app.ApplicationServices.GetRequiredService<IApiVersionDescriptionProvider>();
94+
95+
// build a swagger endpoint for each discovered API version
96+
foreach ( var description in provider.ApiVersionDescriptions )
97+
{
98+
options.SwaggerEndpoint( $"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant() );
99+
}
100+
} );
101+
}
102+
103+
static string XmlCommentsFilePath
104+
{
105+
get
106+
{
107+
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
108+
var fileName = typeof( Startup ).GetTypeInfo().Assembly.GetName().Name + ".xml";
109+
return Path.Combine( basePath, fileName );
110+
}
111+
}
112+
113+
static Info CreateInfoForApiVersion( ApiVersionDescription description )
114+
{
115+
var info = new Info()
116+
{
117+
Title = $"Sample API {description.ApiVersion}",
118+
Version = description.ApiVersion.ToString(),
119+
Description = "A sample application with Swagger, Swashbuckle, and API versioning.",
120+
Contact = new Contact() { Name = "Bill Mei", Email = "bill.mei@somewhere.com" },
121+
TermsOfService = "Shareware",
122+
License = new License() { Name = "MIT", Url = "https://opensource.org/licenses/MIT" }
123+
};
124+
125+
if ( description.IsDeprecated )
126+
{
127+
info.Description += " This API version has been deprecated.";
128+
}
129+
130+
return info;
131+
}
132+
}
133+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp1.0</TargetFramework>
5+
<PreserveCompilationContext>true</PreserveCompilationContext>
6+
<AssemblyName>SwaggerSample</AssemblyName>
7+
<OutputType>Exe</OutputType>
8+
<PackageId>SwaggerSample</PackageId>
9+
<RuntimeFrameworkVersion>1.0.3</RuntimeFrameworkVersion>
10+
<PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>
11+
<DocumentationFile>bin\$(Configuration)\netcoreapp1.0\SwaggerSample.xml</DocumentationFile>
12+
<RootNamespace>Microsoft.Examples</RootNamespace>
13+
</PropertyGroup>
14+
15+
<ItemGroup>
16+
<Content Update="wwwroot\**\*;Views;Areas\**\Views;appsettings.json;web.config">
17+
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
18+
</Content>
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.Mvc.Versioning\Microsoft.AspNetCore.Mvc.Versioning.csproj" />
23+
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer\Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer.csproj" />
24+
</ItemGroup>
25+
26+
<ItemGroup>
27+
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.1" />
28+
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.0.1" />
29+
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.0.2" />
30+
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.0.1" />
31+
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.0.1" />
32+
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.0.1" />
33+
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.0.1" />
34+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.0.1" />
35+
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.0.1" />
36+
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.0.1" />
37+
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.0.1" />
38+
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0-rc3" />
39+
</ItemGroup>
40+
41+
</Project>

0 commit comments

Comments
 (0)