Calling an async api from a xamarin application - xamarin

I am working on a xamarin mobile application, upon making an async call to the exposed api, i do not get any error, however when i execute the .Result on the task the call never proceeds and it stuck forever.
Click here to see stringResourceResponse details
The same .Result call from a separate project (windows service) in the same solution works.
Any idea if .NET standard is causing limitation in executing async tasks, any advice would be helpful, thanks
Code added below:
//This is code from app.xaml.cs
var stringResourceApi = new StringResourceApiTask();
Task.Run(() =>
{
a = controller.CallStringResourceApi(stringResourceApi);
}).Wait();
public class MobileController
{
public string CallStringResourceApi(StringResourceApiTask stringResourceApiTask)
{
return stringResourceApiTask.Start(StringResourceUrl);
}
}
public override string Start(string URL)
{
var stringResourceResponse = SendRequest(url, "", HttpMethod.Get);
var result = stringResourceResponse.Result;
return result;
}
protected async Task < string > SendRequest(string url, string uri, HttpMethod method, int attempt = 1, int maxAttempts = 5)
{
return await SendRequest(
url, uri, Key, Secret, method, string.Empty, attempt, maxAttempts)
.ConfigureAwait(false);
}
protected async Task<string> SendRequest(string url, string uri, string key, string secret, HttpMethod method,
string requestBody = "", int attempt = 1, int maxAttempts = 5)
{
if (attempt > maxAttempts)
{
return null;
}
var client = InitialiseHttpClient(key, secret);
var request = new HttpRequestMessage
{
RequestUri = string.IsNullOrEmpty(url) ? new Uri(uri) : new Uri(url),
Method = method,
};
if (!string.IsNullOrWhiteSpace(requestBody))
{
request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json");
}
SetOutputText($"Attempting to communicate with {uri}...{Environment.NewLine}");
using (var response = await client.SendAsync(request).ConfigureAwait(false))
{
using (var content = response.Content)
{
try
{
response.EnsureSuccessStatusCode();
}
catch (HttpRequestException ex)
{
if (attempt > maxAttempts)
{
SetOutputText(errorMessage);
}
return await SendRequest(url, uri, key, secret, method, requestBody, attempt + 1).ConfigureAwait(false);
}
var responseBody = await content.ReadAsStringAsync().ConfigureAwait(false);
var isSuccessResponseButEmptyBody = response.IsSuccessStatusCode &&
(string.IsNullOrEmpty(responseBody) ||
string.IsNullOrWhiteSpace(responseBody));
if (!isSuccessResponseButEmptyBody)
{
return responseBody;
}
if (attempt > maxAttempts)
{
SetOutputText(errorMessage);
}
return await SendRequest(url, uri, key, secret, method, requestBody, attempt + 1).ConfigureAwait(false);
}
}
}

when i execute the .Result on the task the call never proceeds and it stuck forever.
Yes. This is a common deadlock situation. When code running on the UI thread blocks on asynchronous code, a deadlock usually occurs.
The same .Result call from a separate project (windows service) in the same solution works.
It works because the Win32 service code does not run on a UI thread.
The proper solution is to remove the blocking code; use await instead. This in turn will cause the calling methods to become async (e.g., StringResourceApiTask.Start), and they should also be awaited, etc. The usage of async and await should "grow" through your code; this is natural.
Alternatively, you can block in a thread pool thread, e.g., Task.Run(() => a = controller.CallStringResourceApi(stringResourceApi)).GetAwaiter().GetResult();. This is a bit of a hack (consuming an unnecessary thread), but it's a quick way to remove the deadlock. Note that this hack is not appropriate for ASP.NET apps; it's acceptable here since this is a UI app.

Related

Blazor Startup Error: System.Threading.SynchronizationLockException: Cannot wait on monitors on this runtime

I am trying to call an api during the blazor(client side) startup to load language translations into the ILocalizer.
At the point I try and get the .Result from the get request blazor throws the error in the title.
This can replicated by calling this method in the program.cs
private static void CalApi()
{
try
{
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(#"https://dummy.restapiexample.com/api/v1/employees");
string path = "ididcontent.json";
string response = httpClient.GetStringAsync(path)?.Result;
Console.WriteLine(response);
}
catch(Exception ex)
{
Console.WriteLine("Error getting api response: " + ex);
}
}
Avoid .Result, it can easily deadlock. You get this error because the mechanism is not (cannot be) supported on single-threaded webassembly. I would consider it a feature. If it could wait on a Monitor it would freeze.
private static async Task CalApi()
{
...
string response = await httpClient.GetStringAsync(path);
...
}
All events and lifecycle method overrides can be async Task in Blazor, so you should be able to fit this in.
In Program.cs
public static async Task Main(string[] args)
{
......
builder.Services.AddSingleton<SomeService>();
var host = builder.Build();
...
call your code here but use await
var httpClient = host.Services.GetRequiredService<HttpClient>();
string response = await httpClient.GetStringAsync(path);
...
var someService = host.Services.GetRequiredService<SomeService>();
someService.SomeProperty = response;
await host.RunAsync();
This is a example best:
var client= new ProductServiceGrpc.ProductServiceGrpcClient(Channel);
category = (await client.GetCategoryAsync(new GetProductRequest() {Id = id})).Category;

Async Http Request from component in Blazor

Im creating a list of components in Blazor, each one of these components need to request some data from a webpage. The list are created as follows on a .razor page:
#foreach(stringcomp in Complist){
<myComponent />
}
around 100 components are created. On all of these components the following URL request is preformed (using this code):
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await GetUrl("some url here");
}
}
public async Task<string> GetUrl(string url)
{
HttpClient client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("User-Agent", "get data service");
var response = await client.SendAsync(request).ConfigureAwait(false);
string res = null;
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var streamReader = new StreamReader(responseStream);
res = await streamReader.ReadToEndAsync().ConfigureAwait(false);
}
return res;
}
Doing this I'm running in to some problems where most of my calls to SendAsync never returns a value. I have come to understand that this is because of a lock-state but for the life of me can't figure out how to solve it. most similar answers suggest setting .ConfigureAwait(false) but this does not yeald a different result in my case.
So my question is: Hos can i request webbpages simultaneously in different components and be sure that they won't hang/lookup. As theres many requests that some times takes a long time (5-10 sec) to complete it's not an alternative to do them synchronously.
It might also be of importance to mention that me code and pages are separated, every .razor page are using #inherits to get its functions/logic
Try to use IHttpClientFactory as follows:
[Inject] public IHttpClientFactory clientFactory { get; set;}
using System.IO;
Your GetUrl method:
public async Task<string> GetUrl(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("Accept", "application/json");
request.Headers.Add("User-Agent", "get data service");
var client = clientFactory.CreateClient();
var response = await client.SendAsync(request);
string res = null;
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
var streamReader = new StreamReader(responseStream);
res = await streamReader.ReadToEndAsync().ConfigureAwait(false);
}
return res;
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
// More code here...
}
Hope this works...

How to make async call to api (http). in c#. inside a Task

I am developeing a chatbot using microsoftbotframmwok where I have some requirement to make a call from my task to an api(httpclient). but it is not working. when i test the api from an stand alone console application in side main method it works. but in my application it doesn't work.
I tried to call an api from an simple method without task but when it makes a cal its basically halts or stucked somewhere, i converted my function into task and while making an api call i used await keyword to call it asynchronously but it is returning error, while reading it not the result.
here is the my code which make an api call
private async Task<String> getProblem(IDialogContext context)
{
var response = "Thannks for contacting..";
//here some code logix..
SnowApiClient client = new SnowApiClient(Url, UserId, ApiPassword);
IncidentRequestPayload payload = new IncidentRequestPayload();
payload.caller_id = "tet111";
payload.assignment_group = "it";
payload.category = "complaint";
payload.u_feedback_type = "Praise";
payload.service_offering = "Application Management";
payload.priority = 2;
payload.short_description = "computer battery is dead";
payload.comments = String.Empty;
ApiResponse objResponse = await client.CreateIncident(payload);
//objResponse.payload.number;
return response;
}
//code for CreateIncident...in Api project librarary
public async Task<ApiResponse> CreateIncident(IncidentRequestPayload payload)
{
var incidentRequest = new ApiRequest { method = CreateIncidentMethod, payload = payload };
var createResult = await ExecuteRequest(incidentRequest);
return await ReadIncident(createResult.payload.incNumber);
}
public async Task<ApiResponse> ReadIncident(string number)
{
var incidentRequest = new ApiRequest { method = ReadIncidentMethod, payload = new RequestPayload { number = number } };
return await ExecuteRequest(incidentRequest);
}
private async Task<ApiResponse> ExecuteRequest(ApiRequest requestObject)
{
HttpResponseMessage response = await _client.PostAsJsonAsync("/SRintegratedAPI.rest", requestObject);
ApiResponse responseObject = null;
if (response.IsSuccessStatusCode)
{
responseObject = await response.Content.ReadAsAsync<ApiResponse>();
}
else
{
throw new System.Net.WebException(response.ReasonPhrase);
}
if (responseObject.result != "ok")
{
throw new System.Net.WebException(responseObject.message);
}
return responseObject;
}
I don't understand how and where do i used async/await here in basicalaly in my getProblem function.
please help

How do I exercise Formatters in tests using HttpServer?

In my Web API app, I'm using HttpServer to contain my controller in unit tests, and I'm using HttpClient to call it directly, eg:
[Fact]
public void TestMyController()
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("default", "api/{controller}/{action}/{id}", new { id = RouteParameter.Optional });
var server = new HttpServer(config);
var client = new HttpClient(server);
var response = client.GetAsync("http://localhost/api/test/values").Result;
}
I've noticed (by stepping through the debugger, and confirmed on other SO posts), that the JsonFormatter is not really running - it's initialized, but not exercised. Since this test isn't opening a socket, and the HttpClient is directly invoking the HttpServer through the HttpMessageHandler API, it does make sense that formatting/serialization isn't run because it's not needed.
In my case, I have some custom formatting/serialization/deserialization code that isn't being hit during these tests, but it's hit when I run in a real web server. I'd like to exercise that code in these tests; and it also just seems risky to exclude the serialization/deserialization code path when testing. Any advice on this?
Following is a quick example of what you could do to force formatters to go through serialization/deserialization. Here we are converting ObjectContent to StreamContent. In the below code, the call to CopyToAsync triggers a path where formatters are forced to serialize. In case of deserilization, in order to make sure we go through formatters we want the content to be of type other than ObjectContent as ReadAsAsync has internal logic which special cases ObjectContnent and we want to circumvent it.
HttpClient client = new HttpClient(new InMemoryHttpContentSerializationHandler(new HttpServer(config)));
public class InMemoryHttpContentSerializationHandler : DelegatingHandler
{
public InMemoryHttpContentSerializationHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Content = await ConvertToStreamContentAsync(request.Content);
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
response.Content = await ConvertToStreamContentAsync(response.Content);
return response;
}
private async Task<StreamContent> ConvertToStreamContentAsync(HttpContent originalContent)
{
if (originalContent == null)
{
return null;
}
StreamContent streamContent = originalContent as StreamContent;
if (streamContent != null)
{
return streamContent;
}
MemoryStream ms = new MemoryStream();
await originalContent.CopyToAsync(ms);
// Reset the stream position back to 0 as in the previous CopyToAsync() call,
// a formatter for example, could have made the position to be at the end
ms.Position = 0;
streamContent = new StreamContent(ms);
// copy headers from the original content
foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers)
{
streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
return streamContent;
}
}

Consume WCF Rest Service Json data across all platform using Shared Code

We were developing a sample MWC application with the logic of Business ,DataAccess,Data Layers.
In core Project we used the following code for consuming data from json parsing. This code works fine for Xamarin.Android and Xamarin.iOS, but for windows phone it shows error as 'System.Net.WebRequest does not contain a definition for GetResponse and no extension method for GetResponse...'
We tried to use Async methods for consuming WCF Rest service json data, but it returned as null before the completed method called.
Is it possible to wait and get data from completed method to return the json collection? if no please suggest how to achieve the same.
public String login<T>(T item) where T : BusinessLayer.Contracts.IBusinessEntity, new()
{
var request = HttpWebRequest.Create(url);
request.ContentType = "application/json";
request.Method = "get";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
string nss = content.ToString();
check = nss;
return nss;
}
}
return Check;
}
Edit: I have included the sample code of Async function.
Before Executing the DownloadStringCompleted event it returns null value. We need that DownloadStringCompleted output string for further process.
Note: We were following the logic of Tasky in Xamarin
async Task<string> AccessTheWebAsync(string url)
{
var webClient = new WebClient();
webClient.DownloadStringCompleted += (sender, e) =>
{
string data = (string)e.Result;
check = data;
};
webClient.DownloadStringAsync(new Uri(url));
return check;
}
public async Task<string> login<T>(T item) where T : BusinessLayer.Contracts.IBusinessEntity, new()
{
return check = await AccessTheWebAsync(item.url);
}
Your asynchronous code is not using Task correctly. It should use TaskCompletionSource to get the job done:
Task<string> AccessTheWebAsync(string url)
{
var source = new TaskCompletionSource<string>();
var webClient = new WebClient();
webClient.DownloadStringCompleted += (sender, e) =>
{
source.TrySetResult((string)e.Result);
};
webClient.DownloadStringAsync(new Uri(url));
return source.Task;
}
Before, your function was returning before the event fired. Using the task source wraps it in a task properly and fixes this problem.
You will also need to hook up the error event and call TrySetException to finish the implementation.

Resources