Description
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
OpenAPI document generation fails when a request body class contains a nullable property with [MaxLength(...)].
Here is reproducible code:
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapControllers();
app.MapOpenApi();
await app.RunAsync();
[ApiController]
[Route("api/[controller]")]
public class MyController : ControllerBase
{
public class MyRequestModel
{
// Both types fail with the same error
[MaxLength(10)]
public IList<string>? Items { get; set; }
// public string[]? Items { get; set; }
}
[HttpPost]
public IActionResult Post([FromBody] MyRequestModel request)
{
return Ok(request);
}
}
Calling http://localhost:5000/openapi/v1.json
results in:
System.InvalidOperationException: The node must be of type 'JsonValue'.
at System.Text.Json.Nodes.JsonNode.GetValue[T]()
at Microsoft.AspNetCore.OpenApi.JsonNodeSchemaExtensions.ApplyValidationAttributes(JsonNode schema, IEnumerable`1 validationAttributes)
at Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.<>c__DisplayClass0_0.<.ctor>b__2(JsonSchemaExporterContext context, JsonNode schema)
at System.Text.Json.Schema.JsonSchema.<ToJsonNode>g__CompleteSchema|104_0(JsonNode schema, <>c__DisplayClass104_0&)
at System.Text.Json.Schema.JsonSchema.ToJsonNode(JsonSchemaExporterOptions options)
at System.Text.Json.Schema.JsonSchema.ToJsonNode(JsonSchemaExporterOptions options)
at System.Text.Json.Schema.JsonSchemaExporter.GetJsonSchemaAsNode(JsonTypeInfo typeInfo, JsonSchemaExporterOptions exporterOptions)
at System.Text.Json.Schema.JsonSchemaExporter.GetJsonSchemaAsNode(JsonSerializerOptions options, Type type, JsonSchemaExporterOptions exporterOptions)
at Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.CreateSchema(OpenApiSchemaKey key)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at Microsoft.AspNetCore.OpenApi.OpenApiSchemaStore.GetOrAdd(OpenApiSchemaKey key, Func`2 valueFactory)
at Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.GetOrCreateSchemaAsync(Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription parameterDescription, Boolean captureSchemaByRef, CancellationToken cancellationToken)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetJsonRequestBody(IList`1 supportedRequestFormats, ApiParameterDescription bodyParameter, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetRequestBodyAsync(ApiDescription description, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationAsync(ApiDescription description, HashSet`1 capturedTags, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationsAsync(IGrouping`2 descriptions, HashSet`1 capturedTags, IServiceProvider scopedServiceProvider, IOpenApiOperationTransformer[] operationTransformers, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiPathsAsync(HashSet`1 capturedTags, IServiceProvider scopedServiceProvider, IOpenApiOperationTransformer[] operationTransformers, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiDocumentAsync(IServiceProvider scopedServiceProvider, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Builder.OpenApiEndpointRouteBuilderExtensions.<>c__DisplayClass0_0.<<MapOpenApi>b__0>d.MoveNext()
I have done some debugging and the fault happens here in JsonNodeSchemaExtensions.cs:101
else if (attribute is MaxLengthAttribute maxLengthAttribute)
{
var targetKey = schema[OpenApiSchemaKeywords.TypeKeyword]?.GetValue<string>() == "array" ? OpenApiSchemaKeywords.MaxItemsKeyword : OpenApiSchemaKeywords.MaxLengthKeyword;
schema[targetKey] = maxLengthAttribute.Length;
}
The schema comes through as:
But fails because it's a JsonArray
and not a JsonValue
{
"type" : [ "array", "null" ],
"items" : {
"type" : "string",
"nullable" : false,
"format" : null,
"x-schema-id" : null,
"pattern" : null
},
"nullable" : true
}
Please let me know if I'm doing something wrong, thanks!
Expected Behavior
The document generator should be able to handle a nullable array/collection with a MaxLength(N)
validation attribute.
The resulting OpenAPI document should list the property as a nullable array, with maxItems as N
Steps To Reproduce
See bug description
Exceptions (if any)
See bug description for full trace
System.InvalidOperationException: The node must be of type 'JsonValue'.
at System.Text.Json.Nodes.JsonNode.GetValue[T]()
at Microsoft.AspNetCore.OpenApi.JsonNodeSchemaExtensions.ApplyValidationAttributes(JsonNode schema, IEnumerable`1 validationAttributes)
.NET Version
.NET 9
Anything else?
No response