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().
Related
I am bit frustrated now what's wrong with my code, and I hope you guys can help me with it, so here are the things I have tried.
so I tried making the HttpClient static, and I tried using the IHttpClientFactory.CreateClient() and I even added this on my .csproj
<ServerGarbageCollection>false</ServerGarbageCollection>
Here is the sample code that I have been doing
public class TestController : BaseController
{
private static HttpClient _httpClient = new();
public TestController()
{
}
[HttpGet("bills")]
public async Task<IActionResult> GetBillsPresentment([FromQuery] GetBillPresentmentQuery query)
{
if (!query.AccountNumber.Contains("-"))
query.AccountNumber = FormatAccountNumber(query.AccountNumber);
var billDetails = await GetBillDetail(query.AccountNumber);
if (billDetails == null)
throw new ProviderProcessException(ProviderErrorCode.INVALID_ACCOUNT_NUMBER);
return Ok(new BillPresentmentVm
{
User = new CustomerDto
{
CustomerName = billDetails.Name
},
Billing = new BillingDto
{
AccountNumber = query.AccountNumber,
DueDate = DateTime.Parse(billDetails.LastReadDate).AddMonths(1),
Outstanding = !string.IsNullOrEmpty(billDetails.Arrears) ? decimal.Parse(billDetails.Arrears) : null
}
});
}
private async Task<ResponseModel> GetBillDetail(string accountNumber)
{
try
{
var payload = new { accno = accountNumber };
string json = JsonConvert.SerializeObject(payload);
var buffer = System.Text.Encoding.UTF8.GetBytes(json);
using var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await _httpClient.PostAsync("https://test.com", byteContent);
if (!response.IsSuccessStatusCode)
throw new ProviderProcessException(ProviderErrorCode.BILLING_CYCLE_UNAVAILABLE);
var result = await response.Content.ReadAsStringAsync();
if (result == "Accno not found!") return null;
var data = JsonConvert.DeserializeObject<ResponseModel>(result);
return data;
}
catch (Exception)
{
throw new ProviderProcessException(ProviderErrorCode.BILLING_CYCLE_UNAVAILABLE);
}
}
private static string FormatAccountNumber(string accountNumber)
{
return string.Format("{0:#######-########}", Convert.ToInt64(accountNumber));
}
}
And here's the docker memory usage
The memory usage keeps increasing after a request. Can someone explains me why it is not decreasing?
Thank you very much in advance
I solve this issue using IHttpClientFactory instead of HttpClient.
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
I use some thing like this and it works fine on large amount of requests per second and it use memory in normal way .
namespace BehsaLoyalty.ApiClient
{
public class ApiRepo : IApiRepo
{
private readonly IHttpClientFactory _HttpClientFactory;
public ApiRepo (IHttpClientFactory httpClientFactory)
{
_HttpClientFactory = httpClientFactory;
}
public async Task<ResponseModel> PostMyObject(Myobject model, CancellationToken cancellationToken)
{
HttpClient httpClient = _HttpClientFactory.CreateClient("ApiDestinationURI");
using HttpRequestMessage request = new(HttpMethod.Post, "/blah/blah");
request.Content = new StringContent(JsonSerializer.Serialize(model));
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
response.EnsureSuccessStatusCode();
string createdContent = await response.Content.ReadAsStringAsync();
ResponseModel ResponseReturn = JsonSerializer.Deserialize<ResponseModel>(createdContent);
return ResponseReturn;
}
}
}
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, :)
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.
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;
}
}
The app I'm working on is supposed to retrieve a json string with the http client after which it gets deserialised and used in the app.
Everything works, except for the await functionality. I'm doing something wrong and I can't seem to figure out what. How can I make sure that my DataService class waits untill I have my json and it has been deserialized?
The DataService class:
class DataService : IDataService
{
private IEnumerable<Concert> _concerts;
public DataService()
{
_concerts = new DataFromAPI()._concerts;
Debug.WriteLine("____Deserialization should be done before continuing____");
**other tasks that need the json**
}
}
My http client class:
class DataFromAPI
{
public IEnumerable<Concert> _concerts { get; set; }
public DataFromAPI()
{
Retrieve();
}
public async Task Retrieve()
{
try
{
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
var result = await client.GetAsync(new Uri("http://url-of-my-api"), HttpCompletionOption.ResponseContentRead);
string jsonstring = await result.Content.ReadAsStringAsync();
DownloadCompleted(jsonstring);
}
catch {}
}
void DownloadCompleted(string response)
{
try
{
_concerts = JsonConvert.DeserializeObject<IEnumerable<Concert>>(response.ToString());
}
catch {}
}
}
solution
After a lot of trial and error I realised that for this particular thingy it didn't have to be async, so I just recreated is on the main thread, with success:
The DataService class:
class DataService : IDataService
{
private IEnumerable<Concert> _concerts;
public DataService()
{
_concerts = new DataFromAPI()._concerts;
}
}
My http client class:
public static class DataFromAPI
{
public void Retrieve()
{
try
{
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
var result = client.GetAsync("http://url-of-my-api").Result;
if (result.IsSuccessStatusCode)
{
var responseContent = result.Content;
}
DownloadCompleted(result.Content.ReadAsStringAsync().Result);
}
catch {}
}
}
You are calling Retrieve() without await in the DataFromAPI constructor, That's why your method isn't awaited.
You should better call this methods outside the constructor, with the await keyword like this :
await Retrieve();
You have to refactor your code a little. Here's an example :
public class DataService : IDataService
{
private IEnumerable<Concert> _concerts;
public async Task LoadData()
{
_concerts = await DataFromAPI.Retrieve();
**other tasks that need the json**
}
}
public static class DataFromAPI
{
public static async Task<IEnumerable<Concert>> Retrieve()
{
try
{
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
var result = await client.GetAsync(new Uri("http://url-of-my-api"), HttpCompletionOption.ResponseContentRead);
string jsonstring = await result.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<Concert>>(response.ToString());
}
catch(Exception)
{
}
return Enumerable.Empty<Concert>();
}
}
Then, when you create your DataService instance, just after you have to call it's LoadData() method.
DataService ds = new DataService();
await ds.LoadData();
And of course, these two lines of code must also be called from an async method. (async / await all the way)
After a lot of trial and error I realised that for this particular thingy it didn't have to be async, so I just recreated is on the main thread, with success:
The DataService class:
class DataService : IDataService
{
private IEnumerable<Concert> _concerts;
public DataService()
{
_concerts = new DataFromAPI()._concerts;
}
}
My http client class:
public static class DataFromAPI
{
public void Retrieve()
{
try
{
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage();
var result = client.GetAsync("http://url-of-my-api").Result;
if (result.IsSuccessStatusCode)
{
var responseContent = result.Content;
}
DownloadCompleted(result.Content.ReadAsStringAsync().Result);
}
catch {}
}
}