13 Answer(s)
-
0
Hi @bdadmin
We haven't faced such problem. Could you also share your
Swashbuckle.AspNetCore
version ?Thanks,
-
0
I'm having the same issue. Should optional parameters accept null value?
Thanks,
-
0
hi @morindo The zero demo project should not have the problem you mentioned. Can you compare the relevant code and package version?
-
0
Thanks @maliming for the quick reply. So, for Swashbuckle.AspNetCore nuget package the version is 5.0.0-rc4. Just so you know, it's a new solution, started from ASP.NET Zero (Angular) version 8.1.0 and I didn't update any nuget or npm packages.
Here is my service.config.nswag configuration file.
{ "runtime": "Default", "defaultVariables": null, "documentGenerator": { "fromDocument": { "url": "http://localhost:22742/swagger/v1/swagger.json", "output": null } }, "codeGenerators": { "openApiToTypeScriptClient": { "className": "{controller}ServiceProxy", "moduleName": "", "namespace": "", "typeScriptVersion": 2.7, "template": "Angular", "promiseType": "Promise", "httpClass": "HttpClient", "useSingletonProvider": false, "injectionTokenType": "InjectionToken", "rxJsVersion": 6.0, "dateTimeType": "MomentJS", "nullValue": "Undefined", "generateClientClasses": true, "generateClientInterfaces": false, "generateOptionalParameters": false, "exportTypes": true, "wrapDtoExceptions": false, "exceptionClass": "ApiException", "clientBaseClass": null, "wrapResponses": false, "wrapResponseMethods": [], "generateResponseClasses": true, "responseClass": "SwaggerResponse", "protectedMethods": [], "configurationClass": null, "useTransformOptionsMethod": false, "useTransformResultMethod": false, "generateDtoTypes": true, "operationGenerationMode": "MultipleClientsFromPathSegments", "markOptionalProperties": false, "generateCloneMethod": false, "typeStyle": "Class", "classTypes": [], "extendedClasses": [], "extensionCode": "service.extensions.ts", "generateDefaultValues": true, "excludedTypeNames": [], "excludedParameterNames": [], "handleReferences": false, "generateConstructorInterface": true, "convertConstructorInterfaceData": false, "importRequiredTypes": true, "useGetBaseUrlMethod": false, "baseUrlTokenName": "API_BASE_URL", "queryNullValue": "", "inlineNamedDictionaries": false, "inlineNamedAny": false, "templateDirectory": null, "typeNameGeneratorType": null, "propertyNameGeneratorType": null, "enumNameGeneratorType": null, "serviceHost": null, "serviceSchemes": null, "output": "../src/shared/service-proxies/service-proxies.ts" }, "openApiToCSharpClient": { "clientBaseClass": null, "configurationClass": null, "generateClientClasses": true, "generateClientInterfaces": false, "injectHttpClient": false, "disposeHttpClient": true, "protectedMethods": [], "generateExceptionClasses": true, "exceptionClass": "SwaggerException", "wrapDtoExceptions": true, "useHttpClientCreationMethod": false, "httpClientType": "System.Net.Http.HttpClient", "useHttpRequestMessageCreationMethod": false, "useBaseUrl": true, "generateBaseUrlProperty": true, "generateSyncMethods": false, "exposeJsonSerializerSettings": false, "clientClassAccessModifier": "public", "typeAccessModifier": "public", "generateContractsOutput": false, "contractsNamespace": null, "contractsOutputFilePath": null, "parameterDateTimeFormat": "s", "generateUpdateJsonSerializerSettingsMethod": true, "serializeTypeInformation": false, "queryNullValue": "", "className": "{controller}Client", "operationGenerationMode": "MultipleClientsFromOperationId", "additionalNamespaceUsages": [], "additionalContractNamespaceUsages": [], "generateOptionalParameters": false, "generateJsonMethods": true, "enforceFlagEnums": false, "parameterArrayType": "System.Collections.Generic.IEnumerable", "parameterDictionaryType": "System.Collections.Generic.IDictionary", "responseArrayType": "System.Collections.ObjectModel.ObservableCollection", "responseDictionaryType": "System.Collections.Generic.Dictionary", "wrapResponses": false, "wrapResponseMethods": [], "generateResponseClasses": true, "responseClass": "SwaggerResponse", "namespace": "MyNamespace", "requiredPropertiesMustBeDefined": true, "dateType": "System.DateTime", "jsonConverters": null, "anyType": "object", "dateTimeType": "System.DateTime", "timeType": "System.TimeSpan", "timeSpanType": "System.TimeSpan", "arrayType": "System.Collections.ObjectModel.ObservableCollection", "arrayInstanceType": "System.Collections.ObjectModel.Collection", "dictionaryType": "System.Collections.Generic.Dictionary", "dictionaryInstanceType": "System.Collections.Generic.Dictionary", "arrayBaseType": "System.Collections.ObjectModel.ObservableCollection", "dictionaryBaseType": "System.Collections.Generic.Dictionary", "classStyle": "Inpc", "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, "templateDirectory": null, "typeNameGeneratorType": null, "propertyNameGeneratorType": null, "enumNameGeneratorType": null, "serviceHost": null, "serviceSchemes": null, "output": null }, "openApiToCSharpController": { "controllerBaseClass": null, "controllerStyle": "Partial", "controllerTarget": "AspNet", "useCancellationToken": false, "useActionResultType": false, "generateModelValidationAttributes": false, "routeNamingStrategy": "None", "className": "{controller}", "operationGenerationMode": "MultipleClientsFromOperationId", "additionalNamespaceUsages": [ "System.Web.Http" ], "additionalContractNamespaceUsages": [], "generateOptionalParameters": false, "generateJsonMethods": true, "enforceFlagEnums": false, "parameterArrayType": "System.Collections.Generic.IEnumerable", "parameterDictionaryType": "System.Collections.Generic.IDictionary", "responseArrayType": "System.Collections.ObjectModel.ObservableCollection", "responseDictionaryType": "System.Collections.Generic.Dictionary", "wrapResponses": false, "wrapResponseMethods": [], "generateResponseClasses": true, "responseClass": "SwaggerResponse", "namespace": "MyNamespace", "requiredPropertiesMustBeDefined": true, "dateType": "System.DateTime", "jsonConverters": null, "anyType": "object", "dateTimeType": "System.DateTime", "timeType": "System.TimeSpan", "timeSpanType": "System.TimeSpan", "arrayType": "System.Collections.Generic.IEnumerable", "arrayInstanceType": "System.Collections.ObjectModel.Collection", "dictionaryType": "System.Collections.Generic.Dictionary", "dictionaryInstanceType": "System.Collections.Generic.Dictionary", "arrayBaseType": "System.Collections.ObjectModel.ObservableCollection", "dictionaryBaseType": "System.Collections.Generic.Dictionary", "classStyle": "Inpc", "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, "templateDirectory": null, "typeNameGeneratorType": null, "propertyNameGeneratorType": null, "enumNameGeneratorType": null, "serviceHost": null, "serviceSchemes": null, "output": null } } }
When I run refresh.bat, I get the following result.
R:\Dev\Repos\BakeSimple\angular\nswag>"..\node_modules\.bin\nswag" run NSwag NPM CLI NSwag command line tool for .NET Core NetCore21, toolchain v13.0.6.0 (NJsonSchema v10.0.23.0 (Newtonsoft.Json v11.0.0.0)) Visit http://NSwag.org for more information. NSwag bin directory: R:\Dev\Repos\BakeSimple\angular\node_modules\nswag\bin\binaries\NetCore21 Executing file 'R:\Dev\Repos\BakeSimple\angular\nswag\service.config.nswag' with variables ''... Done. Duration: 00:00:05.3494464
Finally, here is part of my service-proxies.ts generated file.
/** * @param filter (optional) * @param nameFilter (optional) * @param sorting (optional) * @param skipCount (optional) * @param maxResultCount (optional) * @return Success */ getAll(filter: string | undefined, nameFilter: string | undefined, sorting: string | undefined, skipCount: number | undefined, maxResultCount: number | undefined): Observable<PagedResultDtoOfGetProductCategoryForViewDto> { let url_ = this.baseUrl + "/api/services/app/ProductCategories/GetAll?"; if (filter === null) throw new Error("The parameter 'filter' cannot be null."); else if (filter !== undefined) url_ += "Filter=" + encodeURIComponent("" + filter) + "&"; if (nameFilter === null) throw new Error("The parameter 'nameFilter' cannot be null."); else if (nameFilter !== undefined) url_ += "NameFilter=" + encodeURIComponent("" + nameFilter) + "&"; if (sorting === null) throw new Error("The parameter 'sorting' cannot be null."); else if (sorting !== undefined) url_ += "Sorting=" + encodeURIComponent("" + sorting) + "&"; if (skipCount === null) throw new Error("The parameter 'skipCount' cannot be null."); else if (skipCount !== undefined) url_ += "SkipCount=" + encodeURIComponent("" + skipCount) + "&"; if (maxResultCount === null) throw new Error("The parameter 'maxResultCount' cannot be null."); else if (maxResultCount !== undefined) url_ += "MaxResultCount=" + encodeURIComponent("" + maxResultCount) + "&"; url_ = url_.replace(/[?&]$/, ""); let options_ : any = { observe: "response", responseType: "blob", headers: new HttpHeaders({ "Accept": "text/plain" }) }; return this.http.request("get", url_, options_).pipe(_observableMergeMap((response_ : any) => { return this.processGetAll(response_); })).pipe(_observableCatch((response_: any) => { if (response_ instanceof HttpResponseBase) { try { return this.processGetAll(<any>response_); } catch (e) { return <Observable<PagedResultDtoOfGetProductCategoryForViewDto>><any>_observableThrow(e); } } else return <Observable<PagedResultDtoOfGetProductCategoryForViewDto>><any>_observableThrow(response_); })); } protected processGetAll(response: HttpResponseBase): Observable<PagedResultDtoOfGetProductCategoryForViewDto> { const status = response.status; const responseBlob = response instanceof HttpResponse ? response.body : (<any>response).error instanceof Blob ? (<any>response).error : undefined; let _headers: any = {}; if (response.headers) { for (let key of response.headers.keys()) { _headers[key] = response.headers.get(key); }}; if (status === 200) { return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => { let result200: any = null; let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver); result200 = PagedResultDtoOfGetProductCategoryForViewDto.fromJS(resultData200); return _observableOf(result200); })); } else if (status !== 200 && status !== 204) { return blobToText(responseBlob).pipe(_observableMergeMap(_responseText => { return throwException("An unexpected server error occurred.", status, _responseText, _headers); })); } return _observableOf<PagedResultDtoOfGetProductCategoryForViewDto>(<any>null); } ```
-
0
hi This is like a problem with net core 3.0, I am checking it.
-
0
This is a change in Swashbuckle.AspNetCore 5.x version. https://github.com/aspnetzero/aspnet-zero-core/pull/2750/files#diff-ca7c70741c6778bcc051a0512c7b60a9R28 https://github.com/aspnetzero/aspnet-zero-core/pull/2750/files#diff-95b60e9b66067387865beebdd219fadb
But I searched for a long time and didn't find any information about these.
I'm not sure if custom SwaggerNullableParameterFilter may cause other problems. However But for now, SwaggerNullableParameterFilter can solve it.
After applying
SwaggerNullableParameterFilter
public class SwaggerNullableParameterFilter : IParameterFilter { public void Apply(OpenApiParameter parameter, ParameterFilterContext context) { if (!parameter.Schema.Nullable && (context.ApiParameterDescription.Type.IsNullable() || !context.ApiParameterDescription.Type.IsValueType)) { parameter.Schema.Nullable = true; } } }
-
0
https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1528
-
0
Thank you very much @maliming, it just works!
/** * @param filter (optional) * @param nameFilter (optional) * @param sorting (optional) * @param skipCount (optional) * @param maxResultCount (optional) * @return Success */ getAll(filter: string | null | undefined, nameFilter: string | null | undefined, sorting: string | null | undefined, skipCount: number | undefined, maxResultCount: number | undefined): Observable<PagedResultDtoOfGetProductCategoryForViewDto> { let url_ = this.baseUrl + "/api/services/app/ProductCategories/GetAll?"; if (filter !== undefined) url_ += "Filter=" + encodeURIComponent("" + filter) + "&"; if (nameFilter !== undefined) url_ += "NameFilter=" + encodeURIComponent("" + nameFilter) + "&"; if (sorting !== undefined) url_ += "Sorting=" + encodeURIComponent("" + sorting) + "&"; if (skipCount === null) throw new Error("The parameter 'skipCount' cannot be null."); else if (skipCount !== undefined) url_ += "SkipCount=" + encodeURIComponent("" + skipCount) + "&"; if (maxResultCount === null) throw new Error("The parameter 'maxResultCount' cannot be null."); else if (maxResultCount !== undefined) url_ += "MaxResultCount=" + encodeURIComponent("" + maxResultCount) + "&"; url_ = url_.replace(/[?&]$/, ""); let options_ : any = { observe: "response", responseType: "blob", headers: new HttpHeaders({ "Accept": "text/plain" }) };
-
0
So adding SwaggerNullableParameterFilter class is wokring, it remove the null check.
Now, on the nswag (refresh.bat) generation process, how can we opt out null parameter in query string or replace null value by empty string so encodeURIComponent does not encode null to "null". Is there a way to do this? I've been searching this morning and can't find anything to fix it.
Example:
getAll(filter: string | null | undefined, productCategoryIdFilter: string | null | undefined, taxRateIdFilter: string | null | undefined, sorting: string | null | undefined, skipCount: number | undefined, maxResultCount: number | undefined): Observable<PagedResultDtoOfGetProductForViewDto> { let url_ = this.baseUrl + "/api/services/app/Products/GetAll?"; if (filter !== undefined) url_ += "Filter=" + encodeURIComponent("" + filter) + "&"; if (productCategoryIdFilter !== undefined) url_ += "ProductCategoryIdFilter=" + encodeURIComponent("" + productCategoryIdFilter) + "&"; if (taxRateIdFilter !== undefined) url_ += "TaxRateIdFilter=" + encodeURIComponent("" + taxRateIdFilter) + "&"; if (sorting !== undefined) url_ += "Sorting=" + encodeURIComponent("" + sorting) + "&"; if (skipCount === null) throw new Error("The parameter 'skipCount' cannot be null."); else if (skipCount !== undefined) url_ += "SkipCount=" + encodeURIComponent("" + skipCount) + "&"; if (maxResultCount === null) throw new Error("The parameter 'maxResultCount' cannot be null."); else if (maxResultCount !== undefined) url_ += "MaxResultCount=" + encodeURIComponent("" + maxResultCount) + "&"; url_ = url_.replace(/[?&]$/, "");
The line:
if (productCategoryIdFilter !== undefined) url_ += "ProductCategoryIdFilter=" + encodeURIComponent("" + productCategoryIdFilter) + "&";
Should be:
if (productCategoryIdFilter !== undefined) url_ += "ProductCategoryIdFilter=" + encodeURIComponent(productCategoryIdFilter ? "" + productCategoryIdFilter : "") + "&";
Thank you,
-
0
hi @morindo I also didn't find any solution, you can pass the parameter as
undefined
-
0
Thank you very much maliming. Cheers!
-
0
You're welcome, let's wait for the issue of Swashbuckle.
-
0
This issue is closed because of no recent activity. Please create a new issue if you are still having this problem.