diff --git a/docs/generators/csharp-netcore.md b/docs/generators/csharp-netcore.md
index c7935752d018..ac085dc35988 100644
--- a/docs/generators/csharp-netcore.md
+++ b/docs/generators/csharp-netcore.md
@@ -12,11 +12,12 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|
**false**
The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.
**true**
Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.
|true|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
|interfacePrefix|Prefix interfaces with a community standard or widely accepted prefix.| |I|
-|library|HTTP library template (sub-template) to use|
**httpclient**
HttpClient (https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) (Experimental. May subject to breaking changes without further notice.)
|restsharp|
+|library|HTTP library template (sub-template) to use|
**httpclient**
HttpClient (https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) (Experimental. May subject to breaking changes without further notice.)
HttpClient (https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) (Experimental. May subject to breaking changes without further notice.)
|restsharp|
|licenseId|The identifier of the license| |null|
|modelPropertyNaming|Naming convention for the property: 'camelCase', 'PascalCase', 'snake_case' and 'original', which keeps the original name| |PascalCase|
|netCoreProjectFile|Use the new format (.NET Core) for .NET project files (.csproj).| |false|
|nonPublicApi|Generates code with reduced access modifiers; allows embedding elsewhere without exposing non-public API calls to consumers.| |false|
+|nullableReferenceTypes|Set the nullable reference types property to true or false. Default is false.| |false|
|optionalAssemblyInfo|Generate AssemblyInfo.cs.| |true|
|optionalEmitDefaultValues|Set DataMember's EmitDefaultValue.| |false|
|optionalMethodArgument|C# Optional method argument, e.g. void square(int x=10) (.net 4.0+ only).| |true|
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java
index 2e7151dc94e2..4fb0299b5c56 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java
@@ -208,6 +208,9 @@ public class CodegenConstants {
public static final String DOTNET_FRAMEWORK = "targetFramework";
public static final String DOTNET_FRAMEWORK_DESC = "The target .NET framework version.";
+ public static final String NULLABLE_REFERENCE_TYPES = "nullableReferenceTypes";
+ public static final String NULLABLE_REFERENCE_TYPES_DESC = "Set the nullable reference types property to true or false. Default is false.";
+
public static final String TEMPLATING_ENGINE = "templatingEngine";
public static final String TEMPLATING_ENGINE_DESC = "The templating engine plugin to use: \"mustache\" (default) or \"handlebars\" (beta)";
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java
index 2782357482c9..83ef0f315695 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractCSharpCodegen.java
@@ -47,6 +47,7 @@ public abstract class AbstractCSharpCodegen extends DefaultCodegen implements Co
protected boolean useCollection = false;
protected boolean returnICollection = false;
protected boolean netCoreProjectFileFlag = false;
+ protected boolean nullReferenceTypesFlag = false;
protected String modelPropertyNaming = CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.PascalCase.name();
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java
index 2575c4d4d4a8..5c1286d3c18c 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpNetCoreClientCodegen.java
@@ -49,6 +49,7 @@ public class CSharpNetCoreClientCodegen extends AbstractCSharpCodegen {
// HTTP libraries
protected static final String RESTSHARP = "restsharp";
protected static final String HTTPCLIENT = "httpclient";
+ protected static final String HTTPCLIENT_EXPERIMENTAL = "httpclient-experimental";
// Project Variable, determined from target framework. Not intended to be user-settable.
protected static final String TARGET_FRAMEWORK_IDENTIFIER = "targetFrameworkIdentifier";
@@ -230,6 +231,10 @@ public CSharpNetCoreClientCodegen() {
cliOptions.add(modelPropertyNaming.defaultValue("PascalCase"));
// CLI Switches
+ addSwitch(CodegenConstants.NULLABLE_REFERENCE_TYPES,
+ CodegenConstants.NULLABLE_REFERENCE_TYPES_DESC,
+ this.nullReferenceTypesFlag);
+
addSwitch(CodegenConstants.HIDE_GENERATION_TIMESTAMP,
CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC,
this.hideGenerationTimestamp);
@@ -303,6 +308,9 @@ public CSharpNetCoreClientCodegen() {
supportedLibraries.put(HTTPCLIENT, "HttpClient (https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) "
+ "(Experimental. May subject to breaking changes without further notice.)");
supportedLibraries.put(RESTSHARP, "RestSharp (https://github.com/restsharp/RestSharp)");
+ supportedLibraries.put(HTTPCLIENT_EXPERIMENTAL, "HttpClient (https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient) "
+ + "(Experimental. May subject to breaking changes without further notice.)");
+
CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "HTTP library template (sub-template) to use");
libraryOption.setEnum(supportedLibraries);
@@ -555,9 +563,21 @@ public void processOpts() {
* if (additionalProperties.containsKey(prop)) convertPropertyToBooleanAndWriteBack(prop);
*/
+ if (additionalProperties.containsKey(CodegenConstants.NULLABLE_REFERENCE_TYPES)){
+ Object nullableReferenceTypesFlag = additionalProperties.get(CodegenConstants.NULLABLE_REFERENCE_TYPES);
+ if (nullableReferenceTypesFlag == null || nullableReferenceTypesFlag.toString().trim().length() == 0 || nullableReferenceTypesFlag.toString().equals("true")){
+ writePropertyBack(CodegenConstants.NULLABLE_REFERENCE_TYPES, true);
+ this.setNullableReferenceTypes(true);
+ this.nullableType.add("string");
+ }
+ else{
+ writePropertyBack(CodegenConstants.NULLABLE_REFERENCE_TYPES, false);
+ }
+ }
+
if (additionalProperties.containsKey(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT)) {
this.setDisallowAdditionalPropertiesIfNotPresent(Boolean.valueOf(additionalProperties
- .get(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT).toString()));
+ .get(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT).toString()));
}
if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_EMIT_DEFAULT_VALUES)) {
@@ -586,6 +606,9 @@ public void processOpts() {
setLibrary(HTTPCLIENT);
additionalProperties.put("useHttpClient", true);
needsUriBuilder = true;
+ }else if (HTTPCLIENT_EXPERIMENTAL.equals((getLibrary()))){
+ setLibrary(HTTPCLIENT_EXPERIMENTAL);
+ additionalProperties.put("useHttpClientExperimental", true);
} else {
throw new RuntimeException("Invalid HTTP library " + getLibrary() + ". Only restsharp, httpclient are supported.");
}
@@ -725,6 +748,10 @@ public void processOpts() {
additionalProperties.put("modelDocPath", modelDocPath);
}
+ public void setNullableReferenceTypes(Boolean flag){
+ this.nullReferenceTypesFlag = flag;
+ }
+
public void setNetStandard(Boolean netStandard) {
this.netStandard = netStandard;
}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/ApiClient.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/ApiClient.mustache
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/ApiException.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/ApiException.mustache
new file mode 100644
index 000000000000..582e22f5d9e6
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/ApiException.mustache
@@ -0,0 +1,42 @@
+{{>partial_header}}
+
+using System;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// API Exception
+ ///
+ {{>visibility}} class ApiException : Exception
+ {
+ ///
+ /// The reason the api request failed
+ ///
+ public string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ReasonPhrase { get; }
+
+ ///
+ /// The HttpStatusCode
+ ///
+ public System.Net.HttpStatusCode StatusCode { get; }
+
+ ///
+ /// The raw data returned by the api
+ ///
+ public string RawContent { get; }
+
+ ///
+ /// Construct the ApiException from parts of the reponse
+ ///
+ ///
+ ///
+ ///
+ public ApiException(string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} reasonPhrase, System.Net.HttpStatusCode statusCode, string rawContent) : base(reasonPhrase ?? rawContent)
+ {
+ ReasonPhrase = reasonPhrase;
+
+ StatusCode = statusCode;
+
+ RawContent = rawContent;
+ }
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/ApiResponse.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/ApiResponse.mustache
new file mode 100644
index 000000000000..ba1d71bf70a3
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/ApiResponse.mustache
@@ -0,0 +1,104 @@
+{{>partial_header}}
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using Newtonsoft.Json;
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Provides a non-generic contract for the ApiResponse wrapper.
+ ///
+ public interface IApiResponse
+ {
+ ///
+ /// The data type of
+ ///
+ Type ResponseType { get; }
+
+ ///
+ /// Gets or sets the status code (HTTP status code)
+ ///
+ /// The status code.
+ HttpStatusCode StatusCode { get; }
+
+ ///
+ /// Gets or sets any cookies passed along on the response.
+ ///
+ List Cookies { get; set; }
+
+ ///
+ /// The raw content of this response
+ ///
+ string RawContent { get; }
+ }
+
+ ///
+ /// API Response
+ ///
+ {{>visibility}} partial class ApiResponse : IApiResponse
+ {
+ #region Properties
+
+ ///
+ /// The deserialized content
+ ///
+ public T{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} Content { get; set; }
+
+ ///
+ /// Gets or sets the status code (HTTP status code)
+ ///
+ /// The status code.
+ public HttpStatusCode StatusCode { get; }
+
+ ///
+ /// Gets or sets any cookies passed along on the response.
+ ///
+ public List Cookies { get; set; }
+
+ ///
+ /// The content of this response
+ ///
+ public Type ResponseType
+ {
+ get { return typeof(T); }
+ }
+
+ ///
+ /// The raw data
+ ///
+ public string RawContent { get; }
+
+ ///
+ /// The IsSuccessStatusCode from the api response
+ ///
+ public bool IsSuccessStatusCode { get; }
+
+ ///
+ /// The reason phrase contained in the api response
+ ///
+ public string{{#nullableReferenceTypes}}?{{/nullableReferenceTypes}} ReasonPhrase { get; }
+
+ ///
+ /// The headers contained in the api response
+ ///
+ public System.Net.Http.Headers.HttpResponseHeaders Headers { get; }
+
+ #endregion Properties
+
+ ///
+ /// Construct the reponse using an HttpResponseMessage
+ ///
+ ///
+ ///
+ public ApiResponse(System.Net.Http.HttpResponseMessage response, string rawContent)
+ {
+ StatusCode = response.StatusCode;
+ Headers = response.Headers;
+ IsSuccessStatusCode = response.IsSuccessStatusCode;
+ ReasonPhrase = response.ReasonPhrase;
+ RawContent = rawContent;
+ }
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/AssemblyInfo.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/AssemblyInfo.mustache
new file mode 100644
index 000000000000..c5d19bfd2357
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/AssemblyInfo.mustache
@@ -0,0 +1,37 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("{{packageTitle}}")]
+[assembly: AssemblyDescription("{{packageDescription}}")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("{{packageCompany}}")]
+[assembly: AssemblyProduct("{{packageProductName}}")]
+[assembly: AssemblyCopyright("{{packageCopyright}}")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("{{packageVersion}}")]
+[assembly: AssemblyFileVersion("{{packageVersion}}")]
+{{^supportsAsync}}
+[assembly: InternalsVisibleTo("NewtonSoft.Json")]
+[assembly: InternalsVisibleTo("JsonSubTypes")]
+{{/supportsAsync}}
diff --git a/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/ClientUtils.mustache b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/ClientUtils.mustache
new file mode 100644
index 000000000000..684f79e66524
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/csharp-netcore/libraries/httpclient-experimental/ClientUtils.mustache
@@ -0,0 +1,245 @@
+{{>partial_header}}
+
+using System;
+using System.Collections;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+{{#useCompareNetObjects}}
+using KellermanSoftware.CompareNetObjects;
+{{/useCompareNetObjects}}
+
+namespace {{packageName}}.Client
+{
+ ///
+ /// Utility functions providing some benefit to API client consumers.
+ ///
+ public static class ClientUtils
+ {
+ {{#useCompareNetObjects}}
+ ///
+ /// An instance of CompareLogic.
+ ///
+ public static CompareLogic compareLogic;
+
+ ///
+ /// Static contstructor to initialise compareLogic.
+ ///
+ static ClientUtils()
+ {
+ compareLogic = new CompareLogic();
+ }
+ {{/useCompareNetObjects}}
+
+ ///
+ /// Custom JSON serializer
+ ///
+ public static readonly Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings = new Newtonsoft.Json.JsonSerializerSettings
+ {
+ // OpenAPI generated types generally hide default constructors.
+ ConstructorHandling = Newtonsoft.Json.ConstructorHandling.AllowNonPublicDefaultConstructor,
+ MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Error,
+ ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver
+ {
+ NamingStrategy = new Newtonsoft.Json.Serialization.CamelCaseNamingStrategy
+ {
+ OverrideSpecifiedNames = false
+ }
+ }
+ };
+
+ ///
+ /// Sanitize filename by removing the path
+ ///
+ /// Filename
+ /// Filename
+ public static string SanitizeFilename(string filename)
+ {
+ Match match = Regex.Match(filename, @".*[/\\](.*)$");
+ return match.Success ? match.Groups[1].Value : filename;
+ }
+
+ ///
+ /// Convert params to key/value pairs.
+ /// Use collectionFormat to properly format lists and collections.
+ ///
+ /// The swagger-supported collection format, one of: csv, tsv, ssv, pipes, multi
+ /// Key name.
+ /// Value object.
+ /// A multimap of keys with 1..n associated values.
+ public static Multimap ParameterToMultiMap(string collectionFormat, string name, object value)
+ {
+ var parameters = new Multimap();
+
+ if (value is ICollection collection && collectionFormat == "multi")
+ {
+ foreach (var item in collection)
+ {
+ parameters.Add(name, ParameterToString(item));
+ }
+ }
+ else
+ {
+ parameters.Add(name, ParameterToString(value));
+ }
+
+ return parameters;
+ }
+
+ ///
+ /// If parameter is DateTime, output in a formatted string (default ISO 8601), customizable with Configuration.DateTime.
+ /// If parameter is a list, join the list with ",".
+ /// Otherwise just return the string.
+ ///
+ /// The parameter (header, path, query, form).
+ /// An optional configuration instance, providing formatting options used in processing.
+ /// Formatted string.
+ public static string ParameterToString(object obj, IReadableConfiguration configuration = null)
+ {
+ throw new NotImplementedException();
+
+ /*
+ if (obj is DateTime dateTime)
+ // Return a formatted date string - Can be customized with Configuration.DateTimeFormat
+ // Defaults to an ISO 8601, using the known as a Round-trip date/time pattern ("o")
+ // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8
+ // For example: 2009-06-15T13:45:30.0000000
+ return dateTime.ToString((configuration ?? GlobalConfiguration.Instance).DateTimeFormat);
+ if (obj is DateTimeOffset dateTimeOffset)
+ // Return a formatted date string - Can be customized with Configuration.DateTimeFormat
+ // Defaults to an ISO 8601, using the known as a Round-trip date/time pattern ("o")
+ // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8
+ // For example: 2009-06-15T13:45:30.0000000
+ return dateTimeOffset.ToString((configuration ?? GlobalConfiguration.Instance).DateTimeFormat);
+ if (obj is bool boolean)
+ return boolean ? "true" : "false";
+ if (obj is ICollection collection)
+ return string.Join(",", collection.Cast