Scott
Scott

Reputation: 21501

What is the correct/ best way to handle custom ServiceStack exceptions on the client side?

I am trying to simplify error handling in my client application which consumes a ServiceStack REST service using the JsonServiceClient.

My custom exceptions that I throw on the server are serialised in the ResponseStatus object, and I can see a WebServiceException is thrown.

But at the moment I am having to check for my exception types, by matching the WebServiceException ErrorCode to the type name of my exception class. (Which is exposed in the shared DTO class):

/** Current Method **/

try {

    client.Get(new RequestThatWillFail());

} catch(WebServiceException ex) {
    if(ex.ErrorCode == typeof(ValidationFailedException).Name)
        Console.WriteLine("Validation error");
    else if(ex.ErrorCode == typeof(UnauthorizedException).Name)
        Console.WriteLine("Not logged in");
    else if(ex.ErrorCode == typeof(ForbiddenException).Name)
        Console.WriteLine("You're not allowed to do that!");
    else
        throw; // Unexpected exception              
}

Ideally I was hoping that JsonServiceClient would contain some helper method or overridable conversion function that would allow me to translate the WebServiceException to my known exception type; So that I could use my try ... catch in a more traditional way:

/** Ideal Method **/

try {

    client.Get(new RequestThatWillFail());

} catch(ValidationFailedException ex) { // (WebServiceException is converted)
    Console.WriteLine("Validation error");
} catch(UnauthorizedException ex) {
    Console.WriteLine("Not logged in");
} catch(ForbiddenException ex) {
    Console.WriteLine("You're not allowed to do that!");
}

Update (for clarification)

I would envisage being able to provide JsonServiceClient with a map of:

{ Type typeof(Exception), string ErrorCode }

i.e. Something like

JsonServiceClient.MapExceptionToErrorCode = {
    { typeof(BadRequestException), "BadRequestException" },
    { typeof(ValidationFailedException), "ValidationFailedException" },
    { typeof(UnauthorizedException), "UnauthorizedException" },
    { typeof(AnotherException), "AnotherException" }
    // ...
}

Similar to how the server currently maps exceptions to Http status codes.

Then the ThrowWebServiceException<TResponse> and the HandleResponseError<TResponse> within the JsonServiceClient could look the ErrorCode up in the map and if it matches, return a new Exception of that type, passing the WebServiceException as a parameter, or alternatively Translate the properties.

But with the ultimate goal of throwing a more useable error. If there wasn't a match, go ahead and continue throwing the WebServiceException.

I'd override ThrowWebServiceException<TResponse> and the HandleResponseError<TResponse> but I don't think this is possible. And I don't wan't to build my own version to provide this functionality.

I hope I have explained this OK.

Upvotes: 3

Views: 2050

Answers (1)

stefan2410
stefan2410

Reputation: 2051

My approach for exception handling is to do in service side what is described in the Structured Error Handling and Overriding the default Exception handling
I use my ServiceRunner and I override the HandleException. If my API exception is thrown then I create a custom response.

       public override object HandleException(IRequestContext requestContext,T request, 
                                                   Exception ex)
    {
         APIException apiex = ex as APIException;    // custo application exception
        if (apiex != null)
        {
            ResponseStatus rs = new ResponseStatus("APIException", apiex.message);
            rs.Errors = new List<ResponseError>();
            rs.Errors.Add(new ResponseError());
            rs.Errors[0].ErrorCode = apiex.errorCode.ToString();               
            rs.Errors[0].FieldName = requestContext.PathInfo;

             rs.Errors[1].ErrorCode = apiex.detailCode.ToString(); 
            // create an ErrorResponse with the ResponseStatus as parameter
            var errorResponse = DtoUtils.CreateErrorResponse(request, ex, rs);

            Log.Error("your_message", ex);   // log only the the error
            return errorResponse;

        }
        else
            return base.HandleException(requestContext, request, ex);

    }

UPDATE : In client side, I create an wrapper for the service call and in WebServiceException, I check the ResponseStatus.Errors. In case of my error code, then I rethrow my exception.

   T ServiceCall<T>(string command, string rest_uri, object request)
    {
        try
        {   
                if (command == "POST")
                      return client.Post<T>(serverIP+rest_uri, request);

        }
        catch (WebServiceException err)
        {
           if (err.ErrorCode == "APIException" && err.ResponseStatus.Errors != null 
                          &&  err.ResponseStatus.Errors.Count > 0)
            {
                string  error_code = err.ResponseStatus.Errors[0].ErrorCode;
                string  path_info = err.ResponseStatus.Errors[0].FieldName;  
                string detail_error = err.ResponseStatus.Errors[1].ErrorCode; 

                 throw new  APIException(error_code,detail_error,path_info);           
            } 
        } finally {}
   }

Upvotes: 3

Related Questions