Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenAPI: Range attribute causes FormatException #57390

Closed
1 task done
mus65 opened this issue Aug 18, 2024 · 6 comments
Closed
1 task done

OpenAPI: Range attribute causes FormatException #57390

mus65 opened this issue Aug 18, 2024 · 6 comments
Assignees
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates Docs This issue tracks updating documentation feature-openapi
Milestone

Comments

@mus65
Copy link

mus65 commented Aug 18, 2024

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Adding the following attribute to a property of type long causes a FormatException when generating the OpenAPI document:

[Range(0, 9223372036854775807)]

Affected code:

schema[OpenApiSchemaKeywords.MaximumKeyword] = decimal.Parse(rangeAttribute.Maximum.ToString()!, CultureInfo.InvariantCulture);

Exception:

System.FormatException: The input string '9.223372036854776E+18' was not in a correct format.
   at System.Number.ThrowFormatException[TChar](ReadOnlySpan`1 value)
   at System.Number.ParseDecimal[TChar](ReadOnlySpan`1 value, NumberStyles styles, NumberFormatInfo info)
   at System.Decimal.Parse(String s, IFormatProvider provider)
   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.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 Microsoft.AspNetCore.OpenApi.OpenApiSchemaStore.GetOrAdd(OpenApiSchemaKey key, Func`2 valueFactory)
   at Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.GetOrCreateSchemaAsync(Type type, ApiParameterDescription parameterDescription, Boolean captureSchemaByRef, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetJsonRequestBody(IList`1 supportedRequestFormats, ApiParameterDescription bodyParameter, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetRequestBodyAsync(ApiDescription description, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationAsync(ApiDescription description, HashSet`1 capturedTags, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationsAsync(IGrouping`2 descriptions, HashSet`1 capturedTags, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiPathsAsync(HashSet`1 capturedTags, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiDocumentAsync(CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Builder.OpenApiEndpointRouteBuilderExtensions.<>c__DisplayClass0_0.<<MapOpenApi>b__0>d.MoveNext()

Expected Behavior

No exception.

Steps To Reproduce

Add [Range(0, 9223372036854775807)] to a long property.

Exceptions (if any)

System.FormatException: The input string '9.223372036854776E+18' was not in a correct format.
   at System.Number.ThrowFormatException[TChar](ReadOnlySpan`1 value)
   at System.Number.ParseDecimal[TChar](ReadOnlySpan`1 value, NumberStyles styles, NumberFormatInfo info)
   at System.Decimal.Parse(String s, IFormatProvider provider)
   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.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 Microsoft.AspNetCore.OpenApi.OpenApiSchemaStore.GetOrAdd(OpenApiSchemaKey key, Func`2 valueFactory)
   at Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.GetOrCreateSchemaAsync(Type type, ApiParameterDescription parameterDescription, Boolean captureSchemaByRef, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetJsonRequestBody(IList`1 supportedRequestFormats, ApiParameterDescription bodyParameter, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetRequestBodyAsync(ApiDescription description, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationAsync(ApiDescription description, HashSet`1 capturedTags, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationsAsync(IGrouping`2 descriptions, HashSet`1 capturedTags, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiPathsAsync(HashSet`1 capturedTags, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiDocumentAsync(CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Builder.OpenApiEndpointRouteBuilderExtensions.<>c__DisplayClass0_0.<<MapOpenApi>b__0>d.MoveNext()

.NET Version

9.0.100-preview.7.24407.12

Anything else?

No response

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Aug 18, 2024
@martincostello martincostello added feature-openapi old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels and removed area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates labels Aug 18, 2024
@martincostello
Copy link
Member

Looks like these two lines of code need updating to pass through at least NumberStyles.AllowExponent through:

schema[OpenApiSchemaKeywords.MinimumKeyword] = decimal.Parse(rangeAttribute.Minimum.ToString()!, CultureInfo.InvariantCulture);
schema[OpenApiSchemaKeywords.MaximumKeyword] = decimal.Parse(rangeAttribute.Maximum.ToString()!, CultureInfo.InvariantCulture);

@mus65
Copy link
Author

mus65 commented Aug 18, 2024

Couldn't this special case double and int to avoid ToString()/Parse completely? The RangeAttribute constructors only support double, int and string.

string would also fail currently, not sure if it can even be mapped to OpenAPI.

see: https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.rangeattribute?view=net-8.0

@captainsafia
Copy link
Member

@mus65 Thanks for filing this issue!

Unfortunately, we're having to deal with a constraint in the underlying OpenApiSchema representation here. The OpenApiSchema.Minimum and OpenApiSchema.Maximum properties are decimal types. Based on the JSON schema validation spec, they are intended to only validate numeric values (see this section in the spec).

So, for the string case, the best approach would probably be to use a schema transformer to define an extension property on the spec to encode minimum and maximum-values for non-numeric types (like dates).

I suspect that long might have similar constraints given limitations on the maximum integer size supported in JavaScript/JSON? My recollection here is the recommendation is to transmit long values as strings when they need to be transmitted over the wire.

I think the best thing to do here would be to catch the exception that is thrown here and provide a clearer message about the constraints of the minimum/maximum type.

I'll mark this as something to doc for .NET 9 and try to fix the exception for .NET 10.

@captainsafia captainsafia added the Docs This issue tracks updating documentation label Aug 22, 2024
@captainsafia captainsafia added this to the 10.0-preview1 milestone Aug 22, 2024
@captainsafia captainsafia added area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc and removed old-area-web-frameworks-do-not-use *DEPRECATED* This label is deprecated in favor of the area-mvc and area-minimal labels labels Aug 22, 2024
@fdcastel
Copy link

fdcastel commented Oct 9, 2024

Just passing to say that I'm having the same problem.

The following code (generated by NSWAG) causes the problem:

        [System.Text.Json.Serialization.JsonPropertyName("the_value")]
        [System.ComponentModel.DataAnnotations.Range(0D, double.MaxValue)]
        public double The_value { get; set; } = default!;

When commenting the Range annotation, the problem doesn't occur:

        [System.Text.Json.Serialization.JsonPropertyName("the_value")]
        //[System.ComponentModel.DataAnnotations.Range(0D, double.MaxValue)]
        public double The_value { get; set; } = default!;

.NET Version: 9.0.100-rc.1.24452.12

@BieleckiLtd
Copy link

Is there a way to ignore certain data annotations when generating OpenApiSchema?

In my case below code is causing similar exception.

[Range(typeof(DateTime), "2000-01-01", "2100-12-31", ErrorMessage = "MRD must be between {1} and {2}")]
public DateTime Mrd { get; set; }
System.FormatException: The input string '2000-01-01' was not in a correct format.
   at System.Number.ThrowFormatException[TChar](ReadOnlySpan`1 value)
   at System.Number.ParseDecimal[TChar](ReadOnlySpan`1 value, NumberStyles styles, NumberFormatInfo info)
   at System.Decimal.Parse(String s, IFormatProvider provider)
   at Microsoft.AspNetCore.OpenApi.JsonNodeSchemaExtensions.ApplyValidationAttributes(JsonNode schema, IEnumerable`1 validationAttributes)

@captainsafia
Copy link
Member

Is there a way to ignore certain data annotations when generating OpenApiSchema?

Unfortunately, not at the moment. I'm hoping to get a fix for this out in .NET 10 Preview 1 and the first servicing release of .NET 9.

In the long-term, it doesn't appear that JSON Schema supports modeling ranges for non-integer values as part of its default validation vocabulary. There were proposals of a formatMinimum and formatMaximum property to support this that were proposed a while ago (see json-schema-org/json-schema-spec#116) but those never panned out. It seems like the general recommended approach is to use regex patterns to specify constraints for dates which is...not ideal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates Docs This issue tracks updating documentation feature-openapi
Projects
None yet
Development

No branches or pull requests

5 participants