RicoSuter / NSwag

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

Axios TS client doesn't send `Authorization` header #3901

Open eduherminio opened 2 years ago

eduherminio commented 2 years ago

After having a working app generated using Fetch template I've switched to Axios one and Authorization header aren't longer sent in the server requests.

This is the generated client code. In the debugger point transformedOptions_.headers.get("Authorization") still has the right header

    getMine(  cancelToken?: CancelToken | undefined): Promise<PullRequest[]> {
        let url_ = this.baseUrl + "/PullRequest/mine";
        url_ = url_.replace(/[?&]$/, "");

        let options_: AxiosRequestConfig = {
            method: "GET",
            url: url_,
            headers: {
                "Accept": "application/json"
            },
            cancelToken
        };

        return this.transformOptions(options_).then(transformedOptions_ => {
            debugger; // <------------ transformedOptions_.headers.get("Authorization") has the right token
            return this.instance.request(transformedOptions_);
        }).catch((_error: any) => {
            if (isAxiosError(_error) && _error.response) {
                return _error.response;
            } else {
                throw _error;
            }
        }).then((_response: AxiosResponse) => {
            return this.transformResult(url_, _response, (_response: AxiosResponse) => this.processGetMine(_response));
        });
    }

Tested with NSwag.MsBuild 13.15.9 server side and both Axios ^0.21.1 and ^0.26.0 client side

nswag.json: ```json { "runtime": "Net60", "defaultVariables": null, "documentGenerator": { "aspNetCoreToOpenApi": { "project": null, "msBuildProjectExtensionsPath": null, "configuration": null, "runtime": null, "targetFramework": null, "noBuild": false, "verbose": true, "workingDirectory": null, "requireParametersWithoutDefault": false, "apiGroupNames": null, "defaultPropertyNameHandling": "Default", "defaultReferenceTypeNullHandling": "Null", "defaultDictionaryValueReferenceTypeNullHandling": "NotNull", "defaultResponseReferenceTypeNullHandling": "NotNull", "defaultEnumHandling": "String", "flattenInheritanceHierarchy": false, "generateKnownTypes": true, "generateEnumMappingDescription": true, "generateXmlObjects": true, "generateAbstractProperties": false, "generateAbstractSchemas": true, "ignoreObsoleteProperties": false, "allowReferencesWithProperties": false, "excludedTypeNames": [], "serviceHost": null, "serviceBasePath": null, "serviceSchemes": [], "infoTitle": " Api", "infoDescription": "Sample Api", "infoVersion": "1.0.0", "documentTemplate": null, "documentProcessorTypes": [], "operationProcessorTypes": [], "typeNameGeneratorType": null, "schemaNameGeneratorType": null, "contractResolverType": null, "serializerSettingsType": null, "useDocumentProvider": true, "documentName": "Sample.Api", "aspNetCoreEnvironment": null, "createWebHostBuilderMethod": null, "startupType": null, "allowNullableBodyParameters": true, "output": "Autogenerated/openapi.json", "outputType": "OpenApi3", "assemblyPaths": [ "bin/Debug/net6.0/Sample.Api.dll" ], "assemblyConfig": null, "referencePaths": [], "useNuGetCache": false } }, "codeGenerators": { "openApiToTypeScriptClient": { "className": "{controller}Client", "moduleName": "", "namespace": "", "typeScriptVersion": 4.1, "template": "Axios", // Changing this to "Fetch" makes it work again "promiseType": "Promise", "httpClass": "HttpClient", "withCredentials": false, "useSingletonProvider": false, "injectionTokenType": "InjectionToken", "rxJsVersion": 6.0, "dateTimeType": "MomentJS", "nullValue": "Undefined", "generateClientClasses": true, "generateClientInterfaces": false, "generateOptionalParameters": false, "exportTypes": true, "wrapDtoExceptions": false, "exceptionClass": "SwaggerException", "clientBaseClass": "BaseClient", "wrapResponses": false, "wrapResponseMethods": [], "generateResponseClasses": true, "responseClass": "SwaggerResponse", "protectedMethods": [], "configurationClass": null, "useTransformOptionsMethod": true, "useTransformResultMethod": true, "generateDtoTypes": true, "operationGenerationMode": "MultipleClientsFromOperationId", "markOptionalProperties": true, "generateCloneMethod": false, "typeStyle": "Interface", "classTypes": [], "extendedClasses": [], "extensionCode": "Autogenerated/client.extensions.ts", "generateDefaultValues": true, "excludedTypeNames": [], "excludedParameterNames": [], "handleReferences": false, "generateConstructorInterface": true, "convertConstructorInterfaceData": false, "importRequiredTypes": true, "useGetBaseUrlMethod": true, "baseUrlTokenName": "API_BASE_URL", "queryNullValue": "", "inlineNamedDictionaries": false, "inlineNamedAny": false, "templateDirectory": null, "typeNameGeneratorType": null, "propertyNameGeneratorType": null, "enumNameGeneratorType": null, "serviceHost": null, "serviceSchemes": null, "output": "Autogenerated/SampleClient.ts" } } } ``` Base client: ```ts export class BaseClient { token: string = "a_hardcoded_token"; transformOptions(options: RequestInit): Promise { let newHeaders: Headers = new Headers(options.headers); newHeaders.append('Authorization', `Bearer ${this.token}`); options.headers = newHeaders; debugger; // The right token here return Promise.resolve(options); } getBaseUrl(noidea: string, defaultUrl: string | undefined): string { return 'https://localhost:7208'; } transformResult(url: string, response: Response, processor: (response: Response) => Promise): Promise { return processor(response); } } ```
dantheother commented 2 years ago

Probably a bit late for OP, but I think the problem stems from your typing of options in transformOptions. The type should be AxiosRequestConfig, not RequestInit

Rather than

transformOptions(options: RequestInit): Promise<RequestInit> {

It should be

transformOptions(options: AxiosRequestConfig): Promise<AxiosRequestConfig> {

Then, with the correct typing, there's no need to new up an object nor call append on it, it's as simple as

        options.headers = {
            ...options.headers,
            Authorization: `Bearer ${this.token}`,
        };        
        return Promise.resolve(options);