-
Notifications
You must be signed in to change notification settings - Fork 0
Validation
ServiceStack includes a very clean way to validate request DTOs. Even contextual validation for each HTTP method (GET, POST, ...) is supported.
First in the app host the validation mechanism must be initialized:
ValidationFeature.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...).
Tip: First read the documentation about FluentValidation before you continue reading
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(ApplyTo.Get, () =>
{
RuleFor(r => r.Count).GreaterThan(10);
});
//Validation rules for POST and PUT request
RuleSet(ApplyTo.Post | ApplyTo.Put, () =>
{
RuleFor(r => r.Company).NotEmpty();
});
}
}
Info: ServiceStack adds another extension method named
RuleSet
which can handleApplyTo
enum flags. This method doesn't exist in the core FluentValidation framework.
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.
All validators have to be registered in the IoC container:
//This method scans the assembly for validators
container.RegisterValidators(typeof(ValidatedValidator).Assembly);
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": "GreaterThan",
"Message": "'Age' must be greater than '0'.",
"Errors": [
{
"ErrorCode": "GreaterThan",
"FieldName": "Age",
"Message": "'Age' must be greater than '0'."
},
{
"ErrorCode": "NotEmpty",
"FieldName": "Company",
"Message": "'Company' should not be empty."
}
]
}
As you can see, the ErrorCode
and the FieldName
provide an easy way to handle the validation error at the client side.
If you want, you can also configure a custom ErrorCode
for a validation rule:
RuleFor(x => x.Name).NotEmpty().WithErrorCode("ShouldNotBeEmpty");
If the rule fails, the JSON response will look like that:
...
{
"ErrorCode": "ShouldNotBeEmpty",
"FieldName": "Name",
"Message": "'Name' should not be empty."
}
...
Of course FluentValidation can be used for any other classes (not only request DTOs), 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);
}
}
Info: If FluentValidation isn't used for request DTOs, it behaves the same as documented in the Fluent Validation documentation.
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 ValidationErrorException and throw this one
//The errors will be serialized in a clean, human-readable way (as the above JSON example)
throw result.ToException();
}
}
}
- Why ServiceStack?
- What is a message based web service?
- Advantages of message based web services
- Why remote services should use separate DTOs
- Getting Started
- Reference
- Clients
- Formats
- View Engines 4. Razor & Markdown Razor
- Hosts
- Advanced
- Configuration options
- Access HTTP specific features in services
- Logging
- Serialization/deserialization
- Request/response filters
- Filter attributes
- Concurrency Model
- Built-in caching options
- Built-in profiling
- Messaging and Redis
- Form Hijacking Prevention
- Auto-Mapping
- HTTP Utils
- Virtual File System
- Config API
- Physical Project Structure
- Modularizing Services
- Plugins
- Tests
- Other Languages
- Use Cases
- Performance
- How To
- Future