We want to make use of JsonSchema.Net to validate json sent to Asp.Net Core endpoints (Minimal APIs). Results for failures must also be returned in the RFC ProblemDetails format. This is doable but required jumping through a few hoops;
- Adding the
ValidatingJsonConverter
to theJsonSerializerOptions
Converters used by Minimal APIs (builder.Services.ConfigureHttpJsonOptions
) - Creating an Exception handler that could build the ProblemDetails result from the
JsonException
thrown by JsonSchema.Net
app.UseExceptionHandler(exceptionHandlerApp
=> exceptionHandlerApp.Run(async context =>
{
IExceptionHandlerFeature feature = context.Features.Get<IExceptionHandlerFeature>() ?? throw new Exception("Unable to get error feature");
if (feature.Error.InnerException is JsonException e && e.Data["validation"] is EvaluationResults validationResults && validationResults.HasDetails)
{
Dictionary<string, string[]> errors = validationResults.Details.Where(detail => detail.HasErrors).ToDictionary(detail => detail.InstanceLocation.ToString(), detail =>
{
return detail.Errors!.Select(error => error.Value).ToArray();
});
await Results.ValidationProblem(errors).ExecuteAsync(context);
}
}));
- Manually building a schema
public static readonly JsonSchema InvokeRequestSchema =
new JsonSchemaBuilder()
.Type(SchemaValueType.Object)
.Required("appUserId")
.Properties(
("appUserId", new JsonSchemaBuilder()
.Type(SchemaValueType.String)
.Format(Formats.Uuid)
)
);
}
- Applying that schema to our DTO with the
[JsonSchema]
attribute
[JsonSchema(typeof(InvokeRequest), nameof(InvokeRequestSchema))]
public class InvokeRequest
This all works
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"/appUserId": [
"Value does not match format "uuid""
]
}
}
Would I be correct in saying a better practice would be to generate the schema from the type (using e.g FromType<>(InvokeRequest)
) ahead of time, writing that JSON schema to disk and then parsing it back in on application startup to use for validation?