I have this function:
public static async Task<T> Relay<T>(string uriController, T jsonObject)
{
var json = string.Empty;
try
{
LogHelpers.LogVerbose( "Enter " + uriController );
using ( var client = new HttpClient() )
{
HttpResponseMessage response;
client.BaseAddress = Uri;
client.Timeout = new TimeSpan( 0, 0, 0, 20, 0 );
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue( "application/json" ) );
if ( jsonObject != null )
{
var jsonInput = JsonConvert.SerializeObject( jsonObject );
var contentPost = new StringContent( jsonInput, Encoding.UTF8, "application/json" );
response = await client.PostAsync( uriController, contentPost ).ConfigureAwait( false );
}
else
{
response = await client.PostAsync( uriController, null ).ConfigureAwait( false );
}
response.EnsureSuccessStatusCode();
if (response.StatusCode != HttpStatusCode.OK)
{
return default(T);
}
json = await response.Content.ReadAsStringAsync().ConfigureAwait( false );
LogHelpers.LogVerbose( "Exit " + uriController );
SendNotification?.Invoke();
}
}
catch (Exception e)
{
throw e;
}
return JsonConvert.DeserializeObject<T>(json);
}
and this is called by:
private async void GetPiSystemInfo()
{
SystemPiInfo = await RaspberryPi.Relay<SystemPiInfo>("Api/SystemPiInfo/IsConnected", null);
}
which in turn is run from this:
private async Task<bool> RunTheMethod(Action myMethodName)
{
await Task.Run(
() =>
{
while (true)
{
try
{
myMethodName();
return true;
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
});
return false;
}
Which finally is called from this:
Action[] Actions = new Action[10];
Actions[0] = GetPiSystemInfo;
await RunTheMethod(Actions[0]);
The reason for this architecture is that when my app starts it will connect to my Raspberry Pi box. Several api calls are made to get the data from that box.
If for any reason connection is not made or an error is returned I wish to rerun the action until it does so (obviously there would be a cut off).
Task<T> Relay<T>()
Method is generic as it will be called from many different places. So, whenever a server error occurs I would catch and throw back up the stack.
What is happening though is that
return true;
is being called as soon as the action is called.
Related
Wanted to verify if HttpCLient instance should be created outside method passed to polly for ExecuteAsync, or in?
My current usage varies between the two options and I am not sure which is the correct one?
Also, if it incurs some drawbacks, or possible memory leaks, etc. ?
Get:
var client = new HttpClient(new NativeMessageHandler()) { Timeout = new TimeSpan(0, 0, TimeOutSec) };
var httpResponse = await AuthenticationOnUnauthorizePolicy.ExecuteAsync(async () =>
{
UpdateClientHeader(client, correlationId);
return await client.GetAsync(url, token);
});
Post:
var httpResponse = await AuthenticationOnUnauthorizePolicy.ExecuteAsync(async () =>
{
using (var client = new HttpClient(new NativeMessageHandler()) { Timeout = new TimeSpan(0, 0, TimeOutSec) })
{
UpdateClientHeader(client, correlationId);
WriteNetworkAccessStatusToLog();
return await client.PostAsync(url, content);
}
});
The policy used here:
AuthenticationOnUnauthorizePolicy = Policy
.HandleResult<HttpResponseMessage>(reposnse => reposnse.StatusCode == HttpStatusCode.Unauthorized)
.RetryAsync(1, onRetryAsync:
async (response, count, context) =>
{
_logger.Info("Unauthorized Response! Retrying Authentication...");
await Authenticate();
});
Appreciates any comments on the code above.
Is there a correct way?
Do I need to use the Context to get the client again, or is my usage okay?
Update:
Authenticate method:
public virtual async Task Authenticate()
{
// lock it - only one request can request token
if (Interlocked.CompareExchange(ref _isAuthenticated, 1, 0) == 0)
{
var result = new WebResult();
var loginModel = new LoginModel
{
email = _settingService.Email,
password = _settingService.Password
};
var url = ......
var correlationId = Guid.NewGuid().ToString();
try
{
var stringObj = JsonHelper.SerializeObject(loginModel);
HttpContent content = new StringContent(stringObj, Encoding.UTF8, HttpConsts.JsonMediaType);
using (var client = new HttpClient(new NativeMessageHandler()) { Timeout = new TimeSpan(0, 0, TimeOutSec) }
)
{
UpdateClientHeader(client, correlationId, useToken: false); // not token, we need new one
using (var httpResponse = await client.PostAsync(url, content))
{
var sReader = await httpResponse.Content.ReadAsStringAsync();
await HandleRequest(result, sReader, httpResponse, correlationId, url, "result");
}
}
if (result != null && !result.HasError)
{
_loginToken = result.Token;
}
}
catch (Exception ex)
{
// Log error
}
finally
{
_isAuthenticated = 0;
}
}
}
Update client headr method:
if (_loginToken != null &&
!client.DefaultRequestHeaders.Contains("Token"))
{
client.DefaultRequestHeaders.Add("Token", _loginToken );
}
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(HttpConsts.JsonMediaType));
I'm trying to test this HttpRequest:
public void TestX(string baseUrl)
{
StringContent httpContentDistanza = new StringContent(GlobalVariables.JsonDistanza);
using HttpClient httpClient = new HttpClient
{
BaseAddress = new Uri(baseUrl)
};
HttpResponseMessage responseMessage = null;
try
{
responseMessage = httpClient.PostAsync("/xxx/xx/xxx", httpContentDistanza).Result;
// can't reach the code below
if (responseMessage.IsSuccessStatusCode)
{
string strContext = responseMessage.Content.ReadAsStringAsync().Result;
var risultato = JsonSerializer.Deserialize<Distanza1>(strContext);
GlobalVariables.DblAijCrnPsz = risultato.data.processDataIn.valore;
}
}
catch (Exception ex)
{
if (responseMessage == null)
{
responseMessage = new HttpResponseMessage();
}
responseMessage.StatusCode = HttpStatusCode.InternalServerError;
responseMessage.ReasonPhrase = string.Format("RestHttpClient.SendRequest failed: {0}", ex);
}
}
The problem is that the URI is not reachable, and I was expecting that its gonna throw some Exception, but it did not.
In case where URI is not reachable I need some how to catch that Exception.
I'm using BackgroundWorker to run TestX():
public MainWindow()
{
InitializeComponent();
bgWorker = new BackgroundWorker { WorkerReportsProgress = true };
bgWorker.DoWork += ResetAll;
}
private void ResetAll(object sender, DoWorkEventArgs e)
{
var x = gestLink.TestX(GlobalVariables.BaseUrl).ToString();
//it also does't reach the code below
....
}
Update
I don't know what I'm doing wrong.. I still can't catch the exception :
public async Task TestX(string baseUrl)
{
StringContent httpContentDistanza = new StringContent(GlobalVariables.JsonDistanza);
using HttpClient httpClient = new HttpClient
{
BaseAddress = new Uri(baseUrl)
};
HttpResponseMessage responseMessage = null;
try
{
responseMessage = await client.PostAsync("/xxx/xxx/xx", httpContentDistanza);
if (responseMessage.IsSuccessStatusCode)
{
string strContext = await responseMessage.Content.ReadAsStringAsync();
var risultato = JsonSerializer.Deserialize<Distanza1>(strContext);
}
}
catch(Exception ex)
{
var error = ex.Message;
}
It crash here responseMessage = await httpClient.PostAsync("/xxx/xx/xxx", httpContentDistanza); but stops in ResetAll() and var x = this.
I have seen and read similar problems (Debugger stops after async HttpClient.GetAsync() and HttpClient.PostAsync and await crashing app) before, but no solutions have helped me.
Any suggestions how to catch the exception?
You should change this:
public void TestX(string baseUrl)
to
public async Task TestX(string baseUrl)
and this
responseMessage = httpClient.PostAsync("/xxx/xx/xxx",httpContentDistanza).Result;
to
responseMessage = await httpClient.PostAsync("/xxx/xx/xxx", httpContentDistanza);
Then you can handle Exceptions.
I am having a problem with sending a POST request and getting a response. I have made local PHP script which returns some string values, and I can't get it to work with Xamarin.
This is the method I am using for to send the request:
public async Task<string> Post_Request()
{
var request = new HttpRequestMessage();
request.RequestUri = new Uri("http://localhost/server.php");
request.Method = HttpMethod.Post;
request.Headers.Add("Accept", "application/json");
var client = new HttpClient();
HttpResponseMessage response = await client.SendAsync(request).ConfigureAwait(continueOnCapturedContext: false);
if (response.StatusCode == HttpStatusCode.OK)
{
return "OK";
}
else
{
return "BAD!";
}
}
When debbuging, the program does not go into the if or else code branches on the "if (response.StatusCode == HttpStatusCode.OK)" condition.
This is my PHP script:
<?php
return
"
{
"user":"01",
"name":"ime"
}
"
//echo "OK";
?>
This is a sample post request that I have used.
var objRequest = new CustomerDetailsRequest() {
customerId = 1
};
string url = $"/api/v1/CustomerDetails";
var requestBody = await Task.Run(() => JsonConvert.SerializeObject(objRequest));
using (var httpClient = new HttpClient())
{
CustomerDetailsResponse data = new CustomerDetailsResponse();
try
{
httpClient.BaseAddress = new Uri("http://localhost:3000");
var content = new StringContent(requestBody, Encoding.UTF8, "application/json");
var result = await httpClient.PostAsync(url, content);
var response = await result.Content.ReadAsStringAsync();
data = JsonConvert.DeserializeObject<CustomerDetailsResponse>(response);
if (result.IsSuccessStatusCode && result.StatusCode == HttpStatusCode.OK)
{
return data;
}
return null;
}
catch (Exception exp)
{
return null;
}
}
Let me know if this is confusing
I'm trying to upload data to an external webservice, i'm using the WebClients UploadDataTaskAsync to do this asynchronously. but it never returns. I've tested this using the synchronous call and it will timeout eventually.
public Notification Notify(Notification notification)
{
var messageXml = MessageFactory.Create(notification);
var statusCode = SendWebRequestAsync(messageXml);
notification.ProcessedAttempts++;
if (statusCode.Result == HttpStatusCode.OK)
{
notification.Processed = true;
notification.DateProcessed = DateTime.UtcNow;
}
return notification;
}
private async Task<HttpStatusCode> SendWebRequestAsync(string message)
{
using (var webClient = new WebClient())
{
byte[] data = message.ToUtf8Bytes();
var uri = new Uri(url);
try
{
webClient.UploadDataCompleted += WebClient_UploadDataCompleted;
var result = await webClient.UploadDataTaskAsync(uri, "POST", data);
string response = Encoding.UTF8.GetString(result);
if (response == "")
return HttpStatusCode.OK;
return HttpStatusCode.NoContent;
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError)
{
var response = ex.Response as HttpWebResponse;
if (response != null)
{
return response.StatusCode;
}
}
// no http status code available
return HttpStatusCode.ServiceUnavailable; // guess
}
catch (Exception)
{
return HttpStatusCode.InternalServerError;
}
}
}
private void WebClient_UploadDataCompleted(object sender, UploadDataCompletedEventArgs e)
{
Console.WriteLine(e.Result);
}
You're calling statusCode.Result which is the classic ASP.NET deadlock. This makes no sense because you're apparently using async IO to get scalability benefits and then destroying that by blocking.
Don't block.
Also, it looks like HttpClient could reduce the code that you have there a bit.
I have this method in my Windows Phone 8 app where I get some data from a url
public async static Task<byte[]> getData(string url)
{
HttpClient client = null;
HttpResponseMessage response = null;
Stream stream = null;
byte[] dataBytes = null;
bool error = false;
try
{
Uri uri = new Uri(url);
client = new HttpClient();
response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
stream = await response.Content.ReadAsStreamAsync();
dataBytes = getDataBytes(stream);
if (dataBytes == null)
{
error = true;
}
else if (dataBytes.Length == 0)
{
error = true;
}
}
catch (HttpRequestException )
{
}
if (error)
{
return getData(url); // this is where the issue is
}
return dataBytes;
}
But since the method is an async one, the return type cannot be a Task, like I have done on the line return getData(url); since getData(string) returns Task. Any ideas on how I can rewrite this to make it work?
Awaiting the result of getData may do the trick. Still, I strongly recommand you to rewrite your method with a loop, rather than recursively call the method again. It makes it hard to read, and may lead to unforeseen issues.
public async static Task<byte[]> getData(string url)
{
bool success = false;
byte[] dataBytes = null;
while (!success)
{
try
{
Uri uri = new Uri(url);
var client = new HttpClient();
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync();
dataBytes = getDataBytes(stream);
success = dataBytes != null && dataBytes.Length > 0;
}
catch (HttpRequestException)
{
}
}
return dataBytes;
}
you can get around the compile error by adding changing the return to the following :
if (error)
{
return await getData(url); // this is where the issue is
}
I hope you do realize that this code will keep on looping as long as no data is returned? having many clients like this could easily overload your server.