Calling web service asynchronously in page constructor - c#

I need to load data on a XAML page in a windows 10 UWP application. For that I wrote code to call the web service in async task function, and I call this in page constructor. Could you please tell best way to do this? Following is my code.
public sealed partial class MyDownloads : Page
{
string result;
public MyDownloads()
{
this.InitializeComponent();
GetDownloads().Wait();
string jsonstring = result;
//code for binding follows
}
private async Task GetDownloads()
{
JsonObject jsonObject = new JsonObject
{
{"StudentID", JsonValue.CreateStringValue(user.Student_Id.ToString()) },
};
string ServiceURI = "http://m.xxx.com/xxxx.svc/GetDownloadedNotes";
HttpClient httpClient = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, ServiceURI);
request.Content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
HttpResponseMessage response = await httpClient.SendAsync(request);
string returnString = await response.Content.ReadAsStringAsync();
result = returnString;
}
}

Instead that you need use OnNavigatedTo
because, GetDownloads().Wait() bad practice. You block UI Thread until the end of execution
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var result = await GetDownloadsAsync();
string jsonstring = result;
}
private async Task<string> GetDownloadsAsync()
{
JsonObject jsonObject = new JsonObject
{
{"StudentID", JsonValue.CreateStringValue(user.Student_Id.ToString()) },
};
string ServiceURI = "http://m.xxx.com/xxxx.svc/GetDownloadedNotes";
HttpClient httpClient = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, ServiceURI);
request.Content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json");
HttpResponseMessage response = await httpClient.SendAsync(request);
string returnString = await response.Content.ReadAsStringAsync();
return returnString;
}
}

Related

Get a custom answer, an object for an HTTP Post request in a Web API

I call this WebApi endpoint:
public IActionResult MyEndPoint([FromBody] MyType myType)
{
// I do some stuff
var answer = new MyAnswer { Id = Guid.NewGuid() };
return Ok(answer);
}
The call to call the endpoint is this:
public async Task<string> httpPost(string url, string content)
{
var response = string.Empty;
using(var client = new HttpClient())
{
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(url),
Content = new StringContent(content, Encoding.UTF8, "application/json")
};
HttpResponseMessage result = await client.SendAsync(request);
if(result.IsSuccessStatusCode)
{
response = result.StatusCode.ToString(); //here
}
}
return response;
}
I'd like to have access MyAnswer object returned with the Ok() where the //here is. I put a breakpoint but nothing look like my object.
public async Task<MyAnswer> HttpPost(string url, string content)
{
var response = new MyAnswer();
using (var client = new HttpClient())
{
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(url),
Content = new StringContent(JsonSerializer.Serialize(content), Encoding.UTF8, "application/json")
};
HttpResponseMessage result = await client.SendAsync(request);
if (result.IsSuccessStatusCode)
{
var responseString = await result.Content.ReadAsStringAsync();
response = JsonSerializer.Deserialize<MyAnswer>(responseString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
}
}
return response;
}

HttpClient.SendAsync doesn't use DelegatingHandler when testing

We have a few classes in our C# project that make calls out to 3rd party APIs. We're using HttpClient objects for the calls. We've set up our classes where we do these calls to accept an HttpClient so that when testing, we can use a custom/fake DelegatingHandler with the client.
We've set up our classes like this:
public class CallingService : ApiService
{
private readonly ISomeOtherService _someOtherService;
public CallingService (ILogger logger,
IConfigurationManager configurationManager,
ISomeOtherService someOtherService) : base(logger, configurationManager)
{
_someOtherService = someOtherService;
}
public CallingService (ILogger logger,
HttpClient client,
IConfigurationManager configurationManager,
ISomeOtherService someOtherService) : base(logger, configurationManager, client)
{
_someOtherService = someOtherService;
}
private async Task<XmlNodeList> TransmitToApi(string xml_string)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12;
//..
string type = "application/xml";
var content = new StreamContent(new MemoryStream(Encoding.ASCII.GetBytes(xml_string)));
var targetUri = new Uri(ConfigurationManager.GetAppSetting("ApiUrl"));
var message = new HttpRequestMessage
{
RequestUri = targetUri ,
Method = HttpMethod.Post,
Content = content
};
message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
message.Content.Headers.Add("Content-Type", type);
message.Headers.Add("someHeader", someData);
HttpResponseMessage response = null;
try
{
// Define the cancellation token.
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
response = await Client.SendAsync(message, token);
}
catch (Exception ex)
{
throw ex;
}
//...
return someData;
}
The base ApiService class defines a generic HttpClient object if one is not provided.
We're currently using SendAsync so we can define the message headers. (We have more headers than are listed here.)
The test defines the DelegatingHandler like this:
public class FakeResponseHandler : DelegatingHandler
{
private readonly Dictionary<Uri, HttpResponseMessage> _fakeResponses = new Dictionary<Uri, HttpResponseMessage>();
public void AddFakeResponse(Uri uri, HttpResponseMessage responseMessage, string content = "", bool asXml = false)
{
if (!string.IsNullOrWhiteSpace(content))
{
if (asXml)
{
responseMessage.Content = new StringContent(content, Encoding.UTF8, "application/xml");
}
else
{
responseMessage.Content = new StringContent(content, Encoding.UTF8, "application/json");
}
}
_fakeResponses.Add(uri, responseMessage);
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (_fakeResponses.ContainsKey(request.RequestUri))
{
return _fakeResponses[request.RequestUri];
}
return new HttpResponseMessage(HttpStatusCode.NotFound) { RequestMessage = request };
}
}
And then:
[Fact]
public async Task ItWillDoStuffAndCallApi()
{
using (var mock = AutoMock.GetLoose())
{
mock.Mock<IConfigurationManager>()
.Setup(cm => cm.GetAppSetting("ApiUrl"))
.Returns("http://example.org/test/");
string testReturnData = GetFileContents("IntegrationTests.SampleData.SampleApiResponseXML.txt");
FakeResponseHandler fakeResponseHandler = new FakeResponseHandler();
fakeResponseHandler.AddFakeResponse(new Uri("http://example.org/test/"),
new HttpResponseMessage(HttpStatusCode.OK),
testReturnData,
true);
//HttpClient httpClient = new HttpClient(fakeResponseHandler);
HttpClient httpClient = HttpClientFactory.Create(fakeResponseHandler);
mock.Provide(httpClient);
var ourService = new CallingService();
ourService.TransmitToApi(someXmlString);
}
}
When we run the test, we receive the message:
Handler did not return a response message.
And we never seem to get into DelegatingHandler.SendAsync method.
We have other classes calling APIs using HttpClient.PostAsync or GetAsync, and these do call the DelegatingHandler.SendAsync method and work as expected.
We've tried:
HttpClient httpClient = new HttpClient(fakeResponseHandler);
and
HttpClient httpClient = HttpClientFactory.Create(fakeResponseHandler);
We've also tried Client.SendAsync with and without the cancellation token.
Why is this not working?
Should we re-write this to use PostAsync?
I'd need to see the implementation of HttpClientFactory.Create and what Client.SendAsync actually does internally but nevertheless I was able to use the sample code you provide and fill in the blanks where I could to get the following to work:
public class FakeResponseHandler : DelegatingHandler
{
private readonly Dictionary<Uri, HttpResponseMessage> _fakeResponses = new Dictionary<Uri, HttpResponseMessage>();
public void AddFakeResponse(Uri uri, HttpResponseMessage responseMessage, string content = "", bool asXml = false)
{
if (!string.IsNullOrWhiteSpace(content))
{
if (asXml)
{
responseMessage.Content = new StringContent(content, Encoding.UTF8, "application/xml");
}
else
{
responseMessage.Content = new StringContent(content, Encoding.UTF8, "application/json");
}
}
_fakeResponses.Add(uri, responseMessage);
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var emptyContent = string.Empty;
if (request.Content.Headers.ContentType.MediaType == "application/xml")
emptyContent = "<empty />";
return Task.FromResult(_fakeResponses.ContainsKey(request.RequestUri) ?
_fakeResponses[request.RequestUri] :
new HttpResponseMessage(HttpStatusCode.NotFound)
{
RequestMessage = request,
Content = new StringContent(emptyContent)
});
}
}
Just to make things clean use Task.FromResult to return a task in SendAsync and also provide an empty content to avoid null reference exceptions.
public class CallingService
{
private readonly HttpClient _httpClient;
private readonly IConfigurationManager _configurationManager;
public CallingService(HttpClient httpClient,
IConfigurationManager configurationManager)
{
_httpClient = httpClient;
_configurationManager = configurationManager;
}
public async Task<XmlNodeList> TransmitToApi(string xml_string)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12;
//..
string type = "application/xml";
var content = new StreamContent(new MemoryStream(Encoding.ASCII.GetBytes(xml_string)));
var targetUri = new Uri(_configurationManager.GetAppSetting("ApiUrl"));
var message = new HttpRequestMessage
{
RequestUri = targetUri,
Method = HttpMethod.Post,
Content = content
};
message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
message.Content.Headers.Add("Content-Type", type);
string somedata;
try
{
// Define the cancellation token.
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
var response = await _httpClient.SendAsync(message, token);
somedata = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
throw ex;
}
//...
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(somedata);
return xmlDoc.SelectNodes("*");
}
}
And then the test passes the instance of HttpClient to CallingService:
[TestMethod]
public async Task TestMethod1()
{
const string content = #"<root><test>1243</test></root>";
const string httpExample = "http://example.org/test/";
var configurationManager = new Mock<IConfigurationManager>();
configurationManager
.Setup(cm => cm.GetAppSetting("ApiUrl"))
.Returns(httpExample);
var fakeResponseHandler = new FakeResponseHandler();
fakeResponseHandler.AddFakeResponse(new Uri(httpExample),
new HttpResponseMessage(HttpStatusCode.OK), content, true);
using (var httpClient = new HttpClient(fakeResponseHandler))
{
var ourService = new CallingService(httpClient, configurationManager.Object);
var result = await ourService.TransmitToApi(content);
Assert.AreEqual(content, result.Item(0)?.OuterXml);
}
}
This all works so if I had to guess - the issue would be somewhere in your HttpClientFacotry.
Hope that helps!! Cheers, :)

Converting async Task Response to String

First of all, I would like to say, I'm quite new to C#.
I'm trying to create a POST request which sends some data to a PHP file somewhere on a different server.
Now, after the request is send I would like to see the response, as I'm sending back a JSON string from the server as a success message.
When I use the following code:
public MainPage()
{
this.InitializeComponent();
Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().SetDesiredBoundsMode(Windows.UI.ViewManagement.ApplicationViewBoundsMode.UseCoreWindow);
responseBlockTxt.Text = start();
}
public string start()
{
var response = sendRequest();
System.Diagnostics.Debug.WriteLine(response);
return "";
}
public async Task<string> sendRequest()
{
using (var client = new HttpClient())
{
var values = new Dictionary<string, string>
{
{ "vote", "true" },
{ "slug", "the-slug" }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("URL/api.php", content);
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
}
The output is:
System.Threading.Tasks.Task`1[System.String]
So, how would I see all the results from this?
Go Async all the way. Avoid blocking calls when calling async methods. async void is allowed in event handlers so update page to perform the call on load event
Read up on Async/Await - Best Practices in Asynchronous Programming
And then update your code accordingly
public MainPage() {
this.InitializeComponent();
Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().SetDesiredBoundsMode(Windows.UI.ViewManagement.ApplicationViewBoundsMode.UseCoreWindow);
this.Loaded += OnLoaded;
}
public async void OnLoaded(object sender, RoutedEventArgs e) {
responseBlockTxt.Text = await start();
}
public async Task<string> start() {
var response = await sendRequest();
System.Diagnostics.Debug.WriteLine(response);
return response;
}
private static HttpClient client = new HttpClient();
public async Task<string> sendRequest() {
var values = new Dictionary<string, string> {
{ "vote", "true" },
{ "slug", "the-slug" }
};
var content = new FormUrlEncodedContent(values);
using(var response = await client.PostAsync("URL/api.php", content)) {
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
}
I Guess
public string start()
{
var response = sendRequest();
Task<String> t = sendRequest();
System.Diagnostics.Debug.WriteLine(t.Result);
return "";
}
public async Task<string> sendRequest()
{
using (var client = new HttpClient())
{
var values = new Dictionary<string, string>
{
{ "vote", "true" },
{ "slug", "the-slug" }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("URL/api.php", content);
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
}
The problem is in the start method, the SendRequest method returns a Task<string> and that's what you get on your response variable. Since you are attempting to run an async method synchronously you have to do some extra stuff, try this:
public string start()
{
var response = sendRequest().ConfigureAwait(true)
.GetAwaiter()
.GetResult();
System.Diagnostics.Debug.WriteLine(response);
return "";
}
That get the actual result inside your awaitable Task<string>. If you want to find some more info on this take a look at this question
public string start()
{
var response = sendRequest().ConfigureAwait(true)
.GetAwaiter()
.GetResult();
System.Diagnostics.Debug.WriteLine(response);
return "";
}
I have Tried this. It is working perfectly.

HttpClient Access failing in Xamarin PCL Project

I have this block of code in my Xamarin PCL project. However, on line 4 where it is meant to call the GetStringAsync method, code exits the method at that point and returns no response. I am unable to get json data from the web service and have tried several workarounds without success. I am using Visual studio 2015.
//this calls the webservice
public class RestClient<T>
{
private const string WebServiceUrl = "http://localhost:14241/api/Employees/";
public async Task<List<T>> GetAsync()
{
var httpClient = new HttpClient();
var json = await httpClient.GetStringAsync(WebServiceUrl);
var taskModels = JsonConvert.DeserializeObject<List<T>>(json);
return taskModels;
}
public async Task<bool> PostAsync(T t)
{
var httpClient = new HttpClient();
var json = JsonConvert.SerializeObject(t);
HttpContent httpContent = new StringContent(json);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var result = await httpClient.PostAsync(WebServiceUrl, httpContent);
return result.IsSuccessStatusCode;
}
public async Task<bool> PutAsync(int id, T t)
{
var httpClient = new HttpClient();
var json = JsonConvert.SerializeObject(t);
HttpContent httpContent = new StringContent(json);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var result = await httpClient.PutAsync(WebServiceUrl + id, httpContent);
return result.IsSuccessStatusCode;
}
public async Task<bool> DeleteAsync(int id, T t)
{
var httpClient = new HttpClient();
var response = await httpClient.DeleteAsync(WebServiceUrl + id);
return response.IsSuccessStatusCode;
}
}
//this is the main view model that binds to the XAML page
public class MainViewModel : INotifyPropertyChanged
{
private List<Employee> _employeeList;
public List<Employee> EmployeesList
{
get { return _employeeList; }
set
{
_employeeList = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
InitializeDataAsync();
}
private async Task InitializeDataAsync()
{
var employeeServices = new EmployeesServices();
EmployeesList = await employeeServices.GetEmployeesAsync();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName=null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
//method that calls the GetAsync() method to retrieve the employee list
//from the web service
public class EmployeesServices
{
public async Task<List<Employee>> GetEmployeesAsync()
{
RestClient<Employee> restClient = new RestClient<Employee>();
var employeesList = await restClient.GetAsync();
return employeesList;
}
}
Make sure you are using await on the entire call chain all the way down to GetAsync().

Client calling WebAPI stuck in await

I have a httpclient that is calling a WebAPI service. The GET reaches the service and returns the content but the client just keeps waiting...
Client code:
static async Task RunAsyncGet(string baseUri, string uri)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(baseUri);
HttpResponseMessage response = await client.GetAsync(uri); // <-- stuck here
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
IEnumerable<UserAccountModel> users = await response.Content.ReadAsAsync<IEnumerable<UserAccountModel>>();
//...
}
}
}
WebAPI code:
public class UserAccountController : ApiController
{
private IRepository _repo;
public UserAccountController(IRepository repo)
{
_repo = repo;
}
public HttpResponseMessage Get()
{
var s = _repo.GetAllUserAccounts();
IContentNegotiator negotiator = Configuration.Services.GetContentNegotiator();
ContentNegotiationResult result = negotiator.Negotiate(typeof(AuthResponseModel), Request, Configuration.Formatters);
var bestMatchFormatter = result.Formatter;
var mediaType = result.MediaType.MediaType;
return new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new ObjectContent<IQueryable<UserAccount>>(s, bestMatchFormatter, mediaType)
};
}
}
Thoughts?
Further up in your client code (whatever ends up calling RunAsyncGet), some code is calling Task.Wait or Task<T>.Result. That will cause a deadlock if called from the UI thread, as I explain on my blog.
The proper solution is to change that Wait/Result to use await.
This is how I ended up calling the WebAPI:
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:23302");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("api/useraccount").Result;
if (response.IsSuccessStatusCode)
{
var t = response.Content.ReadAsAsync<IEnumerable<UserAccount>>().Result;
...
}
else
{
//Something has gone wrong, handle it here
}
}
It seems that your call to EnsureSuccessStatusCode is the likely culprit. That method actually returns a HttpResponseMessage that will have a HTTP status in the 200 range, or will throw an exception. So, you probably want something like:
static async Task RunAsyncGet(string baseUri, string uri)
{
var client = new HttpClient();
client.BaseAddress = new Uri(baseUri);
HttpResponseMessage response = await client.GetAsync(uri);
IEnumerable<UserAccountModel> users = await response.EnsureSuccessStatusCode().Content.ReadAsAsync<IEnumerable<UserAccountModel>>();
// ... the rest ...
}

Categories

Resources