Код: Выделить всё
AddValidation()У меня есть модульная установка с несколькими сборками:
- Хост () — точка входа, где происходит подключение DI (
Код: Выделить всё
Host.Api)Код: Выделить всё
Program.cs - Функция () – содержит определения срезов/конечных точек + DTO
Код: Выделить всё
Feature.Users - Shared () — содержит самоуверенные оболочки/соглашения MapPost (стиль CQRS).
Код: Выделить всё
Shared.Endpoints
Встроенная поддержка проверки для минимальных API
Этот вопрос помог мне понять, что метаданные проверки генерируются для каждой сборки, и мне может потребоваться зарегистрировать преобразователи из других сборок.
Сценарий № 1: конечная точка сопоставлена непосредственно в сборке объекта (работает)
Код: Выделить всё
// 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; }
}
Код: Выделить всё
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)!);
Примечание: сборка функций также должна содержать вызов .AddValidation() (даже если на него нет прямых ссылок в моем коде!), чтобы генератор источника проверки работал для этой сборки; в противном случае может отсутствовать IValidatableInfoResolver, который можно было бы обнаружить при сканировании.
Например, мне пришлось добавить этот файл в эту сборку:
Код: Выделить всё
///
/// 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();
}
Теперь я перемещаю фактический вызов MapPost в общую библиотеку:
Код: Выделить всё
// 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; }
}
Код: Выделить всё
// 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 работает
- Генерация OpenAPI работает
- Конечная точка выполняется
- Проверка больше не выполняется (недопустимые запросы не возвращают 400)
Вопросы
[*]Почему встроенная минимальная проверка API перестает работать в сценарии №2?
[*]Почему здесь недостаточно «сканировать и регистрировать преобразователи из других сборок»?
[*]Каковы правильные обходные пути и когда требуется каждый из них?
Подробнее здесь: https://stackoverflow.com/questions/798 ... ping-is-in
Мобильная версия