I'd like to reject an OData request if it has $top option exceeding a global limit. ODataQueryOptions is created manually and I don't know how to correctly trigger its validation.
<PackageReference Include="Microsoft.AspNetCore.OData" Version="7.0.0" />
I do global setup like this:
public static void UseODataRoutes(this IRouteBuilder routeBuilder,
IEdmModel edmModel)
{
routeBuilder.MapODataServiceRoute(RouteName,
RoutePrefix,
containerBuilder => containerBuilder.AddService(ServiceLifetime.Singleton, provider => edmModel));
routeBuilder.Count().Filter().OrderBy().Select().MaxTop(1);
routeBuilder.EnableDependencyInjection();
}
And manually create an ODataQueryOptions<TEntity> instance (we need this direct creation):
public ODataQueryFactory(IHttpContextAccessor httpAccessor, IPerRouteContainer container)
{
_request = httpAccessor.HttpContext.Request;
_odataServiceProvider = container.GetODataRootContainer(ODataExtentions.RouteName);
}
public ODataQueryOptions<TEntity> CreateQueryOptions<TEntity>()
where TEntity : class
{
var model = _odataServiceProvider.GetService<IEdmModel>();
var path = _request.ODataFeature().Path;
var context = new ODataQueryContext(model, typeof(TEntity), path);
var queryOptions = new ODataQueryOptions<TEntity>(context, _request);
return queryOptions;
}
```
The problem is that the global `MaxTop` is ignored leading to successful `GET /foo?$top=10` request even if `MaxTop(1)` has been called.
### Expected result
An exception saying that `$top=10` exceeds the global limit.
### Actual result
200 OK with 10 rows in response.
### Additional detail
However, if I just add:
```cs
queryOptions.Validate(new ODataValidationSettings()
{
MaxTop = 1
});
To my factory method, then the request with $top=10 produces a perfectly looking exception leading to 400 response. That's my goal.
How to automatically trigger this validation or create an ODataValidationSettings instance? Direct ODataValidationSettings creation is an option I'd like to avoid.
P.S. Furthermore, is MaxTop setting should affect the count of returning entities if $top is not set at all?
@ilya-chumakov Thanks for your question. The MaxTop should not affect the count of returned entities as far I know. If you find it otherwise, please let us know.
As far as setting MaxTop is concerned, does setting MaxTop in the Page attribute for your class work?
Even if you don't set MaxTop in the Validation setting, you should see the same error as the MaxTop will be pulled in from your global setup. However, since you are not using the EnableQuery attribute, you will have to call queryOptions.validate( new ODataValidationSettings()).
@KanishManuja-MS thanks, queryOptions.Validate(new ODataValidationSettings()) truly uses global validation settings. Could you consider adding the overload without params in the future? It would make more obvious that filling the settings is not mandatory.
Is it possible to set a default global limit if $top is not presented in the query? I'd like to avoid selecting 1M entities if someone just forgot $top option. queryOptions.Top is read-only so I can't simply rewrite it.
I tried
var edmModel = app.GetOdataEdmModel(
builder =>
{
builder.EntitySet<Foo>(nameof(Foo));
builder.EntityType<Foo>().Page(1, 1);
});
but it doesn't affect the default limit.
@ilya-chumakov Thanks for your input, I will keep that in mind when that part of the code gets touched again.
Maybe what you are looking for here is the PageSize. You can set the page size on your model or in your querysettings. This will allow clients to do queries on any number of records but only a limited number of records will be served at a given time.
Since, the core of the question was answered here. I am closing this issue. @ilya-chumakov If you have any concerns regarding top, you can still add to this thread or reopen this.
@KanishManuja-MS Thanks for your support.
However, in my humble opinion, the validation should cover the case of a query without $top parameter.
For example, if MaxTop(1) limit has been set, it prevents selecting more than 1 entity only if $top is specified. I can easily bypass this validation and get all the entities in db by removing $top from the query.
Most helpful comment
@KanishManuja-MS Thanks for your support.
However, in my humble opinion, the validation should cover the case of a query without
$topparameter.For example, if
MaxTop(1)limit has been set, it prevents selecting more than 1 entity only if$topis specified. I can easily bypass this validation and get all the entities in db by removing$topfrom the query.