Incorrect nullable annotation for array of nullable items
See original GitHub issueI have a need in my project to fine-tune the validation of collection items. And I would like to reflect information about these constraints in the generated OpenAPI schema. However, I ran into one problem that doesn’t seem to have a workaround.
Since the built-in validation attributes do not support validation of collection items, I use custom validation attributes and custom schema filters (which use these attributes) to enrich the generated schema with appropriate metadata. Filters check if a member has a matching attribute and override the corresponding schema metadata using that attribute. Below are examples of a model and one such filter that should override the nullable
annotation (examples are simplified for brevity).
public class TestModel
{
[NonNullableItems]
public List<int?> Data1 { get; set; }
public List<int?> Data2 { get; set; }
}
public class NonNullableItemsSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
var attribute = context.MemberInfo
?.GetCustomAttributes<NonNullableItemsAttribute>()
.FirstOrDefault();
if (attribute != null)
{
schema.Items.Nullable = false;
}
}
}
I expect that for the described model using the described filter, the OpenAPI schema shown below will be generated.
"TestModel": {
"type": "object",
"properties": {
"data1": {
"type": "array",
"nullable": true,
"items": {
"type": "string",
"nullable": false
}
},
"data2": {
"type": "array",
"nullable": true,
"items": {
"type": "string",
"nullable": true
}
},
},
"additionalProperties": false
}
In fact, a schema is generated that does not contain nullable
annotations for array items, i.e. the default value is assumed (false
, according to the OpenAPI specification). This scheme incorrectly describes the behavior of the API, since its implementation allows null
values as items of the data1
array.
I checked the source code of the library and found out that this occurs because nullable
annotation is explicitly populated only for object property schemas. In all other cases, including schemas for array items, the default value is retained.
I could get around this limitation by explicitly populating nullable
in my filter depending on the attribute presence (and not just overwriting it if the attribute is present). However, this requires determining whether the corresponding member is nullable, which implies analysis of the underlying data type, a nullable context, a serializer behavior. This problem is difficult to solve within a schema filter due to lack of data. Moreover, it is impractical to solve it within a schema filter, since this will lead to duplication of a logic already implemented within the library.
The described behavior is actual for version 6.1.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:12 (6 by maintainers)
@kristinn-is Yes I did and the problem is that List is treated like a Object and the nullabillity information for the contained type is not used.
There are several ways to fix this so I need to discuss this with @domaindrivendev before making the PR.
I browsed the Open API specification and I couldn’t find that it was allowed to set the nullable attribute on the items type attribute, so I think that the result from this would be something like this:
And then the nullability is set on the ref object. Ref would only be used if the List<> contains a nullable reference type, otherwise it would look the same as today.
@domaindrivendev I’m thinking about modifying
public DataContract GetDataContractForType(Type type)
and introduce a new check for “GenericList” as a new DataType. Then adding a newDataContract.ForGenericList
, and finally modifyingprivate OpenApiSchema GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository)
to hande the new DataType and to setreturnAsReference = true
if the List<> has a nullable type. Let me know if you want me to proceed.PR fixing problems with nullable reference types in dictionary just got merged so I’ll take a look at this next week 😃