Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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 this issue Feb 5, 2024 · 10 comments

Comments

@Arghan
Copy link

Arghan commented Feb 5, 2024

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
Copy link

rm-code commented Feb 6, 2024

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

@codingdna2
Copy link
Contributor

Same here

@soligen2010
Copy link

I am having the same problem.

@soligen2010
Copy link

I think this was likely broken by PR #4691

@stevenvolckaert
Copy link

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
Copy link

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
Copy link

stevenvolckaert commented Mar 27, 2024

@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
Copy link

franklixuefei commented Apr 11, 2024

@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
Copy link

tankbob commented Jun 26, 2024

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

@bbdaniell
Copy link

The workaround we're currently using for 14.1.0 is as follows:

in the nswag.json configuration file:

"useBaseUrl": true,
"generateBaseUrlProperty": false,

Then in the same namespace as the generated client class, add the partial:

public partial class GeneratedApiClient
{
    partial void PrepareRequest(HttpClient client, HttpRequestMessage request, StringBuilder urlBuilder)
    {
        urlBuilder.Insert(0, BaseUrl);
    }
}

Its ugly, but insignificantly slower than the old method, in our testing. You do need to manually define and set the BaseUrl property somewhere. We define a base class ("clientBaseClass") with a protected string BaseUrl and set it from the injected configuration class ("configurationClass"), and that works well, but there's probably other solutions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants