Skip to content

Commit

Permalink
Update the Asp.Net core server generator to support Asp.net Core 2.1 (O…
Browse files Browse the repository at this point in the history
…penAPITools#1008)

* Update the .net core sdk to v2.1 and update the asp.net packages used.

* Upgrade the SwashBuckle Asp.Net Core package to v3.0.0.

* Update the AppSettings json file and add a file used in development.
Also, remove the web.config file.

* Update the program template to use the web host builder class.

* Update the startup class to use the Asp.Net 2.1 paradigms.

* Update the launch settings json file.

* Update the controller template to derive from the ControllerBase class as aposed to Controller.

* Add the SwashBuckle annotations package.

* Add the SwashBuckle.AspNetCore.Annotations namespace to the controller template.

* Update the Startup template to add comments to the Configuration property and an env parameter to the Configure method.

* Update the startup class so we don't need to inject the hosting environment.

* Update the program class to have hte recommended CreateWebHostBuilder method from asp.net core 2.1.

* Update the asp.net core pet store sample server.

* add back aspnetcore 2.0 template via option

* remove web.config for aspnet core 2.1
  • Loading branch information
SeanFarrow authored and jaumard committed Sep 21, 2018
1 parent 8b338b0 commit 645a35e
Show file tree
Hide file tree
Showing 75 changed files with 1,098 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.IllegalArgumentException;
import java.net.URL;
import java.util.Arrays;
import java.util.Locale;
Expand All @@ -38,6 +39,7 @@
public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {

public static final String USE_SWASHBUCKLE = "useSwashbuckle";
public static final String ASPNET_CORE_VERSION = "aspnetCoreVersion";

private String packageGuid = "{" + randomUUID().toString().toUpperCase(Locale.ROOT) + "}";

Expand All @@ -47,7 +49,7 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
private boolean useSwashbuckle = true;
protected int serverPort = 8080;
protected String serverHost = "0.0.0.0";

protected String aspnetCoreVersion= "2.1"; // default to 2.1

public AspNetCoreServerCodegen() {
super();
Expand All @@ -57,6 +59,8 @@ public AspNetCoreServerCodegen() {
modelTemplateFiles.put("model.mustache", ".cs");
apiTemplateFiles.put("controller.mustache", ".cs");

embeddedTemplateDir = templateDir = "aspnetcore/2.1";

// contextually reserved words
// NOTE: C# uses camel cased reserved words, while models are title cased. We don't want lowercase comparisons.
reservedWords.addAll(
Expand All @@ -82,6 +86,10 @@ public AspNetCoreServerCodegen() {
CodegenConstants.SOURCE_FOLDER_DESC,
sourceFolder);

addOption(ASPNET_CORE_VERSION,
"ASP.NET Core version: 2.1 (default), 2.0 (deprecated)",
aspnetCoreVersion);

// CLI Switches
addSwitch(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG,
CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG_DESC,
Expand All @@ -102,6 +110,7 @@ public AspNetCoreServerCodegen() {
addSwitch(USE_SWASHBUCKLE,
"Uses the Swashbuckle.AspNetCore NuGet package for documentation.",
useSwashbuckle);

}

@Override
Expand All @@ -118,6 +127,7 @@ public String getName() {
public String getHelp() {
return "Generates an ASP.NET Core Web API server.";
}

@Override
public void preprocessOpenAPI(OpenAPI openAPI) {
super.preprocessOpenAPI(openAPI);
Expand All @@ -141,13 +151,29 @@ public void processOpts() {
additionalProperties.put(USE_SWASHBUCKLE, useSwashbuckle);
}

// determine the ASP.NET core version setting
if (additionalProperties.containsKey(ASPNET_CORE_VERSION)) {
setAspnetCoreVersion((String) additionalProperties.get(ASPNET_CORE_VERSION));
}

additionalProperties.put("dockerTag", packageName.toLowerCase(Locale.ROOT));

apiPackage = packageName + ".Controllers";
modelPackage = packageName + ".Models";

String packageFolder = sourceFolder + File.separator + packageName;

if ("2.0".equals(aspnetCoreVersion)) {
embeddedTemplateDir = templateDir = "aspnetcore/2.0";
supportingFiles.add(new SupportingFile("web.config", packageFolder, "web.config"));
LOGGER.info("ASP.NET core version: 2.0");
} else if ("2.1".equals(aspnetCoreVersion)) {
// default, do nothing
LOGGER.info("ASP.NET core version: 2.1");
} else {
throw new IllegalArgumentException("aspnetCoreVersion must be '2.1', '2.0' but found " + aspnetCoreVersion);
}

supportingFiles.add(new SupportingFile("build.sh.mustache", "", "build.sh"));
supportingFiles.add(new SupportingFile("build.bat.mustache", "", "build.bat"));
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
Expand All @@ -159,28 +185,34 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("Startup.mustache", packageFolder, "Startup.cs"));
supportingFiles.add(new SupportingFile("Program.mustache", packageFolder, "Program.cs"));
supportingFiles.add(new SupportingFile("validateModel.mustache", packageFolder + File.separator + "Attributes", "ValidateModelStateAttribute.cs"));
supportingFiles.add(new SupportingFile("web.config", packageFolder, "web.config"));

supportingFiles.add(new SupportingFile("Project.csproj.mustache", packageFolder, packageName + ".csproj"));

supportingFiles.add(new SupportingFile("Properties" + File.separator + "launchSettings.json", packageFolder + File.separator + "Properties", "launchSettings.json"));
supportingFiles.add(new SupportingFile("Properties" + File.separator + "launchSettings.json",
packageFolder + File.separator + "Properties", "launchSettings.json"));

if (useSwashbuckle) {
supportingFiles.add(new SupportingFile("Filters" + File.separator + "BasePathFilter.mustache", packageFolder + File.separator + "Filters", "BasePathFilter.cs"));
supportingFiles.add(new SupportingFile("Filters" + File.separator + "GeneratePathParamsValidationFilter.mustache", packageFolder + File.separator + "Filters", "GeneratePathParamsValidationFilter.cs"));
supportingFiles.add(new SupportingFile("Filters" + File.separator + "BasePathFilter.mustache",
packageFolder + File.separator + "Filters", "BasePathFilter.cs"));
supportingFiles.add(new SupportingFile("Filters" + File.separator + "GeneratePathParamsValidationFilter.mustache",
packageFolder + File.separator + "Filters", "GeneratePathParamsValidationFilter.cs"));
}

supportingFiles.add(new SupportingFile("wwwroot" + File.separator + "README.md", packageFolder + File.separator + "wwwroot", "README.md"));
supportingFiles.add(new SupportingFile("wwwroot" + File.separator + "index.html", packageFolder + File.separator + "wwwroot", "index.html"));
supportingFiles.add(new SupportingFile("wwwroot" + File.separator + "web.config", packageFolder + File.separator + "wwwroot", "web.config"));

supportingFiles.add(new SupportingFile("wwwroot" + File.separator + "openapi-original.mustache", packageFolder + File.separator + "wwwroot", "openapi-original.json"));
supportingFiles.add(new SupportingFile("wwwroot" + File.separator + "openapi-original.mustache",
packageFolder + File.separator + "wwwroot", "openapi-original.json"));
}

public void setPackageGuid(String packageGuid) {
this.packageGuid = packageGuid;
}

public void setAspnetCoreVersion(String aspnetCoreVersion) {
this.aspnetCoreVersion= aspnetCoreVersion;
}

@Override
public String apiFileFolder() {
return outputFolder + File.separator + sourceFolder + File.separator + packageName + File.separator + "Controllers";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM microsoft/aspnetcore-build:2.0 AS build-env
WORKDIR /app

ENV DOTNET_CLI_TELEMETRY_OPTOUT 1

# copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore

# copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out

# build runtime image
FROM microsoft/aspnetcore:2.0
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "{{packageName}}.dll"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Linq;
using System.Text.RegularExpressions;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace {{packageName}}.Filters
{
/// <summary>
/// BasePath Document Filter sets BasePath property of Swagger and removes it from the individual URL paths
/// </summary>
public class BasePathFilter : IDocumentFilter
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="basePath">BasePath to remove from Operations</param>
public BasePathFilter(string basePath)
{
BasePath = basePath;
}

/// <summary>
/// Gets the BasePath of the Swagger Doc
/// </summary>
/// <returns>The BasePath of the Swagger Doc</returns>
public string BasePath { get; }

/// <summary>
/// Apply the filter
/// </summary>
/// <param name="swaggerDoc">SwaggerDocument</param>
/// <param name="context">FilterContext</param>
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
swaggerDoc.BasePath = BasePath;
var pathsToModify = swaggerDoc.Paths.Where(p => p.Key.StartsWith(BasePath)).ToList();
foreach (var path in pathsToModify)
{
if (path.Key.StartsWith(BasePath))
{
string newKey = Regex.Replace(path.Key, $"^{BasePath}", string.Empty);
swaggerDoc.Paths.Remove(path.Key);
swaggerDoc.Paths.Add(newKey, path.Value);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Controllers;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace {{packageName}}.Filters
{
/// <summary>
/// Path Parameter Validation Rules Filter
/// </summary>
public class GeneratePathParamsValidationFilter : IOperationFilter
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="operation">Operation</param>
/// <param name="context">OperationFilterContext</param>
public void Apply(Operation operation, OperationFilterContext context)
{
var pars = context.ApiDescription.ParameterDescriptions;
foreach (var par in pars)
{
var swaggerParam = operation.Parameters.SingleOrDefault(p => p.Name == par.Name);
var attributes = ((ControllerParameterDescriptor)par.ParameterDescriptor).ParameterInfo.CustomAttributes;
if (attributes != null && attributes.Count() > 0 && swaggerParam != null)
{
// Required - [Required]
var requiredAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(RequiredAttribute));
if (requiredAttr != null)
{
swaggerParam.Required = true;
}

// Regex Pattern [RegularExpression]
var regexAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(RegularExpressionAttribute));
if (regexAttr != null)
{
string regex = (string)regexAttr.ConstructorArguments[0].Value;
if (swaggerParam is NonBodyParameter)
{
((NonBodyParameter)swaggerParam).Pattern = regex;
}
}

// String Length [StringLength]
int? minLenght = null, maxLength = null;
var stringLengthAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(StringLengthAttribute));
if (stringLengthAttr != null)
{
if (stringLengthAttr.NamedArguments.Count == 1)
{
minLenght = (int)stringLengthAttr.NamedArguments.Single(p => p.MemberName == "MinimumLength").TypedValue.Value;
}
maxLength = (int)stringLengthAttr.ConstructorArguments[0].Value;
}

var minLengthAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(MinLengthAttribute));
if (minLengthAttr != null)
{
minLenght = (int)minLengthAttr.ConstructorArguments[0].Value;
}

var maxLengthAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(MaxLengthAttribute));
if (maxLengthAttr != null)
{
maxLength = (int)maxLengthAttr.ConstructorArguments[0].Value;
}

if (swaggerParam is NonBodyParameter)
{
((NonBodyParameter)swaggerParam).MinLength = minLenght;
((NonBodyParameter)swaggerParam).MaxLength = maxLength;
}

// Range [Range]
var rangeAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(RangeAttribute));
if (rangeAttr != null)
{
int rangeMin = (int)rangeAttr.ConstructorArguments[0].Value;
int rangeMax = (int)rangeAttr.ConstructorArguments[1].Value;
if (swaggerParam is NonBodyParameter)
{
((NonBodyParameter)swaggerParam).Minimum = rangeMin;
((NonBodyParameter)swaggerParam).Maximum = rangeMax;
}
}
}
}
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore;

namespace {{packageName}}
{
/// <summary>
/// Program
/// </summary>
public class Program
{
/// <summary>
/// Main
/// </summary>
/// <param name="args"></param>
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}

/// <summary>
/// Create the web host builder.
/// </summary>
/// <param name="args"></param>
/// <returns>IWebHostBuilder</returns>
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseUrls("http://0.0.0.0:{{#serverPort}}{{serverPort}}{{/serverPort}}{{^serverPort}}8080{{/serverPort}}/");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Description>{{packageName}}</Description>
<Copyright>{{packageName}}</Copyright>
<TargetFramework>netcoreapp2.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PreserveCompilationContext>true</PreserveCompilationContext>
<AssemblyName>{{packageName}}</AssemblyName>
<PackageId>{{packageName}}</PackageId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
{{#useSwashbuckle}}
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0"/>
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="3.0.0" />
{{/useSwashbuckle}}
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.3" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:61788",
"sslPort": 44301
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"WebApplication1": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/values",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Loading

0 comments on commit 645a35e

Please sign in to comment.