diff --git a/eng/Versions.props b/eng/Versions.props index f7b99d3cd9..f1b77d9fd1 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -274,6 +274,7 @@ 4.0.0 2.0.593 3.1.1 + 5.5.1 2.0.3 0.10.0 2.4.1 diff --git a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj index 2d8969ece5..9aa8755e9f 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj +++ b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj @@ -23,6 +23,7 @@ MicrosoftNETCoreAppRuntimeVersion=$(MicrosoftNETCoreAppRuntimeVersion); SystemNetHttpJsonPackageVersion=$(SystemNetHttpJsonPackageVersion); MicrosoftGraphPackageVersion=$(MicrosoftGraphPackageVersion); + SwashbuckleAspNetCorePackageVersion=$(SwashbuckleAspNetCorePackageVersion); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in index 1573dbfe37..44619a1663 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in +++ b/src/ProjectTemplates/Web.ProjectTemplates/WebApi-CSharp.csproj.in @@ -6,13 +6,14 @@ True Company.WebApplication1 - + - - - - + + + + + diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json index 36f8f5bf5c..af0458974c 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/dotnetcli.host.json @@ -57,19 +57,23 @@ "shortName": "" }, "CalledApiUrl": { - "longName": "called-api-url", - "shortName": "" + "longName": "called-api-url", + "shortName": "" }, "CalledApiScopes": { - "longName": "called-api-scopes", - "shortName": "" + "longName": "called-api-scopes", + "shortName": "" }, "CallsMicrosoftGraph": { - "longName": "calls-graph", - "shortName": "" + "longName": "calls-graph", + "shortName": "" + }, + "DisableOpenAPI": { + "longName": "no-openapi", + "shortName": "" } }, "usageExamples": [ "" ] -} +} \ No newline at end of file diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json index 1990a4e711..f11b772861 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json @@ -39,14 +39,14 @@ }, { "condition": "(!GenerateApi)", - "exclude": [ - "Services/DownstreamWebApi.cs" - ] + "exclude": [ + "Services/DownstreamWebApi.cs" + ] }, { "condition": "(!GenerateGraph)", "exclude": [ - "Services/MicrosoftGraphServiceExtensions.cs", + "Services/MicrosoftGraphServiceExtensions.cs", "Services/TokenAcquisitionCredentialProvider.cs" ] } @@ -244,35 +244,45 @@ "defaultValue": "false" }, "CalledApiUrl": { - "type": "parameter", - "datatype": "string", - "replaces": "[WebApiUrl]", - "defaultValue" : "https://graph.microsoft.com/v1.0", - "description": "URL of the API to call from the web app. This option only applies if --auth SingleOrg or --auth IndividualB2C is specified." + "type": "parameter", + "datatype": "string", + "replaces": "[WebApiUrl]", + "defaultValue": "https://graph.microsoft.com/v1.0", + "description": "URL of the API to call from the web app. This option only applies if --auth SingleOrg or --auth IndividualB2C is specified." }, "CallsMicrosoftGraph": { - "type": "parameter", - "datatype": "bool", - "defaultValue": "false", - "description": "Specifies if the web app calls Microsoft Graph. This option only applies if --auth SingleOrg is specified." + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Specifies if the web app calls Microsoft Graph. This option only applies if --auth SingleOrg is specified." }, "CalledApiScopes": { "type": "parameter", "datatype": "string", - "replaces" : "user.read", + "replaces": "user.read", "description": "Scopes to request to call the API from the web app. This option only applies if --auth SingleOrg or --auth IndividualB2C is specified." }, "GenerateApi": { - "type": "computed", - "value": "((IndividualB2CAuth || OrganizationalAuth) && (CalledApiUrl != \"https://graph.microsoft.com/v1.0\" || CalledApiScopes != \"user.read\"))" + "type": "computed", + "value": "((IndividualB2CAuth || OrganizationalAuth) && (CalledApiUrl != \"https://graph.microsoft.com/v1.0\" || CalledApiScopes != \"user.read\"))" }, "GenerateGraph": { - "type": "computed", - "value": "(OrganizationalAuth && CallsMicrosoftGraph)" + "type": "computed", + "value": "(OrganizationalAuth && CallsMicrosoftGraph)" }, "GenerateApiOrGraph": { - "type": "computed", - "value": "(GenerateApi || GenerateGraph)" + "type": "computed", + "value": "(GenerateApi || GenerateGraph)" + }, + "DisableOpenAPI": { + "type": "parameter", + "dataType": "bool", + "defaultValue": "false", + "description": "Disable OpenAPI (Swagger) support" + }, + "EnableOpenAPI": { + "type": "computed", + "value": "(!DisableOpenAPI)" } }, "primaryOutputs": [ @@ -294,4 +304,4 @@ "continueOnError": true } ] -} +} \ No newline at end of file diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/vs-2017.3.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/vs-2017.3.host.json index 9e71ead6d7..e2241ce31a 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/vs-2017.3.host.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/vs-2017.3.host.json @@ -49,6 +49,17 @@ "useHttps": true } ], + "symbolInfo": [ + { + "id": "DisableOpenAPI", + "name": { + "text": "Enable _OpenAPI support" + }, + "invertBoolean": true, + "isVisible": true, + "defaultValue": true + } + ], "excludeLaunchSettings": false, "azureReplyUrlPortName": "HttpsPort", "minFullFrameworkVersion": "4.6.1", diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Properties/launchSettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Properties/launchSettings.json index c01e1f6b98..c346e6c26c 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Properties/launchSettings.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Properties/launchSettings.json @@ -21,7 +21,11 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, + //#if(EnableOpenAPI) + "launchUrl": "swagger", + //#else "launchUrl": "weatherforecast", + //#endif "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -30,7 +34,11 @@ "commandName": "Project", "dotnetRunMessages": "true", "launchBrowser": true, + //#if(EnableOpenAPI) + "launchUrl": "swagger", + //#else "launchUrl": "weatherforecast", + //#endif //#if(RequiresHttps) "applicationUrl": "https://localhost:5001;http://localhost:5000", //#else diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs index 71568e5da0..0e935d107a 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Startup.cs @@ -26,6 +26,10 @@ using Microsoft.Extensions.Logging; #if (GenerateGraph) using Microsoft.Graph; #endif +#if (EnableOpenAPI) +using Microsoft.OpenApi.Models; +#endif + namespace Company.WebApplication1 { public class Startup @@ -71,6 +75,12 @@ namespace Company.WebApplication1 #endif services.AddControllers(); +#if (EnableOpenAPI) + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "Company.WebApplication1", Version = "v1" }); + }); +#endif } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -79,12 +89,15 @@ namespace Company.WebApplication1 if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); + #if (EnableOpenAPI) + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Company.WebApplication1 v1")); + #endif } #if (RequiresHttps) app.UseHttpsRedirection(); #endif - app.UseRouting(); #if (OrganizationalAuth || IndividualAuth) diff --git a/src/ProjectTemplates/test/WebApiTemplateTest.cs b/src/ProjectTemplates/test/WebApiTemplateTest.cs index 6ae0b1d450..3ed7ed94f4 100644 --- a/src/ProjectTemplates/test/WebApiTemplateTest.cs +++ b/src/ProjectTemplates/test/WebApiTemplateTest.cs @@ -39,6 +39,26 @@ namespace Templates.Test [SkipOnHelix("Cert failures", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")] public Task WebApiTemplateCSharp() => WebApiTemplateCore(languageOverride: null); + [ConditionalFact] + [SkipOnHelix("Cert failures", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")] + public async Task WebApiTemplateCSharp_WithoutOpenAPI() + { + Project = await FactoryFixture.GetOrCreateProject("webapinoopenapi", Output); + + var createResult = await Project.RunDotNetNewAsync("webapi", args: new[] { "--no-openapi" }); + Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult)); + + var buildResult = await Project.RunDotNetBuildAsync(); + Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult)); + + using var aspNetProcess = Project.StartBuiltProjectAsync(); + Assert.False( + aspNetProcess.Process.HasExited, + ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process)); + + await aspNetProcess.AssertNotFound("swagger"); + } + private async Task PublishAndBuildWebApiTemplate(string languageOverride, string auth, string[] args) { Project = await FactoryFixture.GetOrCreateProject("webapi" + (languageOverride == "F#" ? "fsharp" : "csharp") + Guid.NewGuid().ToString().Substring(0, 10).ToLower(), Output); @@ -80,6 +100,7 @@ namespace Templates.Test ErrorMessages.GetFailedProcessMessageOrEmpty("Run built project", Project, aspNetProcess.Process)); await aspNetProcess.AssertOk("weatherforecast"); + await aspNetProcess.AssertOk("swagger"); await aspNetProcess.AssertNotFound("/"); } @@ -91,6 +112,8 @@ namespace Templates.Test await aspNetProcess.AssertOk("weatherforecast"); + // Swagger is only available in Development + await aspNetProcess.AssertNotFound("swagger"); await aspNetProcess.AssertNotFound("/"); } }