Code: Select all
AddValidation()Ich habe einen modularen Aufbau mit mehreren Baugruppen:
- Host () – der Einstiegspunkt, an dem die DI-Verkabelung erfolgt (
Code: Select all
Host.Api)Code: Select all
Program.cs - Funktion () – enthält Slices/Endpunktdefinitionen + DTOs
Code: Select all
Feature.Users - Geteilt () – enthält eigenwillige MapPost-Wrapper/-Konventionen (CQRS-Stil)
Code: Select all
Shared.Endpoints
Eingebaute Validierungsunterstützung für Minimal-APIs
Diese Frage hat mir geholfen zu verstehen, dass Validierungsmetadaten pro Assembly generiert werden und ich möglicherweise Resolver von anderen registrieren muss Assemblies.
Szenario Nr. 1: Endpunkt direkt in der Feature-Assembly zugeordnet (funktioniert)
Code: Select all
// App.FeatureA
public static class FeatureAEndpoints
{
public static void Map(IEndpointRouteBuilder app)
{
app.MapPost("/items", (CreateItemRequest request) => Results.Ok());
}
}
public sealed class CreateItemRequest
{
[Required]
public string? Name { get; set; }
}
Code: Select all
builder.Services.AddValidation(options =>
{
foreach (var resolver in DiscoverResolvers(typeof(FeatureAEndpoints).Assembly))
options.Resolvers.Add(resolver);
});
static IEnumerable DiscoverResolvers(Assembly assembly) =>
assembly.GetTypes()
.Where(t => typeof(IValidatableInfoResolver).IsAssignableFrom(t)
&& !t.IsAbstract
&& t.GetConstructor(Type.EmptyTypes) != null)
.Select(t => (IValidatableInfoResolver)Activator.CreateInstance(t)!);
Hinweis: Die Feature-Assembly muss auch einen .AddValidation()-Aufruf enthalten (auch wenn in meinem Code nicht direkt darauf verwiesen wird!), damit der Validierungsquellengenerator für diese Assembly ausgeführt wird. Andernfalls ist möglicherweise kein IValidatableInfoResolver vorhanden, der durch Scannen ermittelt werden kann.
Zum Beispiel musste ich diese Datei zu dieser Assembly hinzufügen:
Code: Select all
///
/// Required so validation source generation sees an .AddValidation() inside this assembly.
/// See https://github.com/dotnet/AspNetCore.Docs/issues/35090#issuecomment-3661156506
///
internal static class ValidationCodegenTrigger
{
public static IServiceCollection Trigger(this IServiceCollection services)
=> services.AddValidation();
}
Jetzt verschiebe ich den eigentlichen MapPost-Aufruf in eine gemeinsam genutzte Bibliothek:
Code: Select all
// App.FeatureA
public static class FeatureAEndpoints
{
public static void Map(IEndpointRouteBuilder app)
{
app.MapCommand("/items"); //it's calling an extension method
}
}
public sealed class CreateItemRequest
{
[Required]
public string? Name { get; set; }
}
Code: Select all
// App.SharedEndpoints
public static class CommandEndpointExtensions
{
public static RouteHandlerBuilder MapCommand(
this IEndpointRouteBuilder app,
string pattern)
where TRequest : class
{
return app.MapPost(pattern, (TRequest request) => Results.Ok());
}
}
- JSON-Bindung funktioniert
- OpenAPI-Generierung funktioniert
- Endpunkt wird ausgeführt
- Validierung wird nicht mehr ausgeführt (ungültige Anfragen geben nicht 400 zurück)
Fragen
- Warum funktioniert die integrierte Minimal-API-Validierung in Szenario Nr. 2 nicht mehr?
- Warum reicht „Resolver aus anderen Assemblys scannen und registrieren“ hier nicht aus?
- Was sind die richtigen Problemumgehungen und wann sind sie jeweils erforderlich?
Mobile version