using System.Collections.Generic;
using System.Threading.Tasks;
private static readonly string _apiUrl = "https://api.dms.com.tw";
private static readonly string _apiVersion = "1";
private static readonly string _clusteringType = "example";
static async Task Main(string[] args)
var pathParams = new List<KeyValuePair<string, string>>
new KeyValuePair<string, string>("topicId", "123"),
new KeyValuePair<string, string>("subtopicIds", "456")
var queryParams = new List<KeyValuePair<string, string>>
new KeyValuePair<string, string>("param1", "value1"),
new KeyValuePair<string, string>("param2", "value2")
var content = await ExecuteWebApiAsync<object>(pathParams, queryParams, CancellationToken.None);
Console.WriteLine($"Received content: {content}");
private static async Task<T> ExecuteWebApiAsync<T>(
IEnumerable<KeyValuePair<string, string>> pathParams, IEnumerable<KeyValuePair<string, string>> queryParams,
CancellationToken cancellationToken)
var asyncPolicy = BuildRetryAsyncPolicy(3, 10);
var client = RestClientFactory<IRestResponse>.Create(asyncPolicy);
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36";
client.Encoding = Encoding.UTF8;
client.FailOnDeserializationError = true;
client.BaseUrl = new Uri($"{_apiUrl}/subtopic/v{_apiVersion}/");
var request = new RestRequest("topic/{topicId}/subtopics/{subtopicIds}/", Method.GET, DataFormat.Json);
foreach (var (key, value) in pathParams)
request.AddUrlSegment(key, value);
foreach (var (key, value) in queryParams)
request.AddParameter(key, value);
request.AddHeader("Accept", "application/json");
request.AddHeader("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36");
var response = await client.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception($"Network or server error with status code: {response.StatusCode}");
content = System.Text.Json.JsonSerializer.Deserialize<T>(response.Content);
private static IAsyncPolicy<IRestResponse> BuildRetryAsyncPolicy(int retryNumber, int timeoutSeconds)
.OrResult<IRestResponse>(
r => r.Request.Method == Method.GET
&& new List<int> { 400, 404, 500, 502, 503, 504 }.Contains((int)r.StatusCode)
.WaitAndRetryAsync(retryNumber, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (outcome, timespan, retryCount, context) =>
Console.WriteLine($"Retry {retryCount} after {timespan}. Error: {outcome.Exception?.Message ?? outcome.Result.StatusCode.ToString()}");
var timeoutPolicy = Policy.TimeoutAsync<IRestResponse>(timeoutSeconds);
return Policy.WrapAsync(retryPolicy, timeoutPolicy);
private static List<int> HttpStatusCodesWorthRetryingGet = new List<int> { 500, 502, 503, 504 };
private static List<int> HttpStatusCodesWorthRetryingExceptGet = new List<int> { 500, 502, 503 };
private static AsyncPolicyWrap<IRestResponse> BuildRetryAsyncPolicy2(int retryNumber, int timeoutSeconds)
var retryPolicy= Policy.Handle<Exception>().Or<AggregateException>()
.OrResult<IRestResponse>(
r => r.Request.Method == Method.GET
&& HttpStatusCodesWorthRetryingGet.Contains((int)r.StatusCode))
r.Request.Method != Method.GET
&& HttpStatusCodesWorthRetryingExceptGet.Contains((int) r.StatusCode)
.WaitAndRetryAsync(retryNumber, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
onRetry: (outcome, calculatedWaitDuration, retryCount, context) =>
"Policy logging onRetry {Retry} times after {Time}. Error: {Response}",
retryCount, calculatedWaitDuration,
outcome.Exception?.Message ?? outcome.Result.StatusCode.ToString());
var timeoutPolicy = Policy.TimeoutAsync<IRestResponse>(timeoutSeconds);
return Policy.WrapAsync(retryPolicy, timeoutPolicy);
private static string BuildWarningMessage(IRestResponse response)
$"The request failed. HttpStatusCode={response.StatusCode}. Uri={response.ResponseUri}; RequestResponse={response.Content}";