6

Is there any way to skips executing the validation rules in the Ruleset if one of them fails.

I have this following rules for my API endpoint

param1, param2, param3

RuleSet  => 
     RuleFor(req => req.param1).NotEmpty().WithMessage("param1 is missing.");
     RuleFor(req => req.param2).NotEmpty().WithMessage("param2 is missing.");
     RuleFor(req => req.param3).NotEmpty().WithMessage("param3 is missing.");
     RuleFor(req => req.param1).Must((req, param1) => IsValidRequest(req)).WithMessage("Invalid request.");

In my IsValidRequest(req) I again have to verify for param1, param2, param3 exist before doing my custom validation as even if the above rules fails all other validations will still continue to try.

anveshtummala
  • 432
  • 4
  • 11

1 Answers1

11

By default, FluentValidation uses Continue cascade mode for invoking validators in rule definition (i.e. it invokes all validators). You should use StopOnFirstFailure mode to stop execution after first validator fails:

ValidatorOptions.CascadeMode = CascadeMode.StopOnFirstFailure;

Note that this will set StopOnFirstFailure mode globally. If you want to specify this mode for particular validators, you can use property of validator class:

public class RequestValidator : AbstractValidator<Request>
{
   public RequestValidator()
   {
       CascadeMode = CascadeMode.StopOnFirstFailure;

       RuleFor(req => req.param1)
          .NotEmpty().WithMessage("param1 is missing.")
          .Must((req, param1) => IsValidRequest(req)).WithMessage("Invalid request.");
       RuleFor(req => req.param2).NotEmpty().WithMessage("param2 is missing.");
       RuleFor(req => req.param3).NotEmpty().WithMessage("param3 is missing.");
   }
}

In the example above Must validator will not be executed if NotEmpty validator fails.


If you want to execute Must validator only when all three parameters are not empty, then When is the best option:

When(req => !String.IsNullOrEmpty(req.param1) 
         && !String.IsNullOrEmpty(req.param2)
         && !String.IsNullOrEmpty(req.param3), () => {
     RuleFor(req => req.param1)
        .Must((req, param1) => IsValidRequest(req)).WithMessage("Invalid request.");
});

You can move precondition check to separate method:

 private bool AllParametersSpecified(Request req)
 {
     return !String.IsNullOrEmpty(req.param1) 
         && !String.IsNullOrEmpty(req.param2)
         && !String.IsNullOrEmpty(req.param3);
 }

And condition will be much more readable:

When(AllParametersSpecified, () => {
     RuleFor(req => req.param1)
        .Must((req, param1) => IsValidRequest(req)).WithMessage("Invalid request.");
});
Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
  • What if my function IsValidRequest need all param1, param2, param3 to be not null.Thank you for the quick reply Sergey. – anveshtummala Apr 26 '17 at 21:56
  • This is great, but I still have one issue with it. I am not able to give users an individual message of param1 is missing or param2 is missing before calling my IsValidRequest function. Now I am restricted to give the error message only for Invalid Request. – anveshtummala Apr 27 '17 at 12:29
  • @anveshtummala just don't remove validation rules for params. They will do validation. And if all params are specified, then IsValidRequest validation will run – Sergey Berezovskiy Apr 27 '17 at 12:43
  • 2
    Easy fix, but for me it seems like some flexibility is missing. CascadeMode = CascadeMode.StopOnFirstFailure; only works inside each Rulefor but not for an each RuleFor inside a Ruleset – anveshtummala Apr 27 '17 at 13:07
  • 3
    @anveshtummala totally agree with you. I would also expect to have an option to stop validation after any validator in any rule fails – Sergey Berezovskiy Apr 27 '17 at 13:10
  • `CascadeMode.Stop` should be used instead of `CascadeMode.StopOnFirstFailure` (deprecated). – Alexei - check Codidact Mar 16 '21 at 15:06