我们希望利用 JsonSchema.Net 来验证发送到 ASP.NET Core 端点(最小 API)的 json。失败结果还必须以 RFC
ProblemDetails
格式返回。这是可行的,但需要跳过一些障碍;
ValidatingJsonConverter
添加到最小 API 使用的 JsonSerializerOptions
转换器 (builder.Services.ConfigureHttpJsonOptions
)ProblemDetails
构建 JsonException
结果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);
}
}));
手动构建架构
public static readonly JsonSchema InvokeRequestSchema =
new JsonSchemaBuilder()
.Type(SchemaValueType.Object)
.Required("appUserId")
.Properties(
("appUserId", new JsonSchemaBuilder()
.Type(SchemaValueType.String)
.Format(Formats.Uuid)
)
);
使用
[JsonSchema]
属性将该模式应用到我们的 DTO
[JsonSchema(typeof(InvokeRequest), nameof(InvokeRequestSchema))]
public class InvokeRequest
这一切都有效
{
"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\""
]
}
}
我是否正确地说,更好的做法是提前从类型(使用例如
FromType<>(InvokeRequest)
)生成模式,将该 JSON 模式写入磁盘,然后在应用程序启动时将其解析回以用于验证?
我更喜欢硬编码版本。 它是静态的,并且以推断意图的方式非常明确地定义模型。 其次,您可以微调手动构建的架构(无论是使用构建器硬编码还是以 JSON/YAML 手写)。 这一代人可能并不总能得到你想要的一切。
也就是说,当您更改模型时,硬编码方法不会更新。 拥有一个从模型生成并保存为外部文件的构建过程可能很有价值。 这实际上取决于您想要什么以及什么对您来说更容易。
如果生成的模式足以满足您的需求,那么我会走这条路。 我还会使用某种定期手动检查或验证测试(例如使用Verify之类的东西)来确保这一代正在按照您的预期进行。
此外,关于您返回的错误,请阅读我的博客文章解释 JSON 架构输出。 试图找出哪些错误与故障相关或对用户有用可能很棘手。