Polly patterns in C# for a workflow? - c#

I have a workflow like this:
call an API method
if any exception is thrown but timeout, the program logs that exception and throws it.
if timeout is thrown, the program has to call another API method.
after calling another API method, if everything goes true, the program returns a result.
otherwise the program throws an exception and log it.
both API methods have the same type of result.
I want to implement this with polly policies.
This is my sample code:
var retryPolicy = Policy
.Handle<HttpRequestException>(ex => ex.StatusCode == HttpStatusCode.RequestTimeout)
.RetryAsync(1, async (exception, retryCount) =>
await CallAnotherAPI());
var fallbackPolicy = Policy<HttpResponseMessage>
.Handle<Exception>()
.FallbackAsync((r, c, ct) => throw r.Exception,
async (r, c) =>
{
Log(r.Message);
});
var result = await fallbackPolicy
.WrapAsync(retryPolicy)
.ExecuteAsync(async () =>
{
await CallAPI();
});
but it doesn't work and all the time fallbackPolicy is executed. How can I write the code that if retryPolicy goes true, fallbackPolicy won't be executed?

If I understand your workflow correctly then you don't need the retry policy at all. The Fallback policy is enough for this.
So, let suppose that the CallApi and CallAnotherApi are implemented like this:
private static HttpClient client = new HttpClient();
public static async Task<HttpResponseMessage> CallAPI()
{
return await client.GetAsync("http://httpstat.us//408");
}
public static async Task<HttpResponseMessage> CallAnotherAPI()
{
var response = await client.GetAsync("http://httpstat.us//500");
response.EnsureSuccessStatusCode();
return response;
}
I've used httpstatus.us to simulate certain http status codes
CallApi will always fail with Request Timeout
CallAnotherApi will always throw HttpRequestException because of the Ensure method call
Now let's see the policy definition and usage:
public static async Task Main(string[] args)
{
var fallbackPolicy = Policy<HttpResponseMessage>
.HandleResult(msg => msg.StatusCode == HttpStatusCode.RequestTimeout)
.FallbackAsync(async (_) => await CallAnotherAPI());
HttpResponseMessage result;
try
{
result = await fallbackPolicy
.ExecuteAsync(async () =>
{
return await CallAPI();
});
}
catch (Exception ex) {
Console.WriteLine(ex.Message); //TODO: replace with logging
throw;
}
Console.WriteLine(result.StatusCode);
}
The fallback policy should be triggered only if the response's status code was 408
ExecuteAsync will throw exception if either the CallApi or CallAnotherApi throws
Let's see the different scenarios one-by-one
CallApi succeeds
public static async Task<HttpResponseMessage> CallAPI()
{
return await client.GetAsync("http://httpstat.us//200");
}
Output
OK
CallApi fails
public static async Task<HttpResponseMessage> CallAPI()
{
var response = await client.GetAsync("http://httpstat.us//500");
response.EnsureSuccessStatusCode();
return response;
}
Output
Response status code does not indicate success: 500 (Internal Server Error).
Then the application crashes because of throw;
CallApi timeouts and CallAnotherApi succeeds
public static async Task<HttpResponseMessage> CallAPI()
{
return await client.GetAsync("http://httpstat.us//408");
}
public static async Task<HttpResponseMessage> CallAnotherAPI()
{
var response = await client.GetAsync("http://httpstat.us//200");
response.EnsureSuccessStatusCode();
return response;
}
Output
OK
CallApi timeouts and CallAnotherApi fails
public static async Task<HttpResponseMessage> CallAPI()
{
return await client.GetAsync("http://httpstat.us//408");
}
public static async Task<HttpResponseMessage> CallAnotherAPI()
{
var response = await client.GetAsync("http://httpstat.us//500");
response.EnsureSuccessStatusCode();
return response;
}
Output
Response status code does not indicate success: 500 (Internal Server Error).
Then the application crashes because of throw;

The polly fallback is enough in this case. like this....
var fallbackPolicy = Policy<HttpResponseMessage>
.Handle<Exception>()
.FallbackAsync((r, c, ct) => throw r.Exception,
async (r, c) =>
{
Log(r.Message);
});

Related

HttpClient in async/sync implementation returns WaitingForActivation

I'm having a problem with async to sync implementation of HttpClient.
Id = 8, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"
I know what I'm doing is probably a bad practice and it would be ideal to make all the path async, but that's a request that the company is making me, so I have to do like this.
Project is build in NET Standard 1.1, to be used as a NuGet package and to be compatible with Framework and Core as well.
Here's my main client construction...
private static HttpClient _client;
private static Uri _baseAddress;
private static readonly JsonSerializerSettings _settings = new JsonSerializerSettings
{ DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore };
public Client() { }
private Client(string baseUrl, Config config)
{
_baseAddress = new Uri(baseUrl);
_client = new HttpClient { Timeout = TimeSpan.FromSeconds(config.Timeout) };
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_client.DefaultRequestHeaders.Add("X-API-KEY", config.Token);
}
private Client _paymentClient;
private Client _mainClient;
public Client Create(bool payment, Config config = null)
{
if (!payment)
{
_mainClient = _mainClient ?? new Client("https://api.address.com/", config);
return _mainClient;
}
_paymentClient = _paymentClient ?? new Client("https://payment.address.com/", config);
return _paymentClient;
}
public void Dispose() => _client.Dispose();
private static async Task<T> Send<T>(HttpMethod method, string url, object data = null)
{
var uri = new UriBuilder(_baseAddress);
uri.Path += url;
var request = new HttpRequestMessage(method, uri.Uri);
if (data != null)
request.Content = new StringContent(JsonConvert.SerializeObject(data, _settings), Encoding.UTF8, "application/json");
var response = await _client.SendAsync(request).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
T result = default;
if (response.IsSuccessStatusCode)
{
if (response.Content.Headers.ContentType.MediaType == "application/json")
{
var responseObj = JsonConvert.DeserializeObject<Response<T>>(content, _settings);
if (responseObj.HasError)
throw new Safe2PayException(responseObj.ErrorCode, responseObj.Error);
responseObj.ResponseDetail = result;
}
}
else throw new Exception((int) response.StatusCode + "-" + response.StatusCode);
request.Dispose();
response.Dispose();
return result;
}
And the Send<T> method is supposed to be a general treatment to process the request and response, wrapped on generic calls like this:
internal Task<T> Get<T>(string url) => Send<T>(HttpMethod.Get, url);
//OR even async...
internal async Task<T> Get<T>(string url) => await Send<T>(HttpMethod.Get, url);
Which are called like this, to send and receive data..
private Client Client { get; }
public CheckoutRequest(Config config) => Client = new Client().Create(true, config);
public object Credit(Transaction transaction)
{
var response = Client.Post<Transaction>("v2/Payment", transaction);
return response;
}
My problem is that the client is always getting me a WaitingfForActivation or even Running or WaitingToRun, doesn't matter if I change it to...
Task.Run(() => Send<T>(HttpMethod.Get, url));
//or
Task.Run(() => Send<T>(HttpMethod.Get, url).Result);
//or
Task.Run(async () => await Send<T>(HttpMethod.Get, url));
//or
Task.Run(async () => await Send<T>(HttpMethod.Get, url).ConfigureAwait(false));
I've been trying to find what I'm doing wrong, tried to change all the awaits, but I'm not being sucessful with this, so any help will be very much appreciated.
I suspect your problem is here:
public object Credit(Transaction transaction)
{
var response = Client.Post<Transaction>("v2/Payment", transaction);
return response;
}
You didn't show your code for Post<T>(), but I assume it's also an async Task<T> method, which means response is a Task<T> and your code is basically doing this:
Start a task.
Return a description of the incomplete task.
When I assume this is really what you want:
Start the task.
Wait for the task to complete.
Return the result of the task.
Ideally, this should be an async method, and you can await the task:
public async Task<object> Credit(Transaction transaction)
{
var response = await Client.Post<Transaction>("v2/Payment", transaction);
return response;
}
If you absolutely must wait for the task synchronously (there are very few reasons to need to) then you can use .GetAwaiter().GetResult():
public object Credit(Transaction transaction)
{
var response = Client.Post<Transaction>("v2/Payment", transaction).GetAwaiter().GetResult();
return response;
}
The main benefit of .GetAwaiter().GetResult() instead of .Result is that, in the case of exceptions, it will throw the actual exception instead of an AggregateException.
Also, you can make your Create() method static:
public static Client Create(bool payment, Config config = null)
Then you don't need to initialize the class just to call it:
public CheckoutRequest(Config config) => Client = Client.Create(true, config);
Update: If you want async and non-async versions of the same method, you can follow the same standard that Microsoft uses and name the async method with the Async suffix. The non-async version can just call the async version. For example:
public async Task<object> CreditAsync(Transaction transaction)
{
var response = await Client.Post<Transaction>("v2/Payment", transaction);
return response;
}
public object Credit(Transaction transaction)
{
return CreditAsync(transaction).GetAwaiter().GetResult();
}

How do I test a public function that has private method dependency?

I'm trying to test a public method (Method A) that consumes an HttpClient to access an external API. This public method calls a private method (Method B) of the same class to get an Access Token that is required by the HttpClient of Method A to send the request. The problem I am having is that I am creating a mock of the HttpClientFactory interface in order to test the response of the Method A, but in order for Method B get the token it needs its own instance of HttpClient. Therefore, the mock instance created in the Test method will be used by the Method B as well, and it will fail trying to get the Access Token. The following code makes the scenario more clear.
Method to be tested (Method A):
public async Task<HttpResponseMessage> SendAsync(string requestUri, string siteName, int accountId)
{
try
{
var accessToken = await GetTokenAsync(siteName, accountId);
if (accessToken == null)
throw new ArgumentNullException("Error Sending request - Could not find an access token");
var request = new HttpRequestMessage(HttpMethod.Get, $"{accessToken.Api}{requestUri}");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Accesstoken);
var httpClient = _httpClientFactory.CreateClient();
return await httpClient.SendAsync(request);
}
catch (Exception e)
{
throw new Exception("Error Sending request.", e);
}
}
Test Method:
[Fact]
public async Task ShouldReturnHttpResponseMessage_OnSendAsync()
{
//_jaClientMock.Setup(x => x.GetTokenAsync(It.IsAny<string>(), It.IsAny<int>())).Verifiable();
_appSettingsMock.Setup(x => x.Value)
.Returns(GetValidFakeAppSettings());
HttpResponseMessage expectedResponse = GetListOfContacts(HttpStatusCode.OK, false);
_httpClientFactoryMock.Setup(x => x.CreateClient())
.Returns(GetMockedHttpClient(expectedResponse));
var response = await _jaClient.SendAsync("someurl", "siteName", 1000);
response.IsSuccessStatusCode.ShouldBeTrue();
}
The private Method (Method B):
private async Task<AccessToken> GetTokenAsync(string siteName, int accountId)
{
try
{
if (_cache.TryGetValue(GetCacheKeyForToken(siteName, accountId), out AccessToken value))
return value;
....
var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
accessToken = await response.Content.ReadAsAsync<AccessToken>();
}
.....
return accessToken;
}
catch (Exception e)
{
throw new Exception("Error Getting an Access Token.", e);
}
}
Any idea How I can test Method A?
There ain't no such thing as a free lunch - if one wants to unit-test some code with external dependencies, then each and every of those external dependencies has to be mocked.
Or one can go one step up the test pyramid to integration tests (though it is not our case, probably).
So, you could:
Either mock the Token response in the _httpClientFactory the same way you mock it for the SendAsync ( ..._httpClientFactoryMock.Setup(x => x.CreateClient()).Returns(GetMockedHttpClient(expectedResponse));...)
Or reorganize code in such a manner that tokens are not retrieved directly from API - create some single-method ITokenProvider interface that will be a bit easier to mock.
public interface ITokenProvider
{
public async Task<AccessToken> GetTokenAsync(string siteName, int accountId);
}
...
public async Task<HttpResponseMessage> SendAsync(string requestUri, string siteName, int accountId)
{
try
{
var accessToken = await _tokenProvider.GetTokenAsync(siteName, accountId);
...
[Fact]
public async Task ShouldReturnHttpResponseMessage_OnSendAsync()
{
var tokenProviderMock = new Mock<ITokenProvider>()
.Setup(o => o.GetTokenAsync("siteName", 1000))
.Returns(Constants.AllowedToken);
_jaClient = new JaClient(tokenProviderMock.Object);...

ASP.NET Web API async controller method and deadlock

Please help me to understand why this code cause a deadlock?
I have an asp.net web api application and I tried to make some controller method asynchronous.
[HttpPost]
[Authentication]
public async Task<SomeDTO> PostSomething([FromBody] SomeDTO someDTO)
{
return await _service.DoSomething(someDTO);
}
this is how looks the called service method:
public async Task<SomeDTO> DoSomething(SomeDTO someDTO)
{
...
var someTask = Task.Run(() =>
{
var entity = new SomeEntity(someDTO);
return _repository.Create(entity);
});
...
var result = await someTask;
...
}
And there is some globalhandler, that prints a response to a console.
public class AppGlobalHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var resp = base.SendAsync(request, cancellationToken);
Debug.WriteLine($"Response:{request.RequestUri}{Environment.NewLine}{resp?.ConfigureAwait(false).GetAwaiter().GetResult()?.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult()}");
return resp;
}
}
Looks like ConfigureAwait(false).GetAwaiter().GetResult()
blocks the caller thread, but I supposed that ConfigureAwait(false) should avoid this, isn't it?
ConfigureAwait(false) would not help you here because it must be all the way down in the call stack (see more here) not at place where you wait synchronously, i.e. it depends rather on the implementation of base.SendAsync. If it acquired a lock on current thread it's too late to do something about it. It is also not recommended in ASP.net pipeline to continue responding on other thread after all (see discussion here and post here).
Finally it is always a highly risky idea to wait synchronously in async context.
If you need to read content, why not doing it like that:
public class AppGlobalHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var resp = await base.SendAsync(request, cancellationToken);
var content = resp?.Content != null
? (await resp.Content.ReadAsStringAsync())
: string.Empty;
Debug.WriteLine($"Response:{request.RequestUri}{Environment.NewLine}{content}");
return resp;
}
}
I think you overlook async keyword in Task.Run() method.
public async Task<SomeDTO> DoSomething(SomeDTO someDTO)
{
var someTask = Task.Run( async () => //simply add this for async run
{
var entity = new SomeEntity(someDTO);
return _repository.Create(entity);
});
var result = await someTask;
}

System.Format Exception throw by SendAsync method of Delegate handler

While using a custom message handler, I keep encountering the following error on the API server side:
API Controller:-
[RoutePrefix("errors")]
public class ErrorController : ApiController
{
[HttpGet]
[Route("{id}")]
public IHttpActionResult GetClientEmailId(int id)
{
return this.Ok();
}
}
Custom Message Handler:-
public class ApiMessageHandler: DelegatingHandler
{
protected override async Task<HttpResponseMessage>
SendAsync(HttpRequestMessage request, CancellationToken
cancellationToken)
{
var logger = LogManager.GetLogger(this.GetType().FullName);
if (logger.IsDebugEnabled)
{
var requestMessage = await
request.Content.ReadAsByteArrayAsync();
var resTask = base.SendAsync(request,
cancellationToken).ContinueWith(
t =>
{
if (t.Exception != null)
{
throw t.Exception;
}
return t.Result;
},
cancellationToken);
byte[] responseMessage;
if (resTask.Result.IsSuccessStatusCode)
{
responseMessage = resTask.Result.Content != null
? await resTask.Result.Content.ReadAsByteArrayAsync()
: Encoding.UTF8.GetBytes(resTask.Result.ReasonPhrase);
}
else
{
responseMessage = Encoding.UTF8.GetBytes(resTask.Result.ReasonPhrase);
}
await this.Log(request, requestMessage, resTask.Result,
responseMessage, logger);
return resTask.Result;
}
var response = await base.SendAsync(request, cancellationToken);
return response;
}
when I called action method http://localhos:4200/errors/adf(pass string parameter instead of integer) then I got System.FormatException in Base.SendAsync method.
The exception was thrown: 'System.FormatException' in mscorlib.dll
Additional information: Input string was not in a correct format.
But this exception is not handled by GlobalException Handler.
It looks like a base.SendAsync method has swallowed this exception.
How we can handle this exception and rethrow so GlobalException handler can handle this exception with proper message.
Thank you in advance.
The problem is that you are swallowing the exception yourself. You are not awaiting the task:
if (resTask.Result.IsSuccessStatusCode)
This is a bad practice in 99% of cases, since you are synchronously blocking the thread instead of asynchronously waiting for the result. Your code should be:
var result = await base.SendAsync(request, cancellationToken));
byte[] responseMessage;
if (result.IsSuccessStatusCode)
Since your method does not have a try-catch surrounding it, any exceptions will be thrown to the global handler as usual.

How do I ensure DelegatingHandlers throw exceptions on timeouts?

I'm using HttpClient's PostAnsyc method to synchronously call a REST API from service code invoked by my MVC application, but I'm losing exceptions in a DelegatingHandler.
The usage is synchronous. I am aware of the async path and it does not fit my use case.
Here are some variant's I've tried that didn't throw exceptions on timeout:
//controller action
[HttpPost]
public JsonResult Foo(int id)
{
try
{
var result = _businessService.Foo(id);
return Json(result, JsonRequestBehavior.DenyGet);
}
catch(Exception exception)
{
return Json(exception, JsonRequestBehavior.DenyGet);
}
}
//infrastructure code deep in my application
public HttpResponseMessage Post(Uri uri, StringContent content)
{
return _httpClient.PostAsync(uri, content).Result;
}
//DelegatingHandler code
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var taskCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
base.SendAsync(request, cancellationToken)
.ContinueWith( t =>
{
if (t.IsFaulted)
{
if(t.Exception != null)
{
taskCompletionSource.TrySetException(t.Exception);
}
}
else if (t.IsCanceled)
{
taskCompletionSource.TrySetCanceled();
}
else
{
try
{
LogResponse(t.Result);
taskCompletionSource.SetResult(t.Result);
}
catch (Exception ex)
{
taskCompletionSource.TrySetException(ex);
}
}
}, cancellationToken);
return taskCompletionSource.Task;
}
How do I ensure that my DelegatingHandlers do not swallow exceptions during a timeout?
public async Task<HttpResponseMessage> PostAsync(Uri uri, StringContent content)
{
var cancellation = new CancellationTokenSource();
var task = _httpClient.PostAsync(uri, content, cancellation.Token);
var timeout = Task.Delay(5000);
await Task.WhenAny(task, timeout);
if(timeout.IsCompleted)
{
cancellation.Cancel();
throw new TimeoutException();
}
else
return await task;
}
This example would provide a timeout of 5 seconds before try to cancel the POST operation and throw a timeout exception.
Unfortunately the HttpClient has no synchronous methods, so whatever you do another thread pool thread is taking care of the request and you have to wait for it.
An alternative is to use the WebRequest, but it is less fancy and you have to serialize your payload yourself (which is not big deal with the NewtonSoft Json library)
It's pretty trivial to create a timeout using a CancellationTokenSource
public async Task<HttpResponseMessage> PostAsync(Uri uri, StringContent content)
{
var cancellation = new CancellationTokenSource(5000); // Cancel after 5 seconds.
return await _httpClient.PostAsync(uri, content, cancellation.Token);
}

Categories

Resources