Подтвердить что ты не робот

Как идиоматически обрабатывать коды ошибок HTTP при использовании RestSharp?

Я создаю клиент HTTP API с помощью RestSharp, и я заметил, что когда сервер возвращает код ошибки HTTP (401 Unauthorized, 404 Not Found, 500 Internal Server Error и т.д.), RestClient.Execute() doesn ' t выдает исключение - вместо этого я получаю действительный RestResponse с свойством null .Data. Я не хочу вручную проверять каждый возможный код ошибки HTTP в моем клиенте API - решает ли RestSharp лучший способ передать эти ошибки моему клиентскому приложению?

Немного подробная информация. RestSharp предоставляет свойство Response.ErrorException - если вызов RestClient.Execute<T>() вызывает какое-либо исключение, он будет открыт через свойство ErrorException вместо того, чтобы быть выброшенным. Их документация включает следующий пример:

// TwilioApi.cs
public class TwilioApi {
    const string BaseUrl = "https://api.twilio.com/2008-08-01";

    public T Execute<T>(RestRequest request) where T : new()
    {
    var client = new RestClient();
    client.BaseUrl = BaseUrl;
    client.Authenticator = new HttpBasicAuthenticator(_accountSid, _secretKey);
    request.AddParameter("AccountSid", _accountSid, ParameterType.UrlSegment); // used on every request
    var response = client.Execute<T>(request);

    if (response.ErrorException != null)
    {
        const string message = "Error retrieving response.  Check inner details for more info.";
        var twilioException = new ApplicationException(message, response.ErrorException);
        throw twilioException;
    }
    return response.Data;
    }

}

Я использовал этот шаблон в своем коде, но мой сервер API возвращает 401 Unauthorized, но свойство ErrorException по-прежнему равно null. Я вижу код RestResponse debug watch Unauthorized status и сообщение об ошибке в свойствах RestResponse.StatusCode и RestResponse.StatusDescription, но я смущен тем, почему неавторизованный ответ не приведет к заполнению поля ErrorException.

4b9b3361

Ответ 1

Я столкнулся с этой же проблемой при попытке создать общий обработчик ошибок для клиента RestSharp WebAPI. Учитывая эти методы расширения:

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;
    }
}

Я сделал запрос, который требует от десериализации ответа:

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;
}

Однако во время тестирования я обнаружил, что, когда у меня не было обработки ошибок, настроенных для этого случая, мой веб-сервер возвратил 404-страницу в формате HTML, когда был запрошен немаркированный URL. Это привело к тому, что свойство response.ErrorException содержало следующую строку:

Ссылка на необъявленный объект 'nbsp'. Строка n, позиция m.

По-видимому, RestSharp попытался разобрать ответ как XML, хотя тип содержимого был text/html. Возможно, я напишу о проблеме с RestSharp для этого.

Конечно, в производстве вы никогда не должны получать 404 при вызове своего собственного сервиса, но я хочу, чтобы этот клиент был тщательным и многоразовым.

Итак, есть два решения, о которых я могу думать:

  • Осмотрите код состояния и покажите описание
  • Убедитесь, что служба возвращает объект ошибки, который вы можете проанализировать

Первое делается довольно легко. В HandleErrorResponse() я создаю сообщение результата (представляемое пользователем) и строку ошибки (loggable) на основе числового значения кода состояния:

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;
}

Теперь, когда мои ответы API всегда завернуты в ResponseModel<T> моего создания, я могу настроить фильтр исключений и маршрут NotFound для возврата анализируемой модели ответа с сообщением об ошибке или исключении в свойстве ResultMessage:

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
        });
    }
}

и

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;
    }
}

Таким образом, ответ от моей службы всегда может быть проанализирован RestSharp, и я могу использовать общий метод ведения журнала:

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

И запишите фактический ответ на // (other error handling here), если он доступен:

if (response.Data != null && !string.IsNullOrWhiteSpace(response.Data.ResultMessage))
{
    resultMessage = response.Data.ResultMessage;
    errorString += string.Format(", Service response: \"{0}\"", response.Data.ResultMessage);
}

Ответ 2

Этого должно быть достаточно, чтобы проверить код успеха и бросить или сообщить об ошибке, если вы получите какой-либо другой код, кроме успеха. Обычно это означает проверку HTTP-статуса 200 после каждого запроса. Если вы создаете новый ресурс, вы должны ожидать Status 201.

В большинстве API/фреймворков очень необычно видеть любой другой код состояния, кроме этих, если ничего не случилось.