Skip to content
Demis Bellot edited this page Dec 3, 2013 · 1 revision

Throwing C# Exceptions

In most cases you won't need to be concerned with ServiceStack's error handling since it provides native support for the normal use-case of throwing C# Exceptions, e.g.:

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
  • UnauthorizedAccessException is returned as 403 Forbidden
  • Other normal C# Exceptions are returned as 500 InternalServerError

All Exceptions get 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 displaying StackTraces in Response DTOs are only enabled in Debug builds, although this behavior is overridable with:

SetConfig(new EndpointHostConfig { DebugMode = true });

Error Response Types

The Error Response that gets returned when an Exception is thrown varies on whether a conventionally-named {RequestDto}Response DTO exists or not.

If it exists:

The {RequestDto}Response is returned, regardless of the service method's response type. If the {RequestDto}Response DTO has a ResponseStatus property, it is populated otherwise no ResponseStatus will be returned. (If you have decorated the {ResponseDto}Response class and properties with [DataContract]/[DataMember] attributes, then ResponseStatus also needs to be decorated, to get populated).

Otherwise, if it doesn't:

A generic ErrorResponse gets returned with a populated ResponseStatus property.

The Service Clients transparently handles the different Error Response types, and for schema-less formats like JSON/JSV/etc there's no actual visible difference between returning a ResponseStatus in a custom or generic ErrorResponse - as they both output the same response on the wire.

Customized Error Messages

If you want even finer grained control of your HTTP errors you can either throw or return an 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.

Fine-grained control over serialization of custom C# Exceptions

In the end all Web Service Exceptions are simply serialized into a ResponseStatus DTO which by default, is serialized by convention.

In addition to the above options, you can override the serialization of ad-hoc exceptions by implementing the IResponseStatusConvertible.ToResponseStatus() method and have it return your own populated ResponseStatus instance instead.

Customize handling of C# Exceptions

Use Config.MapExceptionToStatusCode to change what error codes are returned for different exceptions, e.g.:

SetConfig(new EndpointHostConfig { 
    MapExceptionToStatusCode = {
        { typeof(CustomInvalidRoleException), 403 },
        { typeof(CustomerNotFoundException), 404 },
    }
});

Use Config.CustomHttpHandlers for specifying custom HttpHandlers to use with specific error status codes, e.g.:

SetConfig(new EndpointHostConfig {
    CustomHttpHandlers = {
        { HttpStatusCode.NotFound, new RazorHandler("/notfound") },
        { HttpStatusCode.Unauthorized, new RazorHandler("/login") },
    }
});

Use Config.GlobalHtmlErrorHttpHandler for specifying a fallback HttpHandler for all error status codes, e.g.:

SetConfig(new EndpointHostConfig {
    GlobalHtmlErrorHttpHandler = new RazorHandler("/oops"),
});

Customized Error handling

ServiceStack and its new API provides a flexible way to intercept exceptions. If you need a single entry point for all service exceptions, you can add a handler to AppHostBase.ServiceExceptionHandler in Configure. To handle exceptions occurring outside of services you can set the global AppHostBase.ExceptionHandler handler, e.g.:

public override void Configure(Container container)
{
    //Handle Exceptions occurring in Services:
    this.ServiceExceptionHandler = (httpReq, request, exception) => {
        //log your exceptions here
        ...
        //call default exception handler or prepare your own custom response
        return DtoUtils.HandleException(this, request, exception);
    };

    //Handle Unhandled Exceptions occurring outside of Services
    //E.g. in Request binding or filters:
    this.ExceptionHandler = (req, res, operationName, ex) => {
         res.Write("Error: {0}: {1}".Fmt(ex.GetType().Name, ex.Message));
         res.EndServiceStackRequest(skipHeaders: true);
    };
}

Fine grain error handling using the New API's ServiceRunner

If you want to provide different error handlers for different actions and services you can just tell ServiceStack to run your services in your own custom IServiceRunner and implement the HandleExcepion event hook in your AppHost:

public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(
    ActionContext actionContext)
{           
    return new MyServiceRunner<TRequest>(this, actionContext); 
}

Where MyServiceRunner is just a custom class implementing the custom hooks you're interested in, e.g.:

public class MyServiceRunner<T> : ServiceRunner<T> 
{
    public MyServiceRunner(IAppHost appHost, ActionContext actionContext) 
        : base(appHost, actionContext) {}

    public override object HandleException(IRequestContext requestContext, 
        T request, Exception ex) {
      // Called whenever an exception is thrown in your Services Action
    }
}

Using Old API - inherit a custom base class

If you have old services you still can handle all exceptions in one single place. Create a base class for your services and override HandleException method like shown below.

public class MyServiceBase<TRequest> : RestService<TRequest>
{
   public override object HandleException(TRequest request, Exception exception)
   {
       //log your exceptions here
       ...
       //call default exception handler or prepare your own custom response with 
       return base.HandleException(request, exception);
       // or prepare new customer response with new HttpError(...)
   }
}

Community Resources



  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. 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. 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
    18. MVC Integration
  9. Plugins 3. Request logger 4. Swagger API
  10. Tests
    1. Testing
    2. HowTo write unit/integration tests
  11. Other Languages
    1. FSharp
    2. VB.NET
  12. Use Cases
    1. Single Page Apps
    2. Azure
    3. Logging
    4. Bundling and Minification
    5. NHibernate
  13. Performance
    1. Real world performance
  14. 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
  15. Future
    1. Roadmap
Clone this wiki locally