Would like to manually validate a request dto, but the validator automatically validates before the call to the Post request method.
Is it possible to tell a service or service method to not automatically validate a request?
Using the information from the validation doc, it seems this would work if it wasn’t a validator for a request dto that is registered appHost.RegisterAs<MyFormValidator, IValidator<MyForm>>().
The request dto validator is registered:
public class MyFormFeature : IPlugin {
public void Register(IAppHost appHost) {
appHost.RegisterAs<MyFormValidator, IValidator<MyForm>>();
}
}
So the validator automatically executes prior to the call to the MyFormService.Post(MyForm form) method, so only valid requests go thru.
[Route("/my-form/", "POST")]
public class MyForm : IReturn<MyForm>
{
// props
}
public class MyFormService : Service
{
public IValidator<MyForm> Validator { get; set; }
public object Post(MyForm request)
{
var validationResult = Validator.Validate(request);
// always valid, b/c only valid requests get thru
if (!validationResult.IsValid)
{
// do some things b/c it's invalid
throw validationResult.ToException();
}
// biz logic
return request;
}
}
If instead of registering the Validator in the MyFormFeature plugin, we manually instantiate it in the MyFormService e.g.
public class MyFormService : Service
{
private IValidator<MyForm> Validator => new MyFormValidator();
public object Post(MyForm request)
{
var validationResult = Validator.Validate(request);
// this throws a NullReferenceException
if (!validationResult.IsValid)
{
// do some things b/c it's invalid
throw validationResult.ToException();
}
// biz logic
return request;
}
}
Then we get a 'validationResult.IsValid' threw an exception of type 'System.NullReferenceException'
Changing it to the below and injecting IRequest into the validator seems to get rid of the NullReferenceException, however .IsValid always returns true. Even with an email value of test it returns true. If we switch back to the IoC registered validator, it returns correctly with false.
public class MyFormService : Service
{
public object Post(MyForm request)
{
var validator = new MyFormValidator() { Request = Request };
var validationResult = validator.Validate(request);
// this always returns true, but when switched to IoC Validator it is correctly false
if (!validationResult.IsValid)
{
// do some things b/c it's invalid
throw validationResult.ToException();
}
// biz logic
return request;
}
}
Update showing validator code:
public class MyFormValidator : AbstractValidator<MyForm>
{
public MyFormValidator()
{
RuleSet(
ApplyTo.Put | ApplyTo.Post,
() =>
{
RuleFor(x => x.Email).EmailAddress();
});
}
}
Was going to try to build a method similar, but looks like ValidationContext.Request prop is set to readonly. Setter is internal.
There is a note on the Request prop //Migration: Needs to be injected in Clone(), CloneForChildValidator() + all new ValidationContext()
Not sure if that comment is relevant.
I’ve just made ServiceStack’s Validate() method public (now available from v5.7.1 on MyGet) which you can call with:
validator.Validate(base.Request, request);
I’ve also added a SetRequest() method on ValidationContext in-case you need it in addition to validator.Validate(), let me know if you end up not using it as I’d like to remove it.
You can remove the SetRequest() method on ValidationContext, it isn’t needed.
Everything works with validator.Validate(base.Request, request);
Kind of odd that without base.Request param Validate(...) doesn’t work b/c .IsValid always returns true. Hard to realize that you need base.Request.
One other thing, the new validator.Validate(base.Request, request) method is async, was caught off guard that it wasn’t named .ValidateAsync(...), since there is already other .ValidateAsync(...) methods.
Does this mean there isn’t going to be a sync (non-async) method with the .Validate(base.Request, request)?
No the async Validate() does both sync/async validation. If you know the validator is not async you can block on validator.Validate(...).Result without issue.
Yeah since it’s become public in this release, best to name it appropriately at the start. I’ve also added a sync Validate() to match Fluent Validation APIs, which will throw a NotSupportedException if calling an async validator in this commit.
I’ll let you know when it passes CI and is published on MyGet.