Skip to content
mythz edited this page Mar 20, 2013 · 31 revisions

Validation and Error Handling

As validation and error handling is an essential part of developing services, ServiceStack provides a rich array of error handling options that work intuitively out-of-the-box.

Optimized for developer happiness ServiceStack allows you to idiomatically throw C# exceptions directly in your services and trivially consume them on the client with minimal effort, intuitively and conventionally - allowing the opportunity to inject generic error handling routines to handle errors for all your web services.

For bonus REST Internet points the most appropriate HTTP Status code is returned based upon the C# Exception type.

Typed, Structured Exceptions end-to-end

The error handling support works end-to-end where all errors get auto-serialized into your Response DTO and re-hydrated into a C# Exception on ServiceStack's generic Service Clients. This allows you to idiomatically treat errors like normal C# Exceptions - providing easy access to rich, structured error messages in your clients.

JavaScript support included

To make it trivial to consume errors in JavaScript, you can use the lightweight ss-validation.js JavaScript library to trivially bind your response errors to your HTML form fields with a single line of code.


All Error handling and validation options described below are treated in the same way - serialized into the ResponseStatus property of your Response DTO making it possible for your clients applications to generically treat all Web Service Errors in the same way.

Note: The response DTO must follow the {Request DTO}Response naming convention and has to be in the same namespace as the request DTO!

public class Hello {}

public class HelloResponse //Follows naming convention and is in the same namespace as 'Hello'
{
    public ResponseStatus ResponseStatus { get; set; } //Exception gets serialized here
}

If now an exception occurs in the service implementation, the exception is serialized.

Example JSON output:

{
    "ResponseStatus": {
         "ErrorCode": "NotSupportedException",
         "Message": "..."
    }
}

It's up to the client how to handle this service error.

Throw a C# Exception

The easiest way to generate an Error in ServiceStack is to simply throw a C# Exception:

public object Post(User request) 
{
	if (string.IsNullOrEmpty(request.Name))
		throw new ArgumentNullException("Name");
}

By Default C# Exceptions:

  • Inheriting from ArgumentException are returned as a HTTP StatusCode of 400 BadRequest
  • NotImplementedException is returned as a 405 MethodNotAllowed
  • Other normal C# Exceptions are returned as 500 InternalServerError

All Exceptions gets injected into the ResponseStatus property of your Response DTO that is serialized into your ServiceClient's preferred Content-Type making error handling transparent regardless of your preferred format - i.e. the same C# Error handling code can be used for all ServiceClients.

try 
{
    var client = new JsonServiceClient(BaseUri);
    var response = client.Send<UserResponse>(new User());
} 
catch (WebServiceException webEx) 
{
    /*
      webEx.StatusCode  = 400
      webEx.ErrorCode   = ArgumentNullException
      webEx.Message     = Value cannot be null. Parameter name: Name
      webEx.StackTrace  = (your Server Exception StackTrace - if DebugMode is enabled)
      webEx.ResponseDto = (your populated Response DTO)
      webEx.ResponseStatus   = (your populated Response Status DTO)
      webEx.GetFieldErrors() = (individual errors for each field if any)
    */
}

Enabling StackTraces

By default display StackTraces in your Response DTOs are disabled, but they're a good to have for development, which you can enable with:

SetConfig(new EndpointHostConfig { DebugMode = true });

Customized Error Messages

If you want even finer grained control of your HTTP errors you can either throw or return a HttpError letting you customize the Http Headers and Status Code and HTTP Response body to get exactly what you want on the wire:

public object Get(User request) {
       throw HttpError.NotFound("User {0} does not exist".Fmt(requst.Name));
}

the above is a short-hand for new HttpError(HttpStatusCode.NotFound, string.Format("User {0} does not exist", request.Name)) which returns a 404 NotFound StatusCode on the wire.

FluentValidation for request dtos

For more complex validation and to be able to return multiple validation errors ServiceStack includes the excellent Fluent Validation library by @JeremySkinner - a very clean and DSL-like way to validate request DTOs. Even contextual validation for each HTTP method (GET, POST, ...) is supported.

The example below uses this request dto for validation:

[Route("/users")]
public class User
{
    public string Name { get; set; }
    public string Company { get; set; }
    public int Age { get; set; }
    public int Count { get; set; }
    public string Address { 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 interface IAddressValidator
{
    bool ValidAddress(string address);
}

public class AddressValidator : IAddressValidator
{
    public bool ValidAddress(string address)
    {
	return address != null
	    && address.Length >= 20
	    && address.Length <= 250;
    }
}

public class UserValidator : AbstractValidator<User>
{
    public IAddressValidator AddressValidator { get; set; }
    
    public UserValidator()
    {
        //Validation rules for all requests
        RuleFor(r => r.Name).NotEmpty();
        RuleFor(r => r.Age).GreaterThan(0);
        RuleFor(x => x.Address).Must(x => AddressValidator.ValidAddress(x));

        //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 handle ApplyTo 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.

Like services registered in the IoC container, validators are also auto-wired, so if there's a public property which can be resolved by the IoC container, the IoC container will inject it. In this case, the IoC container will resolve the property AddressValidator, if an object of the type IAdressValidator was registered.

Tip: You can access the http request in a validator by implementing IRequiresHttpRequest. Attention, it's lazily loaded and so only available in the validation delegates.

All validators have to be registered in the IoC container. So add this code to the app host:

First, enable the validation plugin:

//Enable the validation feature
Plugins.Add(new ValidationFeature());

If you want to register all validators which exist in one assembly, you can do this:

//This method scans the assembly for validators
container.RegisterValidators(typeof(UserValidator).Assembly);

Optionally you can also register each validator individually:

//Add the IAdressValidator which will be injected into the UserValidator
container.Register<IAddressValidator>(new AddressValidator());

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."
}

Use FluentValidation everywhere!

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 : Service
{
    //You should have registered your validator in the IoC container to inject the validator into this property
    public IValidator<TestClass> Validator { get; set; }

    public object Get(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();
        }

    }
}

Populating the Response DTO Manually

All the error handling methods illustrated above are just sugar coating for serializing exceptions into your Response DTOs ResponseStatus property. You don't have use any of this as you can simply populate the ResponseStatus property yourself, coupled with the HttpResult class you have complete control of your HTTP Response - this gives you the necessary freedom so you are able to define your own Error handling API and helpers if you have an existing validation library that you would like to use instead.



  1. Getting Started
    1. Create your first webservice
    2. Your first webservice explained
    3. ServiceStack's new API Design
    4. Designing a REST-ful service with ServiceStack
    5. Example Projects Overview
  2. Reference
    1. Order of Operations
    2. The IoC container
    3. Metadata page
    4. Rest, SOAP & default endpoints
    5. SOAP support
    6. Routing
    7. Service return types
    8. Customize HTTP Responses
    9. Plugins
    10. Validation
    11. Error Handling
    12. Security
  3. Clients
    1. Overview
    2. C# client
    3. Silverlight client
    4. JavaScript client
    5. Dart Client
    6. MQ Clients
  4. Formats
    1. Overview
    2. JSON/JSV and XML
    3. ServiceStack's new HTML5 Report Format
    4. ServiceStack's new CSV Format
    5. MessagePack Format
    6. ProtoBuf Format
  5. View Engines 4. Razor & Markdown Razor
    1. Markdown Razor
  6. Hosts
    1. IIS
    2. Self-hosting
    3. Mono
  7. Advanced
    1. Configuration options
    2. Access HTTP specific features in services
    3. Logging
    4. Serialization/deserialization
    5. Request/response filters
    6. Filter attributes
    7. Concurrency Model
    8. Built-in caching options
    9. Built-in profiling
    10. Messaging and Redis
    11. Form Hijacking Prevention
    12. Auto-Mapping
    13. HTTP Utils
    14. Virtual File System
    15. Config API
    16. Physical Project Structure
    17. Modularizing Services
  8. Plugins
    1. Sessions
    2. Authentication/authorization
    3. Request logger
    4. Swagger API
  9. Tests
    1. Testing
    2. HowTo write unit/integration tests
  10. Other Languages
    1. FSharp
    2. VB.NET
  11. Use Cases
    1. Single Page Apps
    2. Azure
    3. Logging
    4. Bundling and Minification
    5. NHibernate
  12. Performance
    1. Real world performance
  13. How To
    1. Sending stream to ServiceStack
    2. Setting UserAgent in ServiceStack JsonServiceClient
    3. ServiceStack adding to allowed file extensions
    4. Default web service page how to
  14. Future
    1. Roadmap
Clone this wiki locally