Skip to content
Arxisos edited this page Dec 27, 2011 · 28 revisions

FluentValidation for request dtos

First in the app host the validation mechanism must be initialized:

ValidationHandler.Init(this);

This request dto should be validated:

[RestService("/users")]
public class User
{
    public string Name { get; set; }
    public string Company { get; set; }
    public int Age { get; set; }
    public int Count { get; set; }
}

The validation rules for this request dto are made with FluentValidation (http://fluentvalidation.codeplex.com/documentation). ServiceStack makes heavy use of rule sets (http://fluentvalidation.codeplex.com/wikipage?title=CreatingAValidator&referringTitle=Documentation&ANCHOR#RuleSets) to provide different validation rules for each HTTP method (GET, POST, PUT...).

public class ValidatedValidator : AbstractValidator<Validated>
{
    public ValidatedValidator()
    {
        //Validation rules for all requests
        RuleFor(r => r.Name).NotEmpty();
        RuleFor(r => r.Age).GreaterThan(0);

        //Validation rules for GET request
        RuleSet(HttpMethods.Get, () =>
            {
                RuleFor(r => r.Count).GreaterThan(10);
            });

        //Validation rules for POST request
        RuleSet(HttpMethods.Post, () =>
            {
                RuleFor(r => r.Company).NotEmpty();
            });
    }
}

Warning: If a validator for a request dto is created, all rules which aren't in any rule set are executed + the rules in the matching rule set. Normally FluentValidation only executes the matching rule set and ignores all other rules (whether they're in a rule set or not) and the rules which don't belong to any rule set are normally only executed, if no rule set-name was given to the validate method of the validator.

This validator has to be registered in the IoC container as IValidator<User>. This can be made in the app host:

container.Register<IValidator<Validated>>(new ValidatedValidator());

Now the service etc can be created and the validation rules are checked every time a request comes in.

If you try now for example to send this request:

POST localhost:50386/validated
{
    "Name": "Max"
} 

You'll get this JSON response:

{
    "ErrorCode": "'Age' must be greater than '0'.",
    "Message": "' age' must be greater than ' 0'.",
    "Errors": [
        {
            "ErrorCode": "'Age' must be greater than '0'.",
            "FieldName": "Age",
            "Message": "' age' must be greater than ' 0'."
        },
        {
            "ErrorCode": "'Company' should not be empty.",
            "FieldName": "Company",
            "Message": "' company' should not be empty."
        }
    ]
}

Use FluentValidation everywhere!

Of course FluentValidation can be used for any other classes, too:

public class TestClass
{
    public string Text { get; set; }
    public int Length { get; set; }
}

Now the validator:

public class TestClassValidator : AbstractValidator<TestClass>
{
    public TestClassValidator()
    {
        RuleFor(x => x.Text).NotEmpty();
        RuleFor(x => x.Length).GreaterThan(0);
    }
}

Inside some service code you can validate an instance of this class:

public class SomeService : RestServiceBase<Validated>
{
    //You should have registered your validator in the IoC container to inject the validator into this property
    public IValidator<TestClass> Validator { get; set; }

    public override object OnGet(Validated request)
    {
        TestClass instance = new TestClass();

        ValidationResult result = this.Validator.Validate(instance);

        if (!result.IsValid)
        {
            //The result will be serialized into a SerializableValidationException and throw this one
            //The erros will be serialized in a clean way (as the above JSON example)
            result.Throw();
        }

    }
}

Clone this wiki locally