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

The generated nswag contract from NSwag v14.0.0-preview009 failed to build #4572

Open JinjinM opened 9 months ago

JinjinM commented 9 months ago

Generated the c# contract from the NSwagStudio (v14.0.00) based on the API spec from NSwag v14.0.0-preview009. The generated contracts code failed to compile due to the below issues: (1) new generated partial class IntPtr confused the complier Issue message: The call is ambitious between the following methods or properties. Screen shot: image

It looks like it got confused with the new introduced class IntPtr at the "contracts.nswag.cs" (2) Several new properties in the new class IntPtr errors

Issue message: Attribute 'xxx' is not valid on this declaration type. It is only valid on 'property, indexer' declarations. Screen shot: image

Update: It looks like the problem is caused by difference with the swagger json generated between the old version and the newer version: Using the older version of Nswag( NSwag.AspNetCore version: 13.18.0 and NSwag.MSBuild version: 13.15.0) The swagger json has the below section:

 "Exception": {
      "type": "object",
      "required": [
        "Message"
      ],
      "properties": {
        "Message": {
          "type": "string"
        },
        "InnerException": {
          "$ref": "#/definitions/Exception"
        },
        "Source": {
          "type": "string"
        },
        "StackTrace": {
          "type": "string"
        }
      }
}

Whereas using the newer version of Nswag (NSwag.AspNetCore version: 14.0.0-preview009 and NSwag.MSBuild version: 14.0.0-preview009) The swagger json has added extra bit required fields for the "Exception" and also have extra new models, such as "MethodBase", "MethodAttributes" and ect which are the public properties of the class "System.Exception":

 "Exception": {
      "type": "object",
      "required": [
        "hasBeenThrown",
        "message",
        "data",
        "hResult"
      ],
      "properties": {
        "targetSite": {
          "$ref": "#/definitions/MethodBase"
        },
        "hasBeenThrown": {
          "type": "boolean"
        },
        "serializationWatsonBuckets": {

        },
        "message": {
          "type": "string"
        },
        "data": {
          "type": "array",
          "items": {
            "additionalProperties": {

            }
          }
        },
        "innerException": {
          "$ref": "#/definitions/Exception"
        },
        "helpLink": {
          "type": "string"
        },
        "source": {
          "type": "string"
        },
        "hResult": {
          "type": "integer",
          "format": "int32"
        },
        "stackTrace": {
          "type": "string"
        },
        "serializationStackTraceString": {
          "type": "string"
        }
      }
    },
    "MethodBase": {
      "allOf": [
        {
          "$ref": "#/definitions/MemberInfo"
        },
        {
          "type": "object",
          "x-abstract": true,
          "required": [
            "attributes",
            "methodImplementationFlags",
            "callingConvention",
            "isAbstract",
            "isConstructor",
            "isFinal",
            "isHideBySig",
            "isSpecialName",
            "isStatic",
            "isVirtual",
            "isAssembly",
            "isFamily",
            "isFamilyAndAssembly",
            "isFamilyOrAssembly",
            "isPrivate",
            "isPublic",
            "isConstructedGenericMethod",
            "isGenericMethod",
            "isGenericMethodDefinition",
            "containsGenericParameters",
            "methodHandle",
            "isSecurityCritical",
            "isSecuritySafeCritical",
            "isSecurityTransparent"
          ],
          "properties": {
            "attributes": {
              "$ref": "#/definitions/MethodAttributes"
            },
            "methodImplementationFlags": {
              "$ref": "#/definitions/MethodImplAttributes"
            },
            "callingConvention": {
              "$ref": "#/definitions/CallingConventions"
            },
            "isAbstract": {
              "type": "boolean"
            },
            "isConstructor": {
              "type": "boolean"
            },
            "isFinal": {
              "type": "boolean"
            },
            "isHideBySig": {
              "type": "boolean"
            },
            "isSpecialName": {
              "type": "boolean"
            },
            "isStatic": {
              "type": "boolean"
            },
            "isVirtual": {
              "type": "boolean"
            },
            "isAssembly": {
              "type": "boolean"
            },
            "isFamily": {
              "type": "boolean"
            },
            "isFamilyAndAssembly": {
              "type": "boolean"
            },
            "isFamilyOrAssembly": {
              "type": "boolean"
            },
            "isPrivate": {
              "type": "boolean"
            },
            "isPublic": {
              "type": "boolean"
            },
            "isConstructedGenericMethod": {
              "type": "boolean"
            },
            "isGenericMethod": {
              "type": "boolean"
            },
            "isGenericMethodDefinition": {
              "type": "boolean"
            },
            "containsGenericParameters": {
              "type": "boolean"
            },
            "methodHandle": {
              "$ref": "#/definitions/RuntimeMethodHandle"
            },
            "isSecurityCritical": {
              "type": "boolean"
            },
            "isSecuritySafeCritical": {
              "type": "boolean"
            },
            "isSecurityTransparent": {
              "type": "boolean"
            }
          }
        }
      ]
    },

image

.....

When I tried to use the contracts that generated by the new version of Nswag, these extra class models have been conflicting the same class models from "system.namespace", which caused the project failed to build. I was expecting the models would stay unchanged because my code stays same.

michaelSant0s commented 6 months ago

I have the same problem, is there any workaround?

simeyla commented 6 months ago

Same issue. Interestingly I only discovered it because it generated the following invalid code. Yes it's duplicates some enum values and not put a | separator between them.

export type MethodAttributes = "PrivateScope""PrivateScope" | "Private" | "FamANDAssem" | "Assembly" | "Family" | "FamORAssem" | "Public" | "MemberAccessMask" | "UnmanagedExport" | "Static" | "Final" | "Virtual" | "HideBySig" | "NewSlot" | "NewSlot" | "CheckAccessOnOverride" | "Abstract" | "SpecialName" | "RTSpecialName" | "PinvokeImpl" | "HasSecurity" | "RequireSecObject" | "ReservedMask";

export type MethodImplAttributes = "IL""IL" | "Native" | "OPTIL" | "CodeTypeMask" | "CodeTypeMask" | "ManagedMask" | "ManagedMask" | "NoInlining" | "ForwardRef" | "Synchronized" | "NoOptimization" | "PreserveSig" | "AggressiveInlining" | "AggressiveOptimization" | "InternalCall" | "MaxMethodImplVal";
simeyla commented 6 months ago

I couldn't find any workable solution right now other than to switch to using NewtonsoftJsonSchemaGeneratorSettings. Your settings may vary.

        services.AddOpenApiDocument(document =>
        {
            document.DocumentName = "a";

            document.SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings()
            {
                SchemaType = NJsonSchema.SchemaType.OpenApi3,

                SerializerSettings = new JsonSerializerSettings()
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver(),
                    Converters = new[]
                    {
                        new StringEnumConverter()
                    }
                }
            };

By default in v14.0 it uses the System.Text generator settings, which for my project at least I found incredibly frustrating to try to get it to match what I had in v13 - especially given years of work.

This allowed me to get back to where I was before in .NET Core 7.

Be careful to check for places where you have JsonPropertyName() but not JsonProperty(). Do a diff between your old .json and new .json open API specs.

JinjinM commented 6 months ago

I parked this issue for a while until now I am back to look at this again. The project I worked on is using System.Text.Json which was being agreed with peers, so it would be better to stick with it unless the road is dead. I will post updates if I can find something useful

RanjithkumarRajendran commented 6 months ago

Tried with the version: 14.0.3, still having same issue I am also facing the same issue while generating the client code for Typescript. any one got any workaround or solution for this issues?

JinjinM commented 6 months ago

@RanjithkumarRajendran Unfortunately, I have not find a workable solution yet

devinlyons commented 5 months ago

I was able to work around it by adding the following to my NSwagStudio configuration. I'm not sure why NSwag was trying to generate code for a bunch of built-in reflection types, but here we are...

"codeGenerators": {
    "excludedTypeNames": [
        "MemberInfo",
        "EventInfo",
        "Anonymous",
        "TypeInfo",
        "MethodBase",
        "RuntimeMethodHandle",
        "IntPtr",
        "MethodAttributes",
        "MethodImplAttributes",
        "CallingConventions",
        "MemberTypes",
        "Module",
        "Assembly",
        "ConstructorInfo",
        "EventAttributes",
        "MethodInfo",
        "ParameterInfo",
        "ParameterAttributes",
        "CustomAttributeData",
        "CustomAttributeTypedArgument",
        "PropertyInfo",
        "FieldAttributes",
        "RuntimeFieldHandle",
        "PropertyAttributes",
        "SecurityRuleSet",
        "ModuleHandle",
        "FieldInfo",
        "ICustomAttributeProvider",
        "CustomAttributeNamedArgument"
      ],

I, also, had to correct some of the generated code at the end of the client file. The commented-out code and the line beneath it are the key. The rest is provided for context.

private void SetExceptionFieldValue(Newtonsoft.Json.Linq.JObject jObject, string propertyName, object value, string fieldName, Newtonsoft.Json.Serialization.IContractResolver resolver, Newtonsoft.Json.JsonSerializer serializer)
{
    var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(System.Exception)).GetDeclaredField(fieldName);
    var jsonPropertyName = resolver is Newtonsoft.Json.Serialization.DefaultContractResolver ? ((Newtonsoft.Json.Serialization.DefaultContractResolver)resolver).GetResolvedPropertyName(propertyName) : propertyName;
    // Workaround due to bug in NSwagStudio 14.0.3.0
    // foreach (var p in jObject.Properties())
    foreach (var property in jObject.Properties())
    {
        if (System.String.Equals(property.Name, jsonPropertyName, System.StringComparison.OrdinalIgnoreCase))
        {
            var fieldValue = property.Value.ToObject(field.FieldType, serializer);
            field.SetValue(value, fieldValue);
            break;
        }
    }
}
devinlyons commented 5 months ago

On further investigation, it looks like NSwagStudio is trying to generate these types because NSwag.AspNetCore is generating an OpenAPI spec with an endpoint that uses the Exception type as a payload for 5xx errors. I'm not sure why it's doing that, but I am going to see if I can figure it out.

UPDATE: One of my controllers was responding with the Exception type in certain cases. At least that's what the ProducesResponseType attribute said. Once I fixed this, everything started working.