RicoSuter / NSwag

The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
http://NSwag.org
MIT License
6.62k stars 1.23k forks source link

Csharp Client : Base URL from base class is not used in version 14.0.3, it was used in 14.0.2 #4764

Open Arghan opened 5 months ago

Arghan commented 5 months ago

Hello,

I think there is an issue with the version 14.0.3, the BaseUrl property from the base client is not used anymore :

Here are the settings I am using :

image

Generated code version 14.0.2 : It uses the BaseUrl property from the base class

var urlBuilder_ = new System.Text.StringBuilder();
if (!string.IsNullOrEmpty(BaseUrl)) urlBuilder_.Append(BaseUrl);
// Operation Path: "api/Consignee"
urlBuilder_.Append("api/Consignee");

Generated code version 14.0.3 : It uses the private field _baseUrl

var urlBuilder_ = new System.Text.StringBuilder();
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
// Operation Path: "api/Consignee"
urlBuilder_.Append("api/Consignee");

Have a nice day.

rm-code commented 5 months ago

Just encountered the same issue after upgrading. Reverted back to the earlier version for now.

codingdna2 commented 5 months ago

Same here

soligen2010 commented 5 months ago

I am having the same problem.

soligen2010 commented 5 months ago

I think this was likely broken by PR #4691

stevenvolckaert commented 4 months ago

I have the same issue. We're using codeGenerators.openApiToCSharpClient.clientBaseClass in nswag.json to specify a base class for each client. The base class contains a BaseUrl property.

This works with all versions up until 14.0.0, but not with 14.0.1, 14.0.2, or 14.0.3.

soligen2010 commented 3 months ago

I think the following changes will fix the issue, but I am not familiar with how this project is built to test it. Can someone test this and submit a pull request if it works?

Change file src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.liquid

Change line 5 from

{% if UseBaseUrl -%}

to

{% if UseBaseUrl and !(HasConfigurationClass == true and GenerateBaseUrlProperty == false) -%}

and change line 292 from

{% if UseBaseUrl %}if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);{% endif %}

to

{% if HasConfigurationClass == true and GenerateBaseUrlProperty == false -%}
    if (!string.IsNullOrEmpty(BaseUrl)) urlBuilder_.Append(BaseUrl);
{% else -%}
    if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
{% endif -%}
stevenvolckaert commented 3 months ago

@RicoSuter I tried NSwag.MSBuild.14.0.7 which was just released. This version produces the same issue: BaseUrl from a base class is not used any more.

My nswag.json below; I'm using openApiToCSharpClient to generate the C# code to call my REST API.

Would there be any workaround?

I'm currently using PrepareRequest(HttpClient, HttpRequestMessage, string) to set the HttpClient.BaseAddress property (HttpClientSettings is my configuration class, specified by codeGenerators.openApiToCSharpClient.configurationClass); is this the recommended way to set the base URL in NSwag v14?

public partial class SessionEndpointsClient
{
    partial void PrepareRequest(HttpClient client, HttpRequestMessage request, string url)
    {
        client.BaseAddress = new Uri(this.HttpClientSettings.BaseUrl);
    }

Edit: I'm now configuring the HttpClient when I register it with the IServiceCollection:

serviceCollection
    .AddTransient<HttpClientSettings>(serviceProvider =>
        configuration.GetSection(HttpClientSettings.FullTypeName).Get<HttpClientSettings>()!
    );

serviceCollection
    .AddHttpClient(
        name: HttpClientSettings.HttpClientName,
        configureClient: (serviceProvider, httpClient) =>
        {
            var httpClientSettings = serviceProvider.GetRequiredService<HttpClientSettings>();
            httpClient.BaseAddress = new Uri(httpClientSettings.BaseUrl);
            httpClient.Timeout = TimeSpan.FromSeconds(httpClientSettings.TimeoutInSeconds);
            // ...
        }
    );

serviceCollection.AddHttpClient<SessionEndpointsClient>(HttpClientSettings.HttpClientName);

With this central configuration, implementing PrepareRequest(HttpClient, HttpRequestMessage, string) in every client is no longer necessary.

This is good enough for us, however if anyone has other ideas, I'd be curious to read about them.

nswag.json below

{
  "runtime": "Net70",
  "defaultVariables": null,
  "documentGenerator": {
    "aspNetCoreToOpenApi": {
      "project": "../Skarabee.DocumentSigning.CoreService/Skarabee.DocumentSigning.CoreService.csproj",
      "msBuildProjectExtensionsPath": null,
      "configuration": null,
      "runtime": null,
      "targetFramework": null,
      "noBuild": true,
      "msBuildOutputPath": null,
      "verbose": true,
      "workingDirectory": null,
      "requireParametersWithoutDefault": true,
      "apiGroupNames": null,
      "defaultPropertyNameHandling": "Default",
      "defaultReferenceTypeNullHandling": "Null",
      "defaultDictionaryValueReferenceTypeNullHandling": "NotNull",
      "defaultResponseReferenceTypeNullHandling": "NotNull",
      "generateOriginalParameterNames": true,
      "defaultEnumHandling": "Integer",
      "flattenInheritanceHierarchy": true,
      "generateKnownTypes": true,
      "generateEnumMappingDescription": false,
      "generateXmlObjects": false,
      "generateAbstractProperties": false,
      "generateAbstractSchemas": false,
      "ignoreObsoleteProperties": false,
      "allowReferencesWithProperties": false,
      "useXmlDocumentation": true,
      "resolveExternalXmlDocumentation": true,
      "excludedTypeNames": [
        "Exception"
      ],
      "serviceHost": null,
      "serviceBasePath": null,
      "serviceSchemes": [],
      "infoTitle": "My Title",
      "infoDescription": null,
      "infoVersion": "1.0.0",
      "documentTemplate": null,
      "documentProcessorTypes": [],
      "operationProcessorTypes": [],
      "typeNameGeneratorType": null,
      "schemaNameGeneratorType": null,
      "contractResolverType": null,
      "serializerSettingsType": null,
      "useDocumentProvider": true,
      "documentName": "v1",
      "aspNetCoreEnvironment": null,
      "createWebHostBuilderMethod": null,
      "startupType": null,
      "allowNullableBodyParameters": true,
      "useHttpAttributeNameAsOperationId": false,
      "output": null,
      "outputType": "OpenApi3",
      "newLineBehavior": "Auto",
      "assemblyPaths": [],
      "assemblyConfig": null,
      "referencePaths": [],
      "useNuGetCache": false
    }
  },
  "codeGenerators": {
    "openApiToCSharpClient": {
      "clientBaseClass": "HttpServiceClientBase<HttpClientSettings>",
      "configurationClass": "HttpClientSettings",
      "generateClientClasses": true,
      "generateClientInterfaces": false,
      "clientBaseInterface": null,
      "injectHttpClient": true,
      "disposeHttpClient": false,
      "protectedMethods": [
        "ProviderEndpointsClient.GetProviderAsync"
      ],
      "generateExceptionClasses": true,
      "exceptionClass": "HttpServiceException",
      "wrapDtoExceptions": true,
      "useHttpClientCreationMethod": false,
      "httpClientType": "System.Net.Http.HttpClient",
      "useHttpRequestMessageCreationMethod": true,
      "useBaseUrl": true,
      "generateBaseUrlProperty": false,
      "generateSyncMethods": true,
      "generatePrepareRequestAndProcessResponseAsAsyncMethods": false,
      "exposeJsonSerializerSettings": false,
      "clientClassAccessModifier": "public",
      "typeAccessModifier": "public",
      "generateContractsOutput": false,
      "contractsNamespace": null,
      "contractsOutputFilePath": null,
      "parameterDateTimeFormat": "o",
      "parameterDateFormat": "yyyy-MM-dd",
      "generateUpdateJsonSerializerSettingsMethod": true,
      "useRequestAndResponseSerializationSettings": false,
      "serializeTypeInformation": false,
      "queryNullValue": "",
      "className": "{controller}Client",
      "operationGenerationMode": "MultipleClientsFromOperationId",
      "additionalNamespaceUsages": [
        "System"
      ],
      "additionalContractNamespaceUsages": [],
      "generateOptionalParameters": false,
      "generateJsonMethods": false,
      "enforceFlagEnums": false,
      "parameterArrayType": "System.Collections.Generic.IEnumerable",
      "parameterDictionaryType": "System.Collections.Generic.IDictionary",
      "responseArrayType": "System.Collections.Generic.IList",
      "responseDictionaryType": "System.Collections.Generic.IDictionary",
      "wrapResponses": false,
      "wrapResponseMethods": [],
      "generateResponseClasses": true,
      "responseClass": "HttpServiceResponse",
      "namespace": "Skarabee.DocumentSigning.CoreService",
      "requiredPropertiesMustBeDefined": true,
      "dateType": "System.DateTimeOffset",
      "jsonConverters": null,
      "anyType": "object",
      "dateTimeType": "System.DateTimeOffset",
      "timeType": "System.TimeSpan",
      "timeSpanType": "System.TimeSpan",
      "arrayType": "System.Collections.Generic.IList",
      "arrayInstanceType": "System.Collections.Generic.List",
      "dictionaryType": "System.Collections.Generic.IDictionary",
      "dictionaryInstanceType": "System.Collections.Generic.Dictionary",
      "arrayBaseType": "System.Collections.Generic.List",
      "dictionaryBaseType": "System.Collections.Generic.Dictionary",
      "classStyle": "Poco",
      "jsonLibrary": "NewtonsoftJson",
      "generateDefaultValues": true,
      "generateDataAnnotations": true,
      "excludedTypeNames": [
        "Exception"
      ],
      "excludedParameterNames": [],
      "handleReferences": false,
      "generateImmutableArrayProperties": false,
      "generateImmutableDictionaryProperties": false,
      "jsonSerializerSettingsTransformationMethod": null,
      "inlineNamedArrays": false,
      "inlineNamedDictionaries": false,
      "inlineNamedTuples": true,
      "inlineNamedAny": false,
      "generateDtoTypes": true,
      "generateOptionalPropertiesAsNullable": false,
      "generateNullableReferenceTypes": false,
      "templateDirectory": null,
      "typeNameGeneratorType": null,
      "propertyNameGeneratorType": null,
      "enumNameGeneratorType": null,
      "serviceHost": null,
      "serviceSchemes": null,
      "output": "Skarabee.DocumentSigning.CoreService.Clients.g.cs",
      "newLineBehavior": "Auto"
    }
  }
}
franklixuefei commented 3 months ago

@RicoSuter We are also hitting the same. If there is a new way to dynamically specify the BaseUrl, please let us know. It is currently blocking us from bumping up to 14.0.7 in our projects. Thanks!

tankbob commented 2 weeks ago

14.0.8 still also has a big problem with the constructor logic.

If you are using BaseUrl property but you are also using a configuration class the generated code for the constructor comes out like this.

public MyClass (MyConfig configuration) : base(configuration)
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
    {
        BaseUrl = baseUrl;
    }

It still runs the line to assign the baseUrl property but its not included in the constructor arguments. This results in non-compilable code. The constructor arguments generator needs the same conditions as the code that outputs the assignment line