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

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.

Related

How to mock response in FHIR Http Client calls to CreateAsync

When implementing calls to CreateAsync in the Hl7.Fhir.Rest.FhirClient library I'm struggling with how to mock a valid response. I know how to mock the dotnet-httpclient using a Mock HttpMessageHandler object and noticed there is a message handler argument that can be specified when creating the FhirClient. What I have tried to do is specify a message handler to the creation step that is a mock message handler object.
This simplified unit test attempts to mock the HttpMessageHandler and cause it to return a valid body and result code from the FhirClient's CreateAsync method call.
[Fact]
public async Task SubscribeAndReturnSubscriptionIdAsync()
{
var mockHttpMessageHandler = MockFhirHttpClientMessageHandler.MockSubscribeMessageResponse(new StringContent("{'id':'abc123','status':'active'}"), HttpStatusCode.Created);
var subscriptionResource = new Subscription()
{
Criteria = "https://server.fire.ly/CareTeam",
Status = Subscription.SubscriptionStatus.Active,
Reason = "test",
Channel = new Subscription.ChannelComponent()
{
Type = Subscription.SubscriptionChannelType.RestHook,
Endpoint = "http://localhost:9999/AscomFhirApi/UpdateCareTeam",
Payload = "application/fhir+json"
},
};
var serverUri = new Uri("http://server.fire.ly");
var clientSettings = new FhirClientSettings()
{
PreferredFormat = ResourceFormat.Json
};
var fhirHttpClient = new Hl7.Fhir.Rest.FhirClient(serverUri, clientSettings, mockHttpMessageHandler.Object);
var subscription = await fhirHttpClient.CreateAsync<Subscription>(subscriptionResource);
Assert.NotEmpty(subscription.Id);
}
The MockSubscribeMessageResponse method shown below creates the HttpMessageHandler that is passed to the FhirClient in the above test.
public static Mock<HttpMessageHandler> MockSubscribeMessageResponse(
HttpContent content,
HttpStatusCode code = HttpStatusCode.OK)
{
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = code,
Content = content
});
return mockHttpMessageHandler;
}
The error I'm getting is a Null Reference Exception in what looks like the HttpResponseMessage or response body.
System.NullReferenceException
Object reference not set to an instance of an object.
at Hl7.Fhir.Rest.HttpToEntryExtensions.ToEntryResponse(HttpResponseMessage response, Byte[] body)
at Hl7.Fhir.Rest.HttpClientRequester.ExecuteAsync(EntryRequest interaction)
at Hl7.Fhir.Rest.BaseFhirClient.executeAsync[TResource](Bundle tx, IEnumerable`1 expect)
at Tests.Unit.Core.Services.FirelyHttpClientShould.SubscribeAndReturnSubscriptionIdAsync() in C:\src\AscomIASharedAssignFHIRApi5\Tests.Unit.Core\Services\FirelyHttpClientShould.cs:line 60
You have probably figured this out long time ago, but the source of error is most probably missing RequestMessage, implementation of ToEntryResponse depends on response.RequestMessage.RequestUri being set. So I guess that what you need to do is:
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = code,
RequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost"),
Content = content
});
return mockHttpMessageHandler;
A long time later again ...
In version 3.8.3 of the Firely SDK the FhirClient now has support for taking the HttpClient in its constructor, so this may make unit testing apis much easier. It wasn't clear from your post what you were testing here...
I wrote a blog post on using it for this type of testing
Roughly something like ...
[TestMethod]
public async Task SubscribeAndReturnSubscriptionIdAsync()
{
using (var fhirServerFactory = new UnitTestFhirServerApplication())
using (var httpclient = fhirServerFactory.CreateClient())
{
var server = new FhirClient("http://server.fire.ly", httpclient);
var subscriptionResource = new Subscription()
{
Criteria = "https://server.fire.ly/CareTeam",
Status = Subscription.SubscriptionStatus.Active,
Reason = "test",
Channel = new Subscription.ChannelComponent()
{
Type = Subscription.SubscriptionChannelType.RestHook,
Endpoint = "http://localhost:9999/AscomFhirApi/UpdateCareTeam",
Payload = "application/fhir+json"
},
};
var subscription = await server.CreateAsync(subscriptionResource);
// ...
}
}

Calling an async api from a xamarin application

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.

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 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;
}
}

Return response from HTTPWebRequest somehow?

I'm trying to validate a user in my WP7 app by validating username/pass on a server.
In my "validate user" method I create a HTTPWebRequest with the stuff I need validated.
Then as far as I can see, the only option to execute is to use request.BeginGetResponse, with an async callback.
But I want to return the response from the request in the same method that I created the request in, how can I accomplish this?
I'm not sure why you would like to make it return in the calling method. I would just use the standard WebClient behavior like below:
public void ValidateUser()
{
WebClient webClient = new WebClient();
Uri uri = new Uri(url);
webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
webClient.DownloadStringAsync(new Uri(url));
}
void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
//Validate result from response by using e.Result
}
BeginGetResponse returns an IAsyncResult which has a wait handle that is signalled when the request completes. All you have to do is wait on this handle: [ doesn't work on WP7 ]
UPDATE: use a ManualResetEvent
var mre = new ManualResetEvent( false );
var iar = myHttpWebRequest.BeginGetResponse( state => mre.Set(), null );
mre.WaitOne();
var response = myHttpWebRequest.EndGetResponse( iar );
The only real way to achieve what you need would be to create an event in your worker class, and fire that event when the async call completes, so (pseudocode)...
void StartDownload(object[] parameters)
{
var req = HttpWebRequest.Create("http://google.com");
req.BeginGetRequestStream(Completed_handler, req);
}
void Completed_handler(object sender, DownloadStringCompletedEventArgs e)
{
var request = (HttpWebRequest)result.AsyncState;
var response = request.EndGetResponse(result);
using (var stream = response.GetResponseStream())
using (var reader = new StreamReader(stream))
{
var contents = reader.ReadToEnd();
NotifyCallerOfContent(contents)
}
}
public event EventHandler<MyCustomEventArgsClass> DownloadHasFinished;
public NotifyCallerOfDownload(string content)
{
if(null != DownloadHasFinished)
{
DownloadHasFinished(this, new MyCustomEventArgsClass(content));
}
}
and then subscribe to the DownloadHasFinished event in your calling class.
As for calling out specifically by a HttpWebRequest, take a look at this question, where you will find some working samples.

Resources