RicoSuter / NSwag

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

NSwag Gen 14.0.2 - FromQuery not serializing properly #4749

Open gogo199432 opened 6 months ago

gogo199432 commented 6 months ago

I'm trying to send a complex object to my GET Endpoint, however when I let it generate the client, it seems to try to send the name of the object?

The DTO:

public class FilterByMetadataDto
 {
     public string? Title { get; set; }
     public DateTime? PublishedAt { get; set; }
     public IEnumerable<string>? AuthorIds { get; set; }
     public IEnumerable<Guid>? TagGuids { get; set; }
     public bool? IsComplete { get; set; }

     [JsonIgnore]
     public bool IsEmpty => Title == null && PublishedAt == null && AuthorIds == null && TagGuids == null && IsComplete == null;

     public static bool TryParse(string? input, out FilterByMetadataDto result)
     {
       // Implemented TryParse. Not pasting it for brevity
     }
}

My Endpoint:

var bookApi = app.MapGroup("/book").WithTags("Book");
bookApi.MapGet("/{page:int}", GetBooks).WithOpenApi();
....
private static async Task<Results<Ok<IEnumerable<ReturnBookDto>>, BadRequest>> GetBooks(IBookService bookService, [FromQuery] FilterByMetadataDto? filter, int page,CancellationToken cancellationToken)
{
    return await bookService.GetBooks(page,filter,cancellationToken) is var response
        ? TypedResults.Ok(response)
        : TypedResults.BadRequest();
}

The urlBuilder in the generated C# Client:

urlBuilder_.Append("book/");
urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(page, System.Globalization.CultureInfo.InvariantCulture)));
urlBuilder_.Append('?');
if (filter != null)
{
    urlBuilder_.Append(System.Uri.EscapeDataString("filter")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(filter, System.Globalization.CultureInfo.InvariantCulture))).Append('&');
}
urlBuilder_.Length--;

My nswag.json

{
  "runtime": "Net80",
  "defaultVariables": null,
  "documentGenerator": {
    "aspNetCoreToOpenApi": {
      "project": "StoryService.csproj",
      "documentName": "v1",
      "msBuildProjectExtensionsPath": null,
      "configuration": null,
      "runtime": null,
      "targetFramework": null,
      "noBuild": true,
      "msBuildOutputPath": null,
      "verbose": true,
      "workingDirectory": null,
      "aspNetCoreEnvironment": null,
      "output": "openapi.json",
      "newLineBehavior": "Auto"
    }
  },
  "codeGenerators": {
    "openApiToCSharpClient": {
      "clientBaseClass": null,
      "configurationClass": null,
      "generateClientClasses": true,
      "suppressClientClassesOutput": false,
      "generateClientInterfaces": false,
      "suppressClientInterfacesOutput": false,
      "clientBaseInterface": null,
      "injectHttpClient": true,
      "disposeHttpClient": true,
      "protectedMethods": [],
      "generateExceptionClasses": true,
      "exceptionClass": "ApiException",
      "wrapDtoExceptions": true,
      "useHttpClientCreationMethod": false,
      "httpClientType": "System.Net.Http.HttpClient",
      "useHttpRequestMessageCreationMethod": false,
      "useBaseUrl": true,
      "generateBaseUrlProperty": true,
      "generateSyncMethods": false,
      "generatePrepareRequestAndProcessResponseAsAsyncMethods": false,
      "exposeJsonSerializerSettings": true,
      "clientClassAccessModifier": "public",
      "typeAccessModifier": "public",
      "propertySetterAccessModifier": "",
      "generateNativeRecords": false,
      "generateContractsOutput": false,
      "contractsNamespace": null,
      "contractsOutputFilePath": null,
      "parameterDateTimeFormat": "s",
      "parameterDateFormat": "yyyy-MM-dd",
      "generateUpdateJsonSerializerSettingsMethod": true,
      "useRequestAndResponseSerializationSettings": false,
      "serializeTypeInformation": false,
      "queryNullValue": "",
      "className": "StoryServiceClient",
      "operationGenerationMode": "MultipleClientsFromOperationId",
      "additionalNamespaceUsages": [],
      "additionalContractNamespaceUsages": [],
      "generateOptionalParameters": false,
      "generateJsonMethods": false,
      "enforceFlagEnums": false,
      "parameterArrayType": "System.Collections.Generic.IEnumerable",
      "parameterDictionaryType": "System.Collections.Generic.IDictionary",
      "responseArrayType": "System.Collections.Generic.ICollection",
      "responseDictionaryType": "System.Collections.Generic.IDictionary",
      "wrapResponses": false,
      "wrapResponseMethods": [],
      "generateResponseClasses": true,
      "responseClass": "SwaggerResponse",
      "namespace": "Frontend.Clients.StoryService",
      "requiredPropertiesMustBeDefined": true,
      "dateType": "System.Date",
      "jsonConverters": null,
      "anyType": "object",
      "dateTimeType": "System.DateTime",
      "timeType": "System.TimeSpan",
      "timeSpanType": "System.TimeSpan",
      "arrayType": "System.Collections.Generic.ICollection",
      "arrayInstanceType": "System.Collections.ObjectModel.Collection",
      "dictionaryType": "System.Collections.Generic.IDictionary",
      "dictionaryInstanceType": "System.Collections.Generic.Dictionary",
      "arrayBaseType": "System.Collections.ObjectModel.Collection",
      "dictionaryBaseType": "System.Collections.Generic.Dictionary",
      "classStyle": "Poco",
      "jsonLibrary": "NewtonsoftJson",
      "generateDefaultValues": true,
      "generateDataAnnotations": true,
      "excludedTypeNames": [],
      "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,
      "serviceHost": null,
      "serviceSchemes": null,
      "output": "../Frontend/Clients/StoryServiceClient.cs",
      "newLineBehavior": "Auto"
    }
  }
}

On a related note the swagger ui seems to behave slightly differently. There I can input the properties of the DTO object as expected, however when it sends it, there is no identifier after the ? in the curl statement. But if I add an extra "filter=" AspNet seems to recognize that this should be parsed.

(I'm using AspNetCore 8.0.1)

Is this still teething issue with the 14.0 Release? Or am I doing something wrong?