Как использовать Polly для повторных попыток и временной обработки ошибок при произвольных условиях отказа

Я хочу использовать Polly не для проверки явных "сбоев", а скорее для других условий. В частности, я хочу сделать один (асинхронный) вызов, например httpClient.GetAsync(...), который для целей этого вопроса, как я знаю, будет успешным, то есть после выполнения:

var response = await _myHttpRequestPolicy.ExecuteAsync(() => httpClient.GetAsync(uri));

response.IsSuccessStatusCode будет true.

Предположим, что я делаю стандарт:

var content = await response.Content.ReadAsStringAsync();

и

content == { "Name":"Tom", "Age", 30", "ErrorCode":"12345" }

Я хочу, чтобы логика моей политики выполнялась на основе содержимого (или отсутствия или присутствия) ErrorCode части ответа. Так что это всего лишь один звонок, который я делаю.

Как я могу сделать это с Полли?


person Howiecamp    schedule 05.08.2018    source источник
comment
Вы можете создать свой собственный класс, наследуемый от политики, и реализовать там свою собственную логику отказа. И использовать этот класс политики, чтобы обернуть вызов веб-API под Polly.   -  person Chetan Ranpariya    schedule 06.08.2018
comment
github.com/ App-vNext/ Проверьте эту ссылку в документации, думаю, она описывает то, что вы пытаетесь сделать.   -  person Max Hampton    schedule 06.08.2018
comment
@Max Я сейчас читаю об этом - я собираюсь попробовать в каком-нибудь коде, но мне не ясно, смогу ли я сделать 1b (где часть ссылки) без 1 ...   -  person Howiecamp    schedule 06.08.2018
comment
@Howiecamp, можете ли вы показать код для конфигурации вашей политики?   -  person Max Hampton    schedule 06.08.2018
comment
@Howiecamp Polly имеет .HandleResult(...), и вы можете использовать это напрямую, сначала нужно использовать .Handle<SomeException>(...). Однако если условие результата зависит от получения ответа с помощью дополнительного вызова async, на него не распространяется ни одна политика Polly. См. этот вопрос и ответ - относится ли это также к вашему вопросу?   -  person mountain traveller    schedule 06.08.2018
comment
@mountaintraveller Отличное замечание. В моем случае это не зависит от дополнительного асинхронного вызова (кстати, что, если бы это был дополнительный вызов синхронизации? Будет ли это проблемой?), это всего лишь один вызов; пожалуйста, посмотрите мой вопрос, который я значительно переформулировал и сделал более явным.   -  person Howiecamp    schedule 06.08.2018
comment
@MaxHampton У меня его еще нет, я изо всех сил пытаюсь его собрать.   -  person Howiecamp    schedule 06.08.2018
comment
@Howiecamp Пока вы используете одну политику для каждого вызова await (как обсуждалось в этом вопросе и ответе), вместо того, чтобы пытаться ввести любое await в (внутри) предложения HandleResult(...), то, конечно, вы можете это сделать. Я думаю, что ответ здесь дает точный ответ на ваш вопрос , но дайте мне знать, если нет!   -  person mountain traveller    schedule 06.08.2018


Ответы (1)


Вы настраиваете политику для защиты метода HttpClient GetAsync, который возвращает Task<HttpResponseMessage>. Вы хотите настроить Policy<HttpResponseMessage> для работы с этим методом, используя асинхронные обработчики.

Policy<T>.HandleResult(Func<T, bool> filter) позволяет вам просмотреть HttpResponseMessage и определить, хотите ли вы обрабатывать этот результат.

Пара вариантов. Во-первых, вы можете выяснить десериализацию/чтение полезной нагрузки json HttpResponseMessage в методе HandleResult. Вы получаете только Func<HttpResponseMessage, bool> для работы. Это должно происходить синхронно, так как добавление async/await изменяет тип возвращаемого значения на Task.

Во-вторых, вы можете применить политику на более высоком уровне. Получите ответ как httpclient.GetAsync(uri), затем десериализуйте содержимое. Может быть, один Policy<HttpResponseMessage> обернет вызов httpclient, а один Policy<MyAbstractApiResponse> будет искать собственный код ошибки после десериализации?

Следует отметить, что ошибка API действительно должна обнаруживаться свойством IsSuccessStatusCode в HttpResponseMessage. Ваш REST API (это ваш? Это предположение) должен устанавливать коды состояния, соответствующие ошибке, а не только 200 и пользовательские свойства ответа.

Связанное дальнейшее чтение: Проверить содержимое строки ответ перед повторной попыткой с Полли

Обновлять:

class Consumer
{
  public void Test()
  {
    var apiResponse = Policy<IApiResponse>
      .HandleResult(resp => !string.IsNullOrWhiteSpace(resp.ErrorCode))
      // Setup Policy
      .ExecuteAsync(() => otherClassInstance.MakeApiCall());
  }
}

class OtherClass
{
  HttpClient httpClient = ...;
  public async Task<IApiResponse> MakeApiCall()
  {
    var response = Policy<HttpResponseMessage>
      .HandleResult(message => !message.IsSuccessStatusCode)
      // Setup Policy
      .ExecuteAsync(() => httpClient.GetAsync(url));

    var content = await response.ReadAsStringAsync();
    return new ApiResponse(content);
  }
}

Я не смотрел на настоящие имена методов или синтаксис при их сборке, поэтому он может не скомпилироваться. Надеюсь, вы уловили мысль, которую я пытаюсь донести: одна политика вызывается из другой.

person Max Hampton    schedule 06.08.2018
comment
Насколько я понимаю, если у меня есть Policy<HttpResponseMessage>, то выражение фильтра (предикат) в моем Policy<HttpResponseMessage>.HandleResult(Func<T, bool> filter) должно проверять какое-то свойство возвращаемого объекта HttpResponseMessage, верно? В моем случае у меня есть произвольный текст в теле ответа. - person Howiecamp; 06.08.2018
comment
Второй вопрос - вы сказали: «Вы настраиваете политику для защиты метода HttpClient GetAsync... В качестве аргумента скажем, что я исключаю эту часть из уравнения, другими словами, я либо обрабатываю, либо с Полли, либо нет, что угодно. В этом случае я бы защищался от вызова var content = await response.Content.ReadAsStringAsync();, а не правильно?? - person Howiecamp; 07.08.2018
comment
Успех... Я переориентировал свое мышление - вижу, где ошибся. Я выбрал подход, при котором можно иметь 2 политики: одну для самого httprequest и одну для проверки содержимого строки возвращаемых данных. Итак, для содержимого строки (я ожидаю наличия или отсутствия данной строки) я сделал: var policy = Policy.HandleResult<string>(r => r == "bad response").Wait..., затем: var body = await readAsStringAsyncPolicy.ExecuteAsync(() => response.Content.ReadAsStringAsync()); и бум, я в порядке. Вы считаете мой подход правильным? - person Howiecamp; 07.08.2018
comment
Я думаю, что ваш подход к двум политикам определенно правильный. Делаете ли вы это последовательно или в упаковке, это больше дизайнерское решение. Я отредактирую и добавлю псевдокод того, как я могу к этому подойти. - person Max Hampton; 07.08.2018
comment
Отлично хотелось бы увидеть. Между прочим, я ошибался в своем успехе - я все еще думал об этом неправильно. Как бы. Поскольку у меня есть обернутая операция ReadAsStringAsync, то в случае сбоя мне нужно повторно выполнить политику original, которая упаковывает http-вызов. Так что теперь у меня есть мысли, чтобы сделать снова, поскольку я не понимаю, как это сделать. - person Howiecamp; 07.08.2018
comment
На самом деле по моему предыдущему комментарию, может помочь? Я борюсь здесь. - person Howiecamp; 17.08.2018