How to idiomatically handle HTTP error codes when using RestSharp?

后端 未结 3 949
迷失自我
迷失自我 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: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> PerformRequestAsync(IRestRequest request)
    {
        var response = await _client.ExecuteTaskAsync>(request);
        ResponseModel responseData;
    
        if (response.IsSuccessful())
        {
            responseData = response.Data;
        }
        else
        {
            string resultMessage = HandleErrorResponse(request, response);
    
            responseData = new ResponseModel         
            {
                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 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
            {
                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
            {
                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(IRestRequest request, IRestResponse<> 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);
    }
    

    提交回复
    热议问题