domaindrivendev / Swashbuckle.AspNetCore

Swagger tools for documenting API's built on ASP.NET Core
MIT License
5.26k stars 1.32k forks source link

[Bug]: AdditionalItems serialization issue still present #3153

Open mrtristan opened 5 days ago

mrtristan commented 5 days ago

Describe the bug

originally discussed in https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/2884 fixed with: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/2890/files#diff-797869ba51539b79a9ed73d460675cc6d1c8ceab044a7383a2ebf81a34c6f7d4R12

the fix was to add a bunch of types, but the approach documented for modifying the theme is not fixed by that approach.

image

a complex anonymous object is not an understood type.

Swashbuckle.AspNetCore version

6.6+

martincostello commented 5 days ago

The linked issue is specific to SwaggerUI's AoT support and is unrelated to Redoc.

I'm assuming you're having an issue in an AoT application using Redoc (for which support is relatively new), but that's not clear from the issue description.

Please clarify the exact scenario you're having an issue with an ideally provide a repro as code (even better as a self-contained repo) that can be used to look into the problem.

mrtristan commented 5 days ago

@martincostello same issue here as the original reported issue. the only additional nuance in mine is that a complex object such as the value required for theme yields the same error and is unresolved. concept has been in use for years and broke with these latest versions

martincostello commented 5 days ago

From what I can assume from your screenshots, you're seeing the same kind of issue in a completely different part of the codebase.

Please provide more detail about exactly what you're trying to do and exactly what version you used, ideally with actual code. Screenshots aren't good because there's no code that can be copy-pasted - it all has to be retyped by hand.

The more complete and detailed your issue is, the easier it is to debug and fix any issues you might be having.

mrtristan commented 4 days ago

apologies, it seemed to me that it's just one more missing type in a common area that is at issue (with of course two different entry points). not that it's valid, i would expect that this line added to the swaggerui side would yield the same result but i've not tested that.

stack ``` System.NotSupportedException: Metadata for type '<>f__AnonymousType0`1[<>f__AnonymousType1`1[System.String]]' was not provided to the serializer. The serializer method used does not support reflection-based creation of serialization-related type metadata. If using source generation, ensure that all root types passed to the serializer have been indicated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically. at System.Text.Json.ThrowHelper.ThrowNotSupportedException_NoMetadataForType(Type type) at System.Text.Json.JsonSerializerOptions.GetClassFromContextOrCreate(Type type) at System.Text.Json.JsonSerializerOptions.GetOrAddClass(Type type) at System.Text.Json.WriteStackFrame.InitializeReEntry(Type type, JsonSerializerOptions options) at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.Converters.DictionaryOfTKeyTValueConverter`3.OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.JsonConverter`1.TryWriteDataExtensionProperty(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.GetMemberAndWriteJsonExtensionData(Object obj, WriteStack& state, Utf8JsonWriter writer) at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.Converters.JsonMetadataServicesConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state) at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state) ```

the simplest repro is adding a theme entry to AdditionalItems in a project such as Swashbuckle.AspNetCore\test\WebSites\ReDoc\Startup.cs here and trying to start the redoc website project.

app.UseReDoc(c =>
{
    c.RoutePrefix = "api-docs";
    c.ConfigObject = new ConfigObject
    {
        HideDownloadButton = true,
        HideLoading = true
    };
    // add this
    c.ConfigObject.AdditionalItems.Add("theme", new { colors = new { primary = "#086eaa" } });
});

the functionality at issue is mentioned in the readme: https://github.com/domaindrivendev/Swashbuckle.AspNetCore?tab=readme-ov-file#inject-custom-css-1

image

There is a CustomUIConfig test project for SwaggerUI but seemingly not one that exercises the same for ReDoc

EvgeniyZ commented 2 days ago

@mrtristan thanks for flagging this bug, I'm too facing it, I think I found a workaround for anonymous types for "theme". You can do the following:

...
var theme = new { colors = new { primary = "#086eaa" } };
c.ConfigObject.AdditionalItems.Add("theme", System.Text.Json.JsonSerializer.SerializeToDocument(theme));

The reason why it's working is because ReDocOptionsJsonContext has definition of [JsonSerializable(typeof(JsonDocument))] and it's perfectly translates later on in the html

martincostello commented 8 hours ago

Playing around with the repro, using JsonNode I can avoid the serialization exception, but then for some reason Redoc doesn't like the string value.

c.ConfigObject.AdditionalItems.Add("theme", new JsonObject { ["colors"] = new JsonObject { ["primary"] = "#086eaa" } });
redoc.standalone.js:2 r: An error occurred. See https://github.com/styled-components/polished/blob/main/src/internalHelpers/errors.md#3 for more information.
    at Sr (http://localhost:5154/redoc/redoc.standalone.js:2:611266)
    at Or (http://localhost:5154/redoc/redoc.standalone.js:2:613340)
    at Br (http://localhost:5154/redoc/redoc.standalone.js:2:615966)
    at http://localhost:5154/redoc/redoc.standalone.js:2:615657
    at linesColor (http://localhost:5154/redoc/redoc.standalone.js:2:618683)
    at Object.get [as linesColor] (http://localhost:5154/redoc/redoc.standalone.js:2:625878)
    at JSON.stringify (<anonymous>)
    at http://localhost:5154/redoc/redoc.standalone.js:2:625974
    at new Pi (http://localhost:5154/redoc/redoc.standalone.js:2:625988)
    at nx (http://localhost:5154/redoc/redoc.standalone.js:1825:3524)

I'm not sure that's a Swashbuckle issue though.

For now I'd say that JsonDocument/JsonObject can be used instead of anonymous types, and then serialization will work. Alternatively, you can define your own JsonSerializationContext that knows how to serialize the additional property object you're using it, and assign it to ReDocOptions.JsonSerializerOptions.

I think the only way we can "fix" this is to add first-class configuration properties for more well-known configuration settings, such as for the colours. The drawback to that is we have to constantly chase the Redoc configuration, and I don't think that's particularly sustainable.

I'm tempted to resolve this via documentation on how to make this scenario work (i.e. using custom types with the ReDocOptions.AdditionalItems property).

It's unfortunate that this was accidentally broken due to me not realising complex objects would be used with AdditionalItems, but undoing it would break native AoT support for our Redoc package.