Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Validation

Chris McCuller edited this page · 33 revisions

  1. Getting Started

    1. Creating your first project
      1. Create Service from scratch
    2. Your first webservice explained
    3. ServiceStack's new API Design
    4. Designing a REST-ful service with ServiceStack
    5. Example Projects Overview
    6. Learning Resources
  2. Reference

    1. Order of Operations
    2. The IoC container
    3. Configuration and AppSettings
    4. Metadata page
    5. Rest, SOAP & default endpoints
    6. SOAP support
    7. Routing
    8. Service return types
    9. Customize HTTP Responses
    10. Plugins
    11. Validation
    12. Error Handling
    13. Security
    14. Debugging
    15. JavaScript Client Library (ss-utils.js)
  3. Clients

    1. Overview
    2. C#/.NET client
    3. Add ServiceStack Reference
      1. C# Add Reference
      2. F# Add Reference
      3. VB.NET Add Reference
      4. Swift Add Reference
      5. Java Add Reference
    4. Silverlight client
    5. JavaScript client
      1. Add TypeScript Reference
    6. Dart Client
    7. 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

    1. Razor & Markdown Razor
    2. Markdown Razor
  6. Hosts

    1. IIS
    2. Self-hosting
    3. Messaging
    4. Mono
  7. Security

    1. Authentication/authorization
    2. Sessions
    3. Restricting Services
  8. 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. Form Hijacking Prevention
    11. Auto-Mapping
    12. HTTP Utils
    13. Virtual File System
    14. Config API
    15. Physical Project Structure
    16. Modularizing Services
    17. ServiceStack Integration
    18. Embedded Native Desktop Apps
    19. Auto Batched Requests
    20. Versioning
  9. Server Events

    1. Overview
    2. JavaScript Client
    3. C# Server Events Client
    4. Redis Server Events
  10. Encrypted Messaging

    1. Overview
    2. Encrypted Client
  11. Plugins

    1. Auto Query
    2. Server Sent Events
    3. Swagger API
    4. Postman
    5. Request logger
    6. Sitemaps
    7. Cancellable Requests
    8. CorsFeature
  12. Tests

    1. Testing
    2. HowTo write unit/integration tests
  13. ServiceStackVS

    1. Install ServiceStackVS
    2. Add ServiceStack Reference
    3. AngularJS App Template
    4. ReactJS App Template
  14. Other Languages

    1. FSharp
      1. Add ServiceStack Reference
    2. VB.NET
      1. Add ServiceStack Reference
    3. Swift
      1. Swift Add Reference
    4. Java
      1. Add ServiceStack Reference
      2. Android Studio & IntelliJ
      3. Eclipse
  15. Deployment

    1. Deploy Multiple Sites to single AWS Instance
      1. Simple Deployments to AWS with WebDeploy
    2. Advanced Deployments with OctopusDeploy
  16. Install 3rd Party Products

    1. Redis on Windows
    2. RabbitMQ on Windows
  17. Use Cases

    1. Single Page Apps
      1. HTML, CSS and JS Minifiers
    2. Azure
      1. Connecting to Azure Redis via SSL
    3. Logging
    4. Bundling and Minification
    5. NHibernate
  18. Performance

    1. Real world performance
  19. 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
  20. Future

    1. Roadmap
Clone this wiki locally

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.

Validation Feature

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.

ServiceStack's Fluent Validation feature is encapsulated in the ValidationFeature plugin which can be registered in your AppHost with:

Plugins.Add(new ValidationFeature());

FluentValidation for request dtos

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. ServiceStack makes heavy use of rule sets 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.

Register Validatores

All validators have to be registered in the IoC container, a convenient way to register all validators which exist in one assembly is to use RegisterValidators(), e.g:

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

Something went wrong with that request. Please try again.