Reputation: 21501
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)
typeof(MyException).Name == ex.ErrorCode
within the catch.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
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