.Net Core - Docker Linux memory usage keeps increasing - c#

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

Related

Httpclient returning blank api keys and client id keys

Ok, the second pair of eyes time for some reason my property is always coming back null.
They keys described here are only for demo purchases and will not work other wise.
public class RoundTableAPIClient {
public string ApiKey { get; set; }
public string ClientSecret { get; set; }
}
This is a class that I store all my API calls in
private readonly HttpClient _httpClient;
public RoundTableAPIClient() {
_httpClient = new HttpClient();
if (ApiKey != null | ClientSecret != null) {
_httpClient.DefaultRequestHeaders.Add(Constants.ApiKey, ApiKey);
_httpClient.DefaultRequestHeaders.Add(Constants.ClientSecret, ClientSecret);
}
}
The values for ApiKey and Client secret are null in my stock controller I am passing them in
public class StockController : Controller {
private readonly IStringLocalizer<StockController> _localizer;
RoundTableAPIClient apiClient;
public StockController(IStringLocalizer<StockController> localizer) {
_localizer = localizer;
apiClient = new RoundTableAPIClient();
}
This is my get example where I am going to the api to get the data this function is contained withing my stock controller.
public async Task<object> Get(DataSourceLoadOptions loadOptions) {
List<Stock> _result = new List<Stock>();
apiClient.DeveiceType = device.Desktop;
apiClient.DeveiceType = device.Desktop;
apiClient.ApiKey = "B538F53B-37F7-4564-B7C5-56AFF399252B";
apiClient.ClientSecret = "8132ED0B-8F0B-4841-8BF4-CE8438AC0F3E";
_result = await apiClient.GetStockFromApi();
return DataSourceLoader.Load(_result, loadOptions);
}
public async Task<List<Stock>> GetStockFromApi() {
List<Stock> _result = new List<Stock>();
var uri = new Uri(string.Format(ApiUrl + Constants.GetALlStock, string.Empty));
var response = await _httpClient.GetAsync(uri);
if (response.IsSuccessStatusCode) {
var byteArray = await response.Content.ReadAsByteArrayAsync();
var content = Encoding.UTF8.GetString(byteArray, 0, byteArray.Length);
_result = JsonConvert.DeserializeObject<List<Stock>>(content);
}
return _result.ToList();
}
Its here when I inspect my http client that the default headers are still bank I dont understand why that is the case.
Edit 2
Should I be doing it more like this?
public async Task<List<Stock>> GetStockFromApi(string ApiKey,string ClientSecret) {
List<Stock> _result = new List<Stock>();
var uri = new Uri(string.Format(ApiUrl + Constants.GetALlStock, string.Empty));
var response = await _httpClient.GetAsync(uri);
if (ApiKey != null | ClientSecret != null) {
_httpClient.DefaultRequestHeaders.Add(Constants.ApiKey, ApiKey);
_httpClient.DefaultRequestHeaders.Add(Constants.ClientSecret, ClientSecret);
}
if (response.IsSuccessStatusCode)
{
var byteArray = await response.Content.ReadAsByteArrayAsync();
var content = Encoding.UTF8.GetString(byteArray, 0, byteArray.Length);
_result = JsonConvert.DeserializeObject<List<Stock>>(content);
}
return _result.ToList();
}
You add the headers in the constructor on the condition that either ApiKey or ClientSecret is not null, but they will always be null at that stage. You probably want to give those default values, or add them as parameters in the constructor.

Moq Unit Test case - ASP.NET MVC with WebAPI

I am trying to UnitTest my MVC Controller method, which internally makes call to an WebAPI(using HttpClient). I'm not able to figure out how can I fake the httpclient call, as it should not go for actual request. Below is my source code and unit test case. Test case fails, as the call goes for actual HttpRequest (An error occurred while sending the request. A connection with the server could not be established)
Base MVC Controller
public class BaseController : Controller
{
public virtual async Task<T> PostRequestAsync<T>(string endpoint, Object obj) where T : class
{
var address = "http://localhost:5001/api/Login";
StringContent json = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "");
using (var client = new HttpClient())
{
try
{
var response = await client.PostAsync(address, json); // Test case fails here
if (response.IsSuccessStatusCode)
{
string data = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(data);
}
return default(T);
}
catch (WebException)
{
throw;
}
}
}
}
Derived class Controller
public class AccountController : BaseController
{
public AccountController() : base()
{
}
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
if (ModelState.IsValid)
{
var result = await PostRequestAsync<ResultObject>(Constants.UserLoginAPI, model); // this is call for basecontroller method which actually has HttpClient call.
var output = JsonConvert.DeserializeObject<UserObject>(result.Object.ToString());
if (result.Succeeded && !string.IsNullOrEmpty(output.Email))
{
var userRoleInfo = await GetRequestAsync<List<UserRoleObject>>(string.Format(Constants.GetUserRoleInfoAPI, output.Email));
if (userRoleInfo != null)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, output.Email),
new Claim("Username", output.UserName)
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties { IsPersistent = model.RememberMe });
}
return View(new LoginViewModel());
}
}
return View(model);
}
}
TestMethod
[Fact]
public async Task LoginTest_Post_UserHasInValidCredentials()
{
// Arrange
var mockModel = new LoginViewModel { };
mockModel.Password = "TestPassword";
mockModel.Email = "test#test.com";
mockModel.RememberMe = false;
var commonResult = new CommonResult { Object = null, Succeeded = false, StatusCode = Common.Enums.ResponseStatusCodeEnum.Success };
var email = string.Empty;
var mockHttp = new MockHttpMessageHandler();
var mockBase = new Mock<BaseController>() { CallBase=true};
mockHttp.When("http://127.0.0.1:5001/*").Respond("application/json", "{'name' : 'Test McGee'}"); // Respond with JSON - using RichardSzalay.MockHttp;
//// Inject the handler or client into your application code
StringContent jsonInput = new StringContent(JsonConvert.SerializeObject(mockModel), Encoding.UTF8, "application/json");
var client = new HttpClient(mockHttp);
var response = await client.PostAsync("http://127.0.0.1:5001" + Constants.UserLoginAPI, jsonInput);
var json = await response.Content.ReadAsStringAsync();
mockBase.Setup(test => test.PostRequestAsync<CommonResult>(Constants.UserLoginAPI, mockModel)).Returns(Task.FromResult(CommonResult()));
var result = await accountController.Login(mockModel); // test case fails, as the call goes for actual HttpRequest (An error occurred while sending the request. A connection with the server could not be established)
//var viewResult = Assert.IsType<ViewResult>(result);
Assert.NotNull(commonResult);
Assert.False(commonResult.Succeeded);
Assert.Empty(email);
//Assert.NotNull(model.Email);
}
Tight coupling to HttpClient in the base controller makes it difficult to test derived classes in isolation. Review and refactor that code to follow DI.
No need to have a base controller and it is usually not advised.
Extract PostRequestAsync out into its own service abstraction and implementation.
public interface IWebService {
Task<T> PostRequestAsync<T>(string endpoint, Object obj) where T : class;
Task<T> GetRequestAsync<T>(string endpoint) where T : class;
}
public class WebService : IWebService {
static HttpClient client = new HttpClient();
public virtual async Task<T> PostRequestAsync<T>(string requestUri, Object obj) where T : class {
var content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, "");
try {
var response = await client.PostAsync(requestUri, content); // Test case fails here
if (response.IsSuccessStatusCode) {
string data = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(data);
}
return default(T);
} catch (WebException) {
throw;
}
}
public async Task<T> GetRequestAsync<T>(string requestUri) where T : class {
try {
var response = await client.GetAsync(requestUri);
if (response.IsSuccessStatusCode) {
string data = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(data);
}
return default(T);
} catch (WebException) {
throw;
}
}
}
Refactor derived controllers to depend on the service abstraction
public class AccountController : Controller {
private readonly IWebService webService;
public AccountController(IWebService webService) {
this.webService = webService;
}
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) {
if (ModelState.IsValid) {
var result = await webService.PostRequestAsync<ResultObject>(Constants.UserLoginAPI, model);
if (result.Succeeded) {
var output = JsonConvert.DeserializeObject<UserObject>(result.Object.ToString());
if (output != null && !string.IsNullOrEmpty(output.Email)) {
var userRoleInfo = await webService.GetRequestAsync<List<UserRoleObject>>(string.Format(Constants.GetUserRoleInfoAPI, output.Email));
if (userRoleInfo != null) {
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, output.Email),
new Claim("Username", output.UserName)
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties { IsPersistent = model.RememberMe });
}
return View(new LoginViewModel());
}
}
}
return View(model);
}
}
This should now allow you to mock the dependency when testing in isolation without adverse side effects.
[Fact]
public async Task LoginTest_Post_UserHasInValidCredentials() {
// Arrange
var mockModel = new LoginViewModel { };
mockModel.Password = "TestPassword";
mockModel.Email = "test#test.com";
mockModel.RememberMe = false;
var commonResult = new CommonResult {
Object = null,
Succeeded = false,
StatusCode = Common.Enums.ResponseStatusCodeEnum.Success
};
var mockWebService = new Mock<IWebService>();
var accountController = new AccountController(mockWebService.Object) {
//HttpContext would also be needed
};
mockWebService
.Setup(test => test.PostRequestAsync<CommonResult>(Constants.UserLoginAPI, mockModel))
.ReturnsAsync(commonResult);
//
//Act
var result = await accountController.Login(mockModel);
//Assert
//...Make assertions here
}
I would inject an IHttpClient interface, and in release register a HttpClient wrapper that implements that interface.

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.

checking Internet connection with HttpClient

I am having difficulties to understand on how the bellow code could handle occasional internet connection loss. Ideally I would like to pause the app, once the connection is lost, and resume when it is up again. Is there any guideline on how to do it?
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.UseDefaultCredentials = true;
HttpClient client = new HttpClient(clientHandler) { MaxResponseContentBufferSize = 1000000 };
HttpResponseMessage response = await client.GetAsync(Url, ct);
The following example is not a direct solution, but it is an example I built to show how to return "pre-canned" content to requests whilst offline and then return back online when connectivity is restored. If you can get what I'm doing here, building what you want should be fairly easy.
[Fact]
public async Task Getting_a_response_when_offline()
{
var offlineHandler = new OfflineHandler(new HttpClientHandler(), new Uri("http://oak:1001/status"));
offlineHandler.AddOfflineResponse(new Uri("http://oak:1001/ServerNotRunning"),
new HttpResponseMessage(HttpStatusCode.NonAuthoritativeInformation)
{
Content = new StringContent("Here's an old copy of the information while we are offline.")
});
var httpClient = new HttpClient(offlineHandler);
var retry = true;
while (retry)
{
var response = await httpClient.GetAsync(new Uri("http://oak:1001/ServerNotRunning"));
if (response.StatusCode == HttpStatusCode.OK) retry = false;
Thread.Sleep(10000);
}
}
public class OfflineHandler : DelegatingHandler
{
private readonly Uri _statusMonitorUri;
private readonly Dictionary<Uri, HttpResponseMessage> _offlineResponses = new Dictionary<Uri, HttpResponseMessage>();
private bool _isOffline = false;
private Timer _timer;
public OfflineHandler(HttpMessageHandler innerHandler, Uri statusMonitorUri)
{
_statusMonitorUri = statusMonitorUri;
InnerHandler = innerHandler;
}
public void AddOfflineResponse(Uri uri, HttpResponseMessage response)
{
_offlineResponses.Add(uri,response);
}
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (_isOffline == true) return OfflineResponse(request);
try
{
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.ServiceUnavailable || response.StatusCode == HttpStatusCode.BadGateway)
{
MonitorOfflineState();
return OfflineResponse(request);
}
return response;
}
catch (WebException ex)
{
MonitorOfflineState();
return OfflineResponse(request);
}
}
private void MonitorOfflineState()
{
_isOffline = true;
_timer = new Timer( async state =>
{
var request = new HttpRequestMessage() {RequestUri = _statusMonitorUri};
try
{
var response = await base.SendAsync(request, new CancellationToken());
if (response.StatusCode == HttpStatusCode.OK)
{
_isOffline = false;
_timer.Dispose();
}
}
catch
{
}
}, null, new TimeSpan(0,0,0),new TimeSpan(0,1,0));
}
private HttpResponseMessage OfflineResponse(HttpRequestMessage request)
{
if (_offlineResponses.ContainsKey(request.RequestUri))
{
return _offlineResponses[request.RequestUri];
}
return new HttpResponseMessage(HttpStatusCode.ServiceUnavailable);
}
}
}

Categories

Resources