RicoSuter / NSwag

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

'Missing ApiException' compiler error #2839

Open dgmagno opened 4 years ago

dgmagno commented 4 years ago

Currently I have a project that uses multiple openapi.json v3 files. We used the official openapi tools described in this link: https://docs.microsoft.com/en-us/aspnet/core/web-api/microsoft.dotnet-openapi?view=aspnetcore-3.1

Because of the following code the generated c# client do not compile:

<ItemGroup>
  <!-- @(CurrentOpenApiReference) item group will never contain more than one item. -->
  <CurrentOpenApiReference>
    <Command>$(_NSwagCommand) openapi2csclient /className:%(ClassName) /namespace:%(Namespace)</Command>
  </CurrentOpenApiReference>
  <CurrentOpenApiReference>
    <Command Condition="! %(FirstForGenerator)">%(Command) /GenerateExceptionClasses:false</Command>
  </CurrentOpenApiReference>
  <CurrentOpenApiReference>
    <Command>%(Command) /input:"%(FullPath)" /output:"%(OutputPath)" %(Options)</Command>
  </CurrentOpenApiReference>
</ItemGroup>

Why did you make this design decision of not generating the Exception code for the remaining clients? The commented affirmation of only one item is not true.

I managed it to work by adding a AdditionalNamespaceUsages options but for me it feels like a hack.

Below I added a sample of a csproj to reproduce the issue:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup> 
    <PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> 
    <PackageReference Include="NSwag.ApiDescription.Client" Version="13.5.0" />
  </ItemGroup>

  <ItemGroup>
    <OpenApiReference Include=".\OpenApi1.json">
      <Namespace>Integration.ExternalApi1</Namespace>
    </OpenApiReference>

    <OpenApiReference Include=".\OpenApi2.json">
      <Namespace>Integration.ExternalApi2</Namespace>
      <!-- Workaround to make the generated code compile -->
      <!--<Options>/AdditionalNamespaceUsages:Integration.ExternalApi1</Options>-->
    </OpenApiReference>
  </ItemGroup>

</Project>
KevinOneSixDotNet commented 4 years ago

I just encountered this issue as well. I ended up using the workaround because I have more than one connected service in visual studio and need the other ones to build and they are all in different namespaces.

I can manually modify the code to build fine but the workaround is the only way to get this to succeed on my build server.

iskandersierra commented 3 years ago

The thing is that we would need a set of exception classes per generated namespace.

If we reference multiple OpenAPI services, we need to group by namespace and, either generate the exception classes in the first generated cs per namespace, or generate a separate file like "$(namespace).Exceptions.cs" only containing the exception classes.

I think it is easier, though, to assume that exception MUST always be generated, and leave the user the option to add the option /GenerateExceptionClasses:false when generating multiple clients on the same namespace.

In my case I find it the common case when adding multiple OpenAPI references, because I always add the following attributes to my controller's actions:

[SwaggerResponse(400, "Bad request", typeof(ValidationProblemDetails))]
[SwaggerResponse(404, "Not found", typeof(ProblemDetails))]
[SwaggerResponse(500, "Internal error")]

So, NSwag end's up generating classes for ProblemDetailsand ValidationProblemDetails on each generated client code. This forces my to generate each client on a different namespace anyway.

I prefer to leave the user with more control instead of assuming a configuration that breaks more complex code.

WizX20 commented 3 years ago

I stumbled on this one when I had to generate a new version of a client side-by-side.

I had a look at https://github.com/RicoSuter/NSwag/blob/85ae862fd6d68173a201a79e0ad06e0be2ec5de1/src/NSwag.CodeGeneration.CSharp/CSharpClientGeneratorSettings.cs to check the options we have.

Finally, I decided not to use a generated ApiException class, but instead move that to my own file.

  1. Move the ApiException partial classes from the first generated client to it's own ApiException.cs file with the namespace 'Example.Api.Client.Exceptions'
  2. Use the GenerateExceptionClasses:false in the options:
    <OpenApiReference Include="Example.openapi.v1_3.json" CodeGenerator="NSwagCSharp" Namespace="Example.Api.Clients.v1_3" Condition="'$(Configuration)'=='Debug'">
      <OutputPath>..\Clients\Example.openapi.v1_3Client.cs</OutputPath>
      <Options>/GenerateClientInterfaces:true /GenerateExceptionClasses:false /AdditionalNamespaceUsages:Example.Api.Client.Exceptions</Options>
    </OpenApiReference>
    <OpenApiReference Include="Example.openapi.v1_4.json" CodeGenerator="NSwagCSharp" Namespace="Example.Api.Clients.v1_4" Condition="'$(Configuration)'=='Debug'">
      <OutputPath>..\Clients\Example.openapi.v1_4Client.cs</OutputPath>
      <Options>/GenerateClientInterfaces:true /GenerateExceptionClasses:false /AdditionalNamespaceUsages:Example.Api.Client.Exceptions</Options>
    </OpenApiReference>
mrflo commented 2 years ago

This is still an issue for me. Is there a real fix in the making for this ?

I found out that if you remove all connected services and re-add them again in a different orders, sometimes it generates it.

Artonus commented 2 years ago

Can confirm it is still an issue. If it is not fixed it should at least be mentioned in the official documentation with the description of a workaround for it.

JanRou commented 2 years ago

How to work with two or more OpenAPIs in Visual Studio 2022

The situation is that I have to add two or more OpenAPIs to a VS2022 project, because my service interfaces to more APIs.

Here is my cumbersome procedure:

  1. I download and store the OpenAPI swagger json files in a Git version controlled folder in the project due to maintenance. This means that when I add the OpenAPIs in VS2022, I refer to the downloaded swagger json files.
  2. I add the swagger APIs in VS2022 under Connected Services and refer to the downloaded swaggger json files. The APIs get each a particular namespace and classname that I note.
  3. For each OpenAPI classname I code my own partial class with the same classname as noted in previous step. My own partial class extends the generated partial class in SwaggerClient.cs s. My partial class also implements an interface with the used API methods so I can unit test classes using the API.
  4. I copy the generated partial class ApiException to the my coded partial classes one per namespace. (I haven't figured out how to prevent the duplicated code for ApiException).
  5. To the project file, csproj, I add to the existing ItemGroup containing OpenApiReference tags an Options tag for each OpenApiReference like this: <Options>/AdditionalNamespaceUsages:TheOpenApiNamespace /GenerateExceptionClasses:false</Options>. TheOpenApiNamespace is replaced with the actual namespace.
  6. Save the csproj file and verify that it loads into the solution.
  7. I delete the generated bad ...SwaggerClient.cs files in the obj folder that don't compile due to duplicates of the partial ApiException class.
  8. I rebuild the project without any compile error, because the tag instructs VS2022 not to generated the ApiException partial class.

When I have to maintain the OpenAPI services like extending or upgrade to a new version, then I download the OpenAPI swagger json file and overwrite the existing. Next I delete the ..SwaggerClient.cs n the obj folder. VS2022 rebuilds then a new ...SwaggerClient.cs.

I hope it will be easier soon!

tiesmaster commented 2 years ago

@JanRou We use the exact same approach, if I'm not mistaken (@nzeus can you concur?). Thank you for your detailed HOWTO!! ❤️

JanRou commented 2 years ago

@JanRou We use the exact same approach, if I'm not mistaken (@nZeus can you concur?). Thank you for your detailed HOWTO!! ❤️

Do you have any tip regarding the duplicated ApiException code?

JsAndDotNet commented 2 years ago

I'd just like to add that in Aug 2022, this is still a problem with the latest version. Thanks all for the work arounds, but a proper fix would be much appreciated please.

smargoli2 commented 2 years ago

I am also dealing with this issue. I am generating many API models and getting ApiClient.cs, ApiException etc for each.

iannicktl commented 1 year ago

I do have the same issue. Used the following workaround:

  1. Create a new file _(I named it NSwagClientError_Missing_Code_Generation_Of_ApiExceptionOnTheSecondClient.cs)
  2. Pass the code below
  3. Replace the namespace [YourNameSpaceFoNswagGenerateClient] and the class name [YourClassNameForNswagGenerateClient]. The 2 must match what you put on generated NSwag client

Here is the ApiException code


{
    /// <summary>
    /// https://github.com/RicoSuter/NSwag/issues/2839
    /// </summary>
    public partial class [YourClassNameForNswagGenerateClient]
    {

        public partial class ApiException : System.Exception
        {
            public int StatusCode { get; private set; }

            public string Response { get; private set; }

            public System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> Headers { get; private set; }

            public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Exception innerException)
                : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException)
            {
                StatusCode = statusCode;
                Response = response;
                Headers = headers;
            }

            public override string ToString()
            {
                return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString());
            }
        }

        [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.17.0.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))")]
        public partial class ApiException<TResult> : ApiException
        {
            public TResult Result { get; private set; }

            public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, TResult result, System.Exception innerException)
                : base(message, statusCode, response, headers, innerException)
            {
                Result = result;
            }
        }
    }
}
RyptHunterz commented 1 year ago

@RicoSuter Still no solution ????

Rookian commented 1 year ago

@RyptHunterz Still no PR from you ????

vixero-dev commented 1 year ago

I have added a lazy fix for this issue.