How to post a webapi call using async and await - c#

I am trying write a webapi which tries to post a webapi call using async and await,my current issue is as soon as I call await client.PostAsync(url, content); it hangs.
1.How to debug why it is hanging?
2.Is there a way to do it without async and await?I want to do it sequentially
public static async Task<string> testWCF2(string xmlConfig)
{
string submitOut;
using (var client = new System.Net.Http.HttpClient())
{
var url = "http://server:8100/api/SoftwareProductBuild";
var content = new StringContent(xmlConfig, Encoding.UTF8, "application/xml");
var response = await client.PostAsync(url, content);
if (response.IsSuccessStatusCode)
{
var responseBody = await response.Content.ReadAsStringAsync();
submitOut = responseBody;
}
else
{
submitOut = string.Format("Bad Response {0} \n", response.StatusCode.ToString());
submitOut = submitOut + response;
}
}
return submitOut;
}
public async Task<string> QlasrSubmit(List<XMLSiInfo> xmlConfigs)
{
string submitOut = "QLASR: ";
foreach (XMLSiInfo xmlConfig in xmlConfigs)
{
submitOut = submitOut + "\n" + await testWCF2(xmlConfig.xml);
}
return submitOut;
}
public async Task<string> QlasrPostcommit(string si, string sp, string variant = null)
{
.....
string submitStatus = await QlasrSubmit(siInfo);
.....
return submitStatus;
}
Service:
public async Task<string> QlasrPostcommit(string si, string sp, string variant = null)
{
return await DPR.QlasrPostcommit(si, sp, variant);
}
Controller:
[Route("api/DevPool/QlasrPostcommit")]
[HttpPost]
public ResponseObject QlasrPostcommit(string si, string sp, string variant = null)
{
ResponseObject response = new ResponseObject();
try
{
response.status = 200;
response.data = DPS.QlasrPostcommit(si, sp, variant);
return response;
}
catch (Exception e)
{
response.status = 200;
response.data = null;
response.message = e.Message;
return response;
}
}

You should use async all the way, as I mentioned in your previous question:
[Route("api/DevPool/QlasrPostcommit")]
[HttpPost]
public async Task<ResponseObject> QlasrPostcommit(string si, string sp, string variant = null)
{
ResponseObject response = new ResponseObject();
try
{
response.status = 200;
response.data = await DPS.QlasrPostcommit(si, sp, variant);
return response;
}
catch (Exception e)
{
response.status = 200;
response.data = null;
response.message = e.Message;
return response;
}
}
In this particular case, you're running into a deadlock because you're blocking on asynchronous code.

I solved it and it works perfectly, without deallock and with waiting result!!
You have fix the Service:
public string QlasrPostcommit(string si, string sp, string variant = null)
{
Task<string > task = Task.Run<string >(async () => await
DPR.QlasrPostcommit(si, sp, variant));
task.Result;
}
Generic answer:
public TypeToReturn MyAsyncMethod(myParams...)
{
Task<TypeToReturn> task = Task.Run<TypeToReturn>(async () => await
MyAsyncMethod(myParams...));
task.Result;
}

Related

Xamarin - Logout when token expires

I have an Api hosted on Azure which I consume on my Xamarin Forms project.
I show the login page at the beginning and I check if the JWT token has expired but I also want to check that on each http method in case it expires while the user is using the app.
So I need to either show the user the login page and tell them to login again I have been searching how to do that I can't get it right.
Here is my AzureApiService class.
public class AzureApiService
{
HttpClient httpClient;
public AzureApiService()
{
#if DEBUG
var httpHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (o, cert, chain, errors) => true
};
#else
var httpHandler = new HttpClientHandler();
#endif
httpClient = new HttpClient(httpHandler);
httpClient.Timeout = TimeSpan.FromSeconds(15);
httpClient.MaxResponseContentBufferSize = 256000;
}
public async Task<string> LoginAsync(string url, AuthUser data)
{
var user = await HttpLoginPostAsync(url, data);
if (user != null)
{
//Save data on constants
CurrentPropertiesService.SaveUser(user);
return user.Token;
}
else
{
return string.Empty;
}
}
// Generic Get Method
public async Task<T> HttpGetAsync<T>(string url, string token)
{
T result = default(T);
try
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = httpClient.GetAsync(url).Result;
HttpContent content = response.Content;
if (response.IsSuccessStatusCode)
{
var jsonResponse = await content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
}
else
{
if (IsExpired(token))
{
await Logout();
}
throw new Exception(((int)response.StatusCode).ToString() + " - " + response.ReasonPhrase);
}
}
catch (Exception ex)
{
OnError(ex.ToString());
}
return result;
}
// Generic Post Method
public async Task<T> HttpPostAsync<T>(string url, string token, T data)
{
T result = default(T); // résultat de type générique
try
{
string json = JsonConvert.SerializeObject(data);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await httpClient.PostAsync(new Uri(url), content);
var jsonResponse = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var jsons = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
}
else
{
if (IsExpired(token))
{
await Logout();
}
throw new Exception(((int)response.StatusCode).ToString() + " - " + response.ReasonPhrase);
}
return result;
}
catch (Exception ex)
{
OnError(ex.ToString());
return result;
}
}
// Generic Put Method
public async Task<T> HttpPutAsync<T>(string url, string token, T data)
{
T result = default(T); // résultat de type générique
try
{
string json = JsonConvert.SerializeObject(data);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await httpClient.PutAsync(new Uri(url), content);
if (response.IsSuccessStatusCode)
{
var jsonResponse = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
}
else
{
if (IsExpired(token))
{
await Logout();
}
throw new Exception(((int)response.StatusCode).ToString() + " - " + response.ReasonPhrase);
}
return result;
}
catch (Exception ex)
{
OnError(ex.ToString());
return result;
}
}
// Generic Delete Method
public async Task<bool> HttpDeleteAsync(string url, string token)
{
try
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await httpClient.DeleteAsync(url);
if (response.IsSuccessStatusCode)
{
return true;
}
else
{
if (IsExpired(token))
{
await Logout();
}
return false;
throw new Exception(((int)response.StatusCode).ToString() + " - " + response.ReasonPhrase);
}
}
catch (Exception ex)
{
OnError(ex.ToString());
return false;
}
}
// Login Post Method
public async Task<T> HttpLoginPostAsync<T>(string url, T data)
{
T result = default(T); // résultat de type générique
try
{
string json = JsonConvert.SerializeObject(data);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(new Uri(url), content);
if (response.IsSuccessStatusCode)
{
var jsonResponse = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
}
else
{
throw new Exception(((int)response.StatusCode).ToString() + " - " + response.ReasonPhrase);
}
return result;
}
catch (Exception ex)
{
OnError(ex.ToString());
return result;
}
}
public bool IsExpired(string token)
{
if (token == null || "".Equals(token))
{
return true;
}
/***
* Make string valid for FromBase64String
* FromBase64String cannot accept '.' characters and only accepts stringth whose length is a multitude of 4
* If the string doesn't have the correct length trailing padding '=' characters should be added.
*/
int indexOfFirstPoint = token.IndexOf('.') + 1;
String toDecode = token.Substring(indexOfFirstPoint, token.LastIndexOf('.') - indexOfFirstPoint);
while (toDecode.Length % 4 != 0)
{
toDecode += '=';
}
//Decode the string
string decodedString = Encoding.ASCII.GetString(Convert.FromBase64String(toDecode));
//Get the "exp" part of the string
Regex regex = new Regex("(\"exp\":)([0-9]{1,})");
Match match = regex.Match(decodedString);
long timestamp = Convert.ToInt64(match.Groups[2].Value);
DateTime date = new DateTime(1970, 1, 1).AddSeconds(timestamp);
DateTime compareTo = DateTime.UtcNow;
int result = DateTime.Compare(date, compareTo);
return result < 0;
}
private async Task Logout()
{
CurrentPropertiesService.Logout();
CurrentPropertiesService.RemoveCart();
await Shell.Current.GoToAsync($"//main");
}
private void OnError(string error)
{
Console.WriteLine("[WEBSERVICE ERROR] " + error);
}
}
So you can see that in each http method I'm trying yo check if the token has expired already and then logout but it just gives an error.
On my Logout method I just want to delete all the properties and then navigate to the login page but it isn't working.
Please help I would like to know how to do this. Thanks.
EDIT
Trying to implement DelegatingHandler stops at SendAsync
Here is my HttpDelegatingHandler class
public class HttpDelegatingHandler : DelegatingHandler
{
public HttpDelegatingHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("Bearer", CurrentPropertiesService.GetToken());
// before request
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
// after request
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
await Logout();
}
return response;
}
private async Task Logout()
{
CurrentPropertiesService.Logout();
CurrentPropertiesService.RemoveCart();
await Shell.Current.GoToAsync($"//main");
}
}
Here my AzureApiService class
public class AzureApiService
{
HttpClient httpClient;
public AzureApiService()
{
var clientHandler = new HttpClientHandler();
#if DEBUG
clientHandler.ServerCertificateCustomValidationCallback =
(sender, cert, chain, sslPolicyErrors) =>
{
return true;
};
#endif
httpClient = new HttpClient(new HttpDelegatingHandler(clientHandler));
httpClient.Timeout = TimeSpan.FromSeconds(15);
httpClient.MaxResponseContentBufferSize = 256000;
}
public async Task<string> LoginAsync(string url, AuthUser data)
{
var user = await HttpLoginPostAsync(url, data);
if (user != null)
{
//Save data on constants
CurrentPropertiesService.SaveUser(user);
return user.Token;
}
else
{
return string.Empty;
}
}
// Generic Get Method
public async Task<T> HttpGetAsync<T>(string url, string token)
{
T result = default(T);
try
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await httpClient.GetAsync(url);
HttpContent content = response.Content;
var jsonResponse = await content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
throw new Exception(((int)response.StatusCode).ToString() + " - " + response.ReasonPhrase);
}
catch (Exception ex)
{
OnError(ex.ToString());
}
return result;
}
It works for PostAsync
// Login Post Method
public async Task<T> HttpLoginPostAsync<T>(string url, T data)
{
T result = default(T); // résultat de type générique
try
{
string json = JsonConvert.SerializeObject(data);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(new Uri(url), content);
var jsonResponse = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
return result;
}
catch (Exception ex)
{
OnError(ex.ToString());
return result;
}
}
But as I said it stops when trying to get data
You can handle 401 Unauthorized response in a custom Delegating handler. This way you can handle anything before and after request execution in a single place.
public class HttpDelegatingHandler : DelegatingHandler
{
public HttpDelegatingHandler(HttpMessageHandler innerHandler) : base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("Authorization", string.Format("Basic {0}", MyUserRepository.AuthToken));
// before request
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
// after request
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
await Shell.Current.GoToAsync($"//main");
}
return response;
}
}
public class AzureApiService
{
HttpClient httpClient;
public AzureApiService()
{
var clientHandler = new HttpClientHandler();
#if DEBUG
clientHandler.ServerCertificateCustomValidationCallback =
(sender, cert, chain, sslPolicyErrors) =>
{
return true;
};
#endif
httpClient = new HttpClient(new HttpDelegatingHandler(clientHandler));
httpClient.Timeout = TimeSpan.FromSeconds(15);
httpClient.MaxResponseContentBufferSize = 256000;
}
....
// Generic Get Method
public async Task<T> HttpGetAsync<T>(string url, string token)
{
T result = default(T);
try
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = await httpClient.GetAsync(url);
var jsonResponse = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<T>(jsonResponse);
}
catch (Exception ex)
{
OnError(ex.ToString());
}
return result;
}

Unit Test case for Generic HttpGet methods

How can I write test cases for Generic Http methods. My methods are below.
public async Task<T> HttpGetAsync<T>(string urlSuffix)
{
var url = Common.FormatUrl(urlSuffix, _instanceUrl, _apiVersion);
return await HttpGetAsync<T>(url);
}
public async Task<T> HttpGetAsync<T>(Uri uri)
{
try
{
var response = await HttpGetAsync(uri);
var jToken = JToken.Parse(response);
if (jToken.Type == JTokenType.Array)
{
var jArray = JArray.Parse(response);
return JsonConvert.DeserializeObject<T>(jArray.ToString());
}
// else
try
{
var jObject = JObject.Parse(response);
return JsonConvert.DeserializeObject<T>(jObject.ToString());
}
catch
{
return JsonConvert.DeserializeObject<T>(response);
}
}
catch (BaseHttpClientException e)
{
throw ParseForceException(e.Message);
}
}
protected async Task<string> HttpGetAsync(Uri uri)
{
var responseMessage = await _httpClient.GetAsync(uri).ConfigureAwait(false);
if (responseMessage.StatusCode == HttpStatusCode.NoContent)
{
return string.Empty;
}
var response = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
if (responseMessage.IsSuccessStatusCode)
{
return response;
}
throw new BaseHttpClientException(response, responseMessage.StatusCode);
}

GetStringAsync method call hangs the UI and never completes

I am trying to access my url using GetStringAsync method but the call hangs and never ends. When I check it in browser it works perfect but somehow in my code it never ends. My code:
public bool Login(string url,string userName, string password)
{
try
{
Task<LoginResponse> response = GetLoginData(url, userName, password);
if(response.Result.UserInfo.UserId > 0){
IsAuthenticated = true;
}
return IsAuthenticated;
}
catch (ArgumentException argex)
{
ErrorMessage = argex.Message;
IsAuthenticated = false;
return IsAuthenticated;
}
}
public async Task<LoginResponse> GetLoginData(string url, string userName, string password)
{
try{
var param = new AuthenticationParam();
param.UserName = userName;
param.Password = password;
var response = await LoginValidate(url, param);
response = response.Replace("\"", string.Empty);
response = response.Replace("\\", "\"");
LoginResponse list = JsonConvert.DeserializeObject<LoginResponse>(
response, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
return list;
}
catch(Exception e){
System.Diagnostics.Debug.WriteLine(e);
return new LoginResponse();
}
}
private static async Task<string> LoginValidate(string url, AuthenticationParam param){
try
{
string result = "";
using (var client = new HttpClient())
{
var json_data = string.Empty;
json_data = JsonConvert.SerializeObject(param);
var byteArray = Encoding.UTF8.GetBytes(json_data);
var base64 = Convert.ToBase64String(byteArray);
url = $"{url}{base64}";
result = await client.GetStringAsync(url);
return result;
}
}
catch (Exception ex)
{
return "Error: " + ex.Message;
}
}
It hangs at line result = await client.GetStringAsync(url);
if(response.Result.UserInfo.UserId > 0){ -> .Result is a blocking call and can lead to deadlocks. Use await all the way up the call chain.
public async Task<bool> Login(string url,string userName, string password)
{
try
{
var response = await GetLoginData(url, userName, password);
if(response.UserInfo.UserId > 0){
IsAuthenticated = true;
}
return IsAuthenticated;
}
catch (ArgumentException argex)
{
ErrorMessage = argex.Message;
IsAuthenticated = false;
return IsAuthenticated;
}
}
Up in the call stack use var success = await Login(...);
And please, read this excellent blogpost for the background about how this deadlock is created.

Mock returns null value when ReturnResult is a custom object but works as expected when it is a primitive type bool

I am using Moq in .net core(1.1) and having a bit of torrid time understanding this behavior as all the examples on interweb points to the fact the this should work with no issues.
I have already tried with:
Returns(Task.FromResult(...)
Returns(Task.FromResult(...)
ReturnsAsync(...)
Mocking a IHttpClient interface to wrap PostAsync, PutAsync and GetAsync. All of these return an ApiResponse object.
var mockClient = new Mock<IHttpClient>();
Does not work:
mockClient.Setup(x => x.PostAsync(url, JsonConvert.SerializeObject(body), null))
.Returns(Task.FromResult(new ApiResponse()));
PostSync definition:
public async Task<ApiResponse> PostAsync(string url, string body, string authToken = null)
Does work:
mockClient.Setup(x => x.PostAsync(url, JsonConvert.SerializeObject(body), null))
.Returns(Task.FromResult(bool));
PostSync definition:
public async Task<bool> PostAsync(string url, string body, string authToken = null)
Usage:
var api = new ApiService(mockClient.Object);
var response = api.LoginAsync(body.Username, body.Password);
UPDATE
[Fact]
public async void TestLogin()
{
var mockClient = new Mock<IHttpClient>();
mockClient.Setup(x => x.PostAsync(url, JsonConvert.SerializeObject(body), null)).Returns(Task.FromResult(new ApiResponse()));
var api = new ApiService(mockClient.Object);
var response = await api.LoginAsync(body.Username, body.Password);
Assert.IsTrue(response);
}
Return Type:
public class ApiResponse
{
public string Content { get; set; }
public HttpStatusCode StatusCode { get; set; }
public string Reason { get; set; }
}
LoginAsync:
public async Task<bool> LoginAsync(string user, string password)
{
var body = new { Username = user, Password = password };
try
{
var response = await _http.PostAsync(_login_url, JsonConvert.SerializeObject(body), null);
return response .State == 1;
}
catch (Exception ex)
{
Logger.Error(ex);
return false;
}
}
PostAsync:
public async Task<object> PostAsync(string url, string body, string authToken = null)
{
var client = new HttpClient();
var content = new StringContent(body, Encoding.UTF8, "application/json");
var response = await client.PostAsync(new Uri(url), content);
var resp = await response.Result.Content.ReadAsStringAsync();
return new ApiResponse
{
Content = resp,
StatusCode = response.Result.StatusCode,
Reason = response.Result.ReasonPhrase
};
}
Assuming a simple method under test like this based on minimal example provided above.
public class ApiService {
private IHttpClient _http;
private string _login_url;
public ApiService(IHttpClient httpClient) {
this._http = httpClient;
}
public async Task<bool> LoginAsync(string user, string password) {
var body = new { Username = user, Password = password };
try {
var response = await _http.PostAsync(_login_url, JsonConvert.SerializeObject(body), null);
return response.StatusCode == HttpStatusCode.OK;
} catch (Exception ex) {
//Logger.Error(ex);
return false;
}
}
}
The following test works when configured correctly
[Fact]
public async Task Login_Should_Return_True() { //<-- note the Task and not void
//Arrange
var mockClient = new Mock<IHttpClient>();
mockClient
.Setup(x => x.PostAsync(It.IsAny<string>(), It.IsAny<string>(), null))
.ReturnsAsync(new ApiResponse() { StatusCode = HttpStatusCode.OK });
var api = new ApiService(mockClient.Object);
//Act
var response = await api.LoginAsync("", "");
//Assert
Assert.IsTrue(response);
}
The above is just for demonstrative purposes only to show that it can work provided the test is configured properly and exercised based on the expected behavior.
Take some time and review the Moq quick start to get a better understanding of how to use the framework.

C# return from async task not working

I'm working on an async http call using HttpClient. The call is made inside an async task. The call is successful and I get a response from the Http call. But when I try to return the response from the task nothing happens, even though I have a breakpoint waiting after the return.
public void ExecuteTask(Foundation.Security.SecurityToken token, Order order)
{
ExecuteTaskAsync(token, order).Wait();
}
public async Task ExecuteTaskAsync(Foundation.Security.SecurityToken token, Order order)
{
if (order != null)
{
log.Info("Starting export of order " + order.ID.ToString());
bool success = await ExportOrder(order, token);
if (!success)
{
log.Error("Failed to export order with ID " + order.ID.ToString());
}
}
}
private async Task<bool> ExportOrder(Order order, Foundation.Security.SecurityToken token)
{
try
{
ResponseObject response = await webService.SendOrder(new SenderInformation(token), new ReceiverInformation(order, token));
if (response.Success && response.Status.Equals("201", StringComparison.OrdinalIgnoreCase))
{
log.Info(String.Format("Order ({0}) was successfully exported"), order.ExternalOrderID);
return true;
}
return false;
}
catch (Exception e)
{
log.Error(String.Format("Exception occured while exporting order ({0})", order.ID), e);
return false;
}
}
Below is the code which does the actual http call. I marked the last functional line with the comment "The code successfully reach this line. After this nothing happens"
public Task<ResponseObject> SendOrder(SenderInformation sender, ReceiverInformation receiver)
{
OrderRequest request = new OrderRequest(sender, receiver);
return ExecuteRequest<OrderRequest, ResponseObject>(request);
}
private async Task<ResponseType> ExecuteRequest<RequestType, ResponseType> (RequestType request)
where RequestType : RequestObject
where ResponseType : class, ResponseObject, new()
{
try
{
using (var client = new HttpClient())
{
string xml = SerializeRequest(request);
HttpContent content = new StringContent(xml);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/xml");
string requestUrl = "URL";
HttpResponseMessage response = await client.PostAsync(requestUrl, content).ConfigureAwait(false);
// Parse response
if (response.IsSuccessStatusCode)
{
Stream responseStream = await response.Content.ReadAsStreamAsync();
ResponseType responseObject = DeserializeResponse<ResponseType>(responseStream);
if (responseObject != null)
{
responseObject.Success = true;
return responseObject; //The code successfully reach this line. After this nothing happens
}
else
{
log.Error("Response could not be deserialized");
}
}
else
{
log.Error("Error during request, got status code " + response.StatusCode);
}
}
}
catch (Exception e)
{
log.Error("Something went wrong!", e);
}
return new ResponseType() { Success = false };
}
The problem is on this line:
ExecuteTaskAsync(token, order).Wait();
This causes a deadlock: the awaits in the called method can't resume because the UI thread is blocked.
When you use async code, you must use it all the way; never wait synchronously for an async task to complete.

Categories