Conditions
Checking conditions is the sixth step in the request flow. It occurs after filtering and before pre-operation actions. An unlimited number of Conditions may be added to a route. They are run in order against the filtered dataset and the current IApiContext<TModel, TUser>. Conditions return a boolean value indicating whether or not the condition has passed, and can also throw an exception upon failure. If a condition does not pass, an exception of type ConditionFailedException is thrown and the route stops further execution.
Builder Methods
Low-Level Methods
These methods are primarily used when you've implemented your own ICondition<TModel, TUser> and want to attach it to a route.
High-Level Methods
Builder Prefix: Require
These are the methods you'll typically use to set conditions on a route.
- Require(Func<IApiContext<TModel, TUser>, IQueryable<TModel>, Boolean>, String)
- Require(Func<IQueryable<TModel>, Boolean>, String)
- RequireAll(Func<TModel, Boolean>, String)
- RequireAllInput(Func<TModel, Boolean>, String)
- RequireAsync(Func<IApiContext<TModel, TUser>, IQueryable<TModel>, Task<Boolean>>, String)
- RequireAsync(Func<IQueryable<TModel>, Task<Boolean>>, String)
- RequireAtLeast(Int32, String)
- RequireExactly(Int32, String)
- RequireExactlyOne(String)
- RequireExactlyOneInput(String)
- RequireInput(Func<TModel[], Boolean>, String)
- RequireInputAsync(Func<TModel[], Task<Boolean>>, String)
- RequireInputHasAtLeast(Int32, String)
- RequireInputHasExactly(Int32, String)
- RequireNonEmpty(String)
- RequireNonEmptyInput(String)
- RequireParameter<T>(ParameterRetriever, Func<T, Boolean>, String)
- RequireParameterOpt<T>(ParameterRetriever, Func<T, Boolean>, String)
- RequireQuery<T>(String, Func<T, Boolean>, String)
- RequireQueryOpt<T>(String, Func<T, Boolean>, String)
- RequireRoute<T>(String, Func<T, Boolean>, String)
- RequireRouteOpt<T>(String, Func<T, Boolean>, String)
Warning
Even though the RequireProperty
methods share a similar name and purpose with the condition methods, they actually set ParsingOptions on the IBodyParser<TModel> and do not enforce conditions.
Examples
Requiring a condition on every element in the dataset
The RequireAll(Func<TModel, Boolean>, String) method can be used to setup one Condition that checks every single element in the dataset.
The following example ensures that every element in the filtered dataset was created recently.
app.UseSeltzr<BlogPost>(api => {
api.RequireAll(m => m.DateCreated < DateTime.Now.AddDays(-30))
});
The RequireAllInput(Func<TModel, Boolean>, String) works in the same way with the parsed request body.
The following example ensures that the PublishDate
of every element in the parsed body is in the future.
app.UseSeltzr<BlogPost>(api => {
api.RequireAll(m => m.PublishDate > DateTime.Now)
});
Note
Even if your body parser doesn't accept arrays, the RequireAllInput(Func<TModel, Boolean>, String) method will still work fine.
Using Conditions with Filters
Conditions can be especially powerful when used in conjunction with parameter Filters.
The following example shows how to set up a Filter on a query parameter and restricting its bounds to certain range.
app.UseSeltzr<MyModel>(api => {
api
.FilterByQueryEqual(m => m.Value)
.RequireQuery<int>("value", v => v > 10 && v < 50);
});
Tip
These methods accept a type parameter to parse the parameter before it's passed to the lambda method. To avoid parsing the parameter, use the string
type.
Note
Like the FilterBy
methods, Require
methods that work with request parameters have Opt
alternatives that only apply the condition when the request parameter is present
Setting a custom error message on a Condition
All Condition builder methods have a failureMessage
parameter that defaults to null
, which will use a default error message. To customize the exception message and, therefore, the error message shown when using the SimpleExceptionHandler, provide a value for the failureMessage
parameter.
app.UseSeltzr<MyModel>(api => {
api.RequireAllInput(m => m.Value < 50, "Value must be less than 50 for all provided models");
});
Using the Require
method for more complex logic
The Require
and RequireAsync
methods both take in a function, which either accepts the dataset or both the dataset and IApiContext<TModel, TUser>, and returns a boolean value indicating whether or not the condition was met.
This example verifies that the Value
property of models with ValueType
set to "Integer"
doesn't have a decimal component.
app.UseSeltzr<MyModel>(api => {
api.Require(d => d.Where(m => m.ValueType == "Integer").All(m => (m.Value - (int)m.Value) < 0.00001));
});