Skip to content

Commit dc1a065

Browse files
authored
Don't require AttributeProvider for JsonPropertyInfo-based modifications (#56712)
* Don't require AttributeProvider for JsonPropertyInfo-based modifications * Add test coverage for fix
1 parent d498542 commit dc1a065

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ internal sealed class OpenApiSchemaService(
9191
schema.ApplyPrimitiveTypesAndFormats(context);
9292
schema.ApplySchemaReferenceId(context);
9393
schema.ApplyPolymorphismOptions(context);
94-
if (context.PropertyInfo is { AttributeProvider: { } attributeProvider } jsonPropertyInfo)
94+
if (context.PropertyInfo is { } jsonPropertyInfo)
9595
{
9696
// Short-circuit STJ's handling of nested properties, which uses a reference to the
9797
// properties type schema with a schema that uses a document level reference.
@@ -102,6 +102,9 @@ internal sealed class OpenApiSchemaService(
102102
return new JsonObject { [OpenApiSchemaKeywords.RefKeyword] = context.TypeInfo.GetSchemaReferenceId() };
103103
}
104104
schema.ApplyNullabilityContextInfo(jsonPropertyInfo);
105+
}
106+
if (context.PropertyInfo is { AttributeProvider: { } attributeProvider })
107+
{
105108
if (attributeProvider.GetCustomAttributes(inherit: false).OfType<ValidationAttribute>() is { } validationAttributes)
106109
{
107110
schema.ApplyValidationAttributes(validationAttributes);

src/OpenApi/test/Services/OpenApiDocumentServiceTestsBase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,9 @@ internal static OpenApiDocumentService CreateDocumentService(IEndpointRouteBuild
101101
provider.OnProvidersExecuted(context);
102102

103103
var apiDescriptionGroupCollectionProvider = CreateApiDescriptionGroupCollectionProvider(context.Results);
104+
var jsonOptions = builder.ServiceProvider.GetService<IOptions<Microsoft.AspNetCore.Http.Json.JsonOptions>>() ?? Options.Create(new Microsoft.AspNetCore.Http.Json.JsonOptions());
104105

105-
var schemaService = new OpenApiSchemaService("Test", Options.Create(new Microsoft.AspNetCore.Http.Json.JsonOptions()), builder.ServiceProvider, options.Object);
106+
var schemaService = new OpenApiSchemaService("Test", jsonOptions, builder.ServiceProvider, options.Object);
106107
((TestServiceProvider)builder.ServiceProvider).TestSchemaService = schemaService;
107108
var documentService = new OpenApiDocumentService("Test", apiDescriptionGroupCollectionProvider, hostEnvironment, options.Object, builder.ServiceProvider, new OpenApiTestServer());
108109
((TestServiceProvider)builder.ServiceProvider).TestDocumentService = documentService;

src/OpenApi/test/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
using System.ComponentModel;
55
using System.ComponentModel.DataAnnotations;
66
using System.IO.Pipelines;
7+
using System.Text.Json.Serialization.Metadata;
78
using Microsoft.AspNetCore.Builder;
89
using Microsoft.AspNetCore.Mvc;
10+
using Microsoft.Extensions.DependencyInjection;
911
using Microsoft.OpenApi.Any;
1012
using Microsoft.OpenApi.Models;
1113

@@ -468,6 +470,51 @@ await VerifyOpenApiDocument(builder, document =>
468470
});
469471
}
470472

473+
[Fact]
474+
public async Task SupportsNestedTypes_WithNoAttributeProvider()
475+
{
476+
// Arrange: this test ensures that we can correctly handle the scenario
477+
// where the attribute provider is null and we need to patch the property mappings
478+
// that are created by the underlying JsonSchemaExporter.
479+
var serviceCollection = new ServiceCollection();
480+
serviceCollection.ConfigureHttpJsonOptions(options =>
481+
{
482+
options.SerializerOptions.TypeInfoResolver = options.SerializerOptions.TypeInfoResolver?.WithAddedModifier(jsonTypeInfo =>
483+
{
484+
foreach (var propertyInfo in jsonTypeInfo.Properties)
485+
{
486+
propertyInfo.AttributeProvider = null;
487+
}
488+
489+
});
490+
});
491+
var builder = CreateBuilder(serviceCollection);
492+
493+
// Act
494+
builder.MapPost("/api", (NestedType type) => { });
495+
496+
// Assert
497+
await VerifyOpenApiDocument(builder, document =>
498+
{
499+
var operation = document.Paths["/api"].Operations[OperationType.Post];
500+
var requestBody = operation.RequestBody;
501+
var content = Assert.Single(requestBody.Content);
502+
Assert.Equal("NestedType", content.Value.Schema.Reference.Id);
503+
var schema = content.Value.Schema.GetEffective(document);
504+
Assert.Collection(schema.Properties,
505+
property =>
506+
{
507+
Assert.Equal("name", property.Key);
508+
Assert.Equal("string", property.Value.Type);
509+
},
510+
property =>
511+
{
512+
Assert.Equal("nested", property.Key);
513+
Assert.Equal("NestedType", property.Value.Reference.Id);
514+
});
515+
});
516+
}
517+
471518
private class DescriptionTodo
472519
{
473520
[Description("The unique identifier for a todo item.")]

0 commit comments

Comments
 (0)