1

I have working code but am hoping there is a better way...

Problem:

  • annotate a parameter in a method used in a minimal API in a way that informs OpenAPI (but does not mislead a future developer -- RangeAttribute is misleading)
  • do validation of a primitive type

Based on this Using DataAnnotation Model Validation in Minimal Api, I have the following validator

public static RouteHandlerBuilder Validate<T>(this RouteHandlerBuilder builder, IValidator<T?> validator, int? position = null)
{
    builder.AddEndpointFilter(RouteHandlerFilter);

    return builder;

    async ValueTask<object?> RouteHandlerFilter(EndpointFilterInvocationContext invocationContext, EndpointFilterDelegate next)
    {
        T? argument = position.HasValue ? invocationContext.GetArgument<T>(position.Value) : invocationContext.Arguments.OfType<T>().FirstOrDefault();
            
        ValidationResult? validationResult = await validator.ValidateAsync(argument).ConfigureAwait(false);

        return validationResult.IsValid ? await next(invocationContext) : TypedResults.ValidationProblem(validationResult.ToDictionary());
    }
}

which is using a Fluent Validations validator

internal class IntRangeValidator : AbstractValidator<int?>
{
    public IntRangeValidator(int min = int.MinValue, int max = int.MaxValue)
    {
        this.RuleFor(i => i).NotNull().InclusiveBetween(min, max);
    }
}

which is added to the route via

// IEndpointRouteBuilder app
app.MapGet( /* ... */ ).Validate(new IntRangeValidator(1));

Since .WithOpenApi(operation => { /* ... */ }) is unable to modify the schema of the parameter, the following is used

internal class PositiveSchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (context.ParameterInfo?.GetCustomAttribute<PositiveAttribute>() != null)
        {
            schema.Minimum = 1;
        }
    }
}
// SwaggerGenOptions options
options.SchemaFilter<PositiveSchemaFilter>();

Questions:

  1. Does anyone have a more compact approach that doesn't need as many moving parts?
  2. Is there some other "magic" method in the route building process that allows access to the parameter info (name, type, custom attributes) that could be used in lieu of the Validate extension method (and without its positional weirdness)?
1
  • An example of a method where the attribute is used public static async Task<Results<NotFound, Ok<WeatherForecast>>> GetForecast( [FromRoute] [Positive] int id, IWeatherRepo repo, CancellationToken cancellationToken) Commented Mar 13, 2024 at 21:49

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.