How to idiomatically handle HTTP error codes when using RestSharp?

后端 未结 3 950
迷失自我
迷失自我 2020-12-29 06:32

I\'m building an HTTP API client using RestSharp, and I\'ve noticed that when the server returns an HTTP error code (401 Unauthorized, 404 Not Found, 500 Internal Server Err

相关标签:
3条回答
  • 2020-12-29 06:44

    It should be enough to check for a success code, and throw or report the error if you get any other code apart from success. This usually means checking for HTTP Status 200 after every request. If you create a new resource, you should expect Status 201.

    With most APIs/frameworks, it is very very unusual to see any other status code except these if nothing has gone wrong.

    0 讨论(0)
  • 2020-12-29 06:50

    I encountered this same problem while trying to create a generic error handler for a RestSharp WebAPI client. Given these extension methods:

    public static class RestSharpExtensionMethods
    {
        public static bool IsSuccessful(this IRestResponse response)
        {
            return response.StatusCode.IsSuccessStatusCode()
                && response.ResponseStatus == ResponseStatus.Completed;
        }
    
        public static bool IsSuccessStatusCode(this HttpStatusCode responseCode)
        {
            int numericResponse = (int)responseCode;
            return numericResponse >= 200
                && numericResponse <= 399;
        }
    }
    

    I made a request that required the response to be deserialized:

    public async Task<ResponseModel<TResponse>> PerformRequestAsync<TResponse>(IRestRequest request)
    {
        var response = await _client.ExecuteTaskAsync<ResponseModel<TResponse>>(request);
        ResponseModel<TResponse> responseData;
    
        if (response.IsSuccessful())
        {
            responseData = response.Data;
        }
        else
        {
            string resultMessage = HandleErrorResponse<TResponse>(request, response);
    
            responseData = new ResponseModel<TResponse>         
            {
                Success = false,
                ResultMessage = resultMessage
            };
        }
    
        return responseData;
    }
    

    However, during testing, I found that when I had no error handling configured for that case, my web serivce returned an HTML-formatted 404 page when an unmapped URL was requested. This caused the response.ErrorException property to contain the following string:

    Reference to undeclared entity 'nbsp'. Line n, position m.

    As apparently RestSharp tried to parse the response as XML, even though the content-type was text/html. Maybe I'll file an issue with RestSharp for this.

    Of course in production you should never get a 404 when calling your own service, but I want this client to be thorough and reusable.

    So there's two solutions I can think of:

    • Inspect the status code and show the description
    • Make sure the service returns an error object that you can parse

    The former is done quite easily. In HandleErrorResponse() I build the result message (user presentable) and error string (loggable) based on the numeric value of the status code:

    public string HandleErrorResponse(IRestRequest request, IRestResponse response)
    {
        string statusString = string.Format("{0} {1} - {2}", (int)response.StatusCode, response.StatusCode, response.StatusDescription);
        string errorString = "Response status: " + statusString;
    
        string resultMessage = "";
        if (!response.StatusCode.IsScuccessStatusCode())
        {
            if (string.IsNullOrWhiteSpace(resultMessage))
            {
                resultMessage = "An error occurred while processing the request: "
                              + response.StatusDescription;
            }
        }
        if (response.ErrorException != null)
        {
            if (string.IsNullOrWhiteSpace(resultMessage))
            {
                resultMessage = "An exception occurred while processing the request: "
                              + response.ErrorException.Message;
            }
            errorString += ", Exception: " + response.ErrorException;
        }
    
        // (other error handling here)
    
        _logger.ErrorFormat("Error response: {0}", errorString);
    
        return resultMessage;
    }
    

    Now as my API responses always are wrapped in a ResponseModel<T> of my making, I can set up an exception filter and a NotFound route to return a parsable response model with the error or exception message in the ResultMessage property:

    public class HandleErrorAttribute : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            // (log context.Exception here)
    
            context.Response = context.Request.CreateResponse(HttpStatusCode.InternalServerError, new ResponseModel<object>
            {
                Success = false,
                ResultMessage = "An exception occurred while processing the request: " + context.Exception.Message
            });
        }
    }
    

    And:

    public class ErrorController : ApiController
    {
        public HttpResponseMessage Handle404()
        {
            const string notFoundString = "The requested resource could not be found";
    
            var responseMessage = Request.CreateResponse(HttpStatusCode.NotFound, new ResponseModel<object>
            {
                Success = false,
                ResultMessage = notFoundString
            });
    
            responseMessage.ReasonPhrase = notFoundString;
    
            return responseMessage;
        }
    }
    

    This way the response from my service can always be parsed by RestSharp, and I can use the generic logging method:

    public string HandleErrorResponse<TResponseModel>(IRestRequest request, IRestResponse<<ResponseModel<TResponseModel>> response)
    

    And log the actual response at // (other error handling here), if available:

    if (response.Data != null && !string.IsNullOrWhiteSpace(response.Data.ResultMessage))
    {
        resultMessage = response.Data.ResultMessage;
        errorString += string.Format(", Service response: \"{0}\"", response.Data.ResultMessage);
    }
    
    0 讨论(0)
  • 2020-12-29 06:54

    RestSharp has added boolean property IRestResponse.IsSuccessful which covers your use case. I couldn't find any documentation referring to this property, but here's the line that defines the property's method.

    Interesting to note is that RestSharp considers codes 200-299 to be successful, while CodeCaster considers codes 200-399 to be successful.

    0 讨论(0)
提交回复
热议问题