I have a Xamarin.Forms App which should communicate with an embedded device via a RESTService.
So in my SharedProject I have a Service which wraps a httpclient.
HttpClient client = new HttpClient() { Timeout = new TimeSpan(0, 0, 31) };
public async Task<MetaData> RequesMetaData()
{
try
{
var response =await client.GetAsync("http://192.168.1.23:9090/api/meta/", HttpCompletionOption.ResponseContentRead);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
var meta = JsonConvert.DeserializeObject<MetaData>(result);
return meta;
}
catch(OperationCanceledException ocex)
{
ConnectionError?.Invoke(new Message { MessageInfo = ocex.Message, IsError = true });
return new MetaData();
}
catch(Exception ex)
{
ConnectionError?.Invoke(new Message { MessageInfo = ex.Message, IsError = true });
Debug.Print(ex.GetType().Name);
return new MetaData();
}
}
The call should return a Json, which works most times.
The problem is sometimes, the last byte, the curly bracket '}' of the Json is missing.
It is always the last '}', no matter how long the message sent by the server was.
This only occurs on Android. When I test the RESTservice from Firefox,
no such error occurs. Additionally I logged with Wireshark, which reports the full json was delivered over the wire.
Also I tried the same code in a WPF Desktop App, which works flawless. Only on Xamarin Android it is missing the last byte. In the Android Options,
I tried all options (Android,Managed,Default).
Instead of ReadAsStringAsync(), I also tried ReasAsStreamAsync(), but got the same results.
When changing to https, this issue persits but in another form. Instead of the missing last byte, on occassion the Content will just be an empty string "",
maybe because the message could not be decrypted.
For testing I use the x86 Emulator of VisualStudio(Android 9) , as well as a Nexus 5 (Android 6).
I think I found the issue.
The server of the embedded device I was talking to was using http/1.0 and didn't include content-length. Changing it to use http/1.1 and using content-lenght solved the issue for me.
I can only guess that Xamarin.Android HttpClient has problems with http/1.0.
Related
I've tried many different approaches for the past couple of hours, but my method call is hanging up the thread.
Here is my Web API code, which works fine when making AJAX call from the MVC client, but I'm trying to test calling from the server:
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
Below is my MVC controller code and model code:
public async Task<ActionResult> TestApi()
{
try
{
var result = await VoipModels.GetValues();
return MVCUtils.JsonContent(result);
}
catch (Exception ex)
{
return MVCUtils.HandleError(ex);
}
}
...
public static async Task<string[]> GetValues()
{
string[] result = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:44305/api/");
//THIS IS THE LINE THAT HANGS UP - I'VE TRIED MANY VARIATIONS
var response = await client.GetAsync("values", HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
result = await response.Content.ReadAsAsync<string[]>();
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
return result;
}
I've used this format successfully when calling a separate, 3rd party API. I've run out of examples to try from my couple of hours of Googling.
What am I doing wrong here?
Check your port number. Based on your code, you have "hard coded" the port "http://localhost:44305/api/" which may likely be incorrect, you should convert that to grab it from the host
configuration instead.
Check your local machine's firewall. Make sure that your local machine's firewall is allowing connections to the port assigned.
Check your protocol. Ensure that you are using http or https appropriately in your request URL.
As a special note, there are very rare cases / exception cases that you would want to have a web API server call itself. Doing so, is rather inefficient design as it will consume resources for no gain (such a generating request and response).
I am currently using a web service, which offers 2 endpoints, as backups for fall over. I need to test all 2 endpoints before my code completely fails and then will need to log the exception. My thoughts were to be to return the status code of the HTML response using this:
Function1:
public string ValidateHttpRequest(string endpointUrl)
{
try
{
var url = endpointUrl;
HttpClient httpClient = new HttpClient();
var reponse = httpClient.GetAsync(endpointUrl);
return reponse.Result.StatusCode.ToString();
}
catch (Exception ex)
{
Log.Log("exception thrown in ValidateHttpRequest()! " + ex.ToString());
Log.Log(ex);
return null;
}
}
This is called from another function, say function2().
Function 2:
private bool function2()
{
//Specify the binding to be used for the client.
BasicHttpsBinding binding = new BasicHttpsBinding();
var epA = "https://www1.endpoint1.com/endpointService.asmx";
var epB = "https://www2.endpoint1.com/endpointService.asmx";
if (ValidateHttpRequest(epA)== "OK")
{
EndpointAddress address = new EndpointAddress("https://www1.enpoint1.com/endpointService.asmx");
_Client = new WebService.SoapClient(binding, address);
return true;
}
else if ((ValidateHttpRequest(epB))== "OK")
{
EndpointAddress address2 = new EndpointAddress(("https://www2.enpoint2.com/endpointService.asmx"));
else
{
// Now Log error here completely, and only fail here if both above checks return anything apart from 200 status code
LogException(“Only log exception if all endpoints fail”);
return false;
}
}
This is all well and good, however I need this to not fail on the first call, as I will need to check if the other endpoint is valid/active. The issue is that if the response is null, the exception is handled and I will not check the rest of my endpoints, how can I correctly ensure my code is safe with i.e. exceptions are handled correctly, but continuing my code to check all endpoints before completely failing and halting execution. it should fail if i receive any other response apart from 200 OK I have researched about how to check the HTTP response and all that I can come up with is this but it doesn’t completely suit my needs .If anyone could point me in the right direction or help with a solution I would be very grateful.
Thanks in advance
I have seen a few questions posted with this same problem. I think I have all the pieces, but I am still getting the "Empty Payload" error. Does anyone see what is missing?
I want to update the category of some mail messages. I am using the beta endpoint because of this post: Microsoft Graph Client SDK vs. Requesting JSONs
Here is my code:
public async void UpdateMailCategory(GraphServiceClient graphClient, string messageId, string inbox)
{
string newCategory = #"{'categories': ['Processed']}";
try
{
// Get the request URL for adding a page.
string requestUrl = graphClient
.Users[inbox]
.Messages[messageId]
.Request()
.RequestUrl;
HttpRequestMessage hrm =
new HttpRequestMessage(new HttpMethod("PATCH"), requestUrl);
hrm.Content =
new StringContent(newCategory, Encoding.UTF8, "application/json");
// Authenticate (add access token) our HttpRequestMessage
await graphClient.AuthenticationProvider
.AuthenticateRequestAsync(hrm);
// Send the request and get the response.
HttpResponseMessage response =
await graphClient.HttpProvider.SendAsync(hrm);
}
catch (ServiceException Servex)
{
throw Servex;
}
}
When I look at the hrm.content, it shows this:
{ System.Net.Http.StringContent }
Headers:
{
Content - Type : application / json;charset = utf - 8
Content - Length : 35
}
You're using the Graph Client SDK is a rather roundabout way which is more likely going to cause you headaches than anything else. It also leads to more complicated code than necessary.
The SDK includes everything you need for the entire request. With the exception of an edge case, you should never need to deal with the HttpProvider or manage HttpRequestMessage and HttpResponseMessage instances.
The following will accomplish the same thing (setting the Categories property of a message) with a lot less complexity:
public async void UpdateMailCategory(GraphServiceClient graphClient, string messageId, string inbox)
{
try
{
await graphClient
.Users[inbox]
.Messages[messageId]
.Request()
.UpdateAsync(new Message()
{
Categories = new List<string>() { "Processed" }
});
}
catch (ServiceException Servex)
{
throw Servex;
}
}
You also shouldn't use the /beta version for this operation as it is supported by the /v1.0 version. You should only need to leverage the /beta version to manage the user's Categories since this isn't generally available yet.
I am developing (at least attempting anyways...) a xamarin android app with connection to API. Below is a basic example of what I am trying to do with a Post. I am not sure how to return the error message (not just code) back to client? I am open to a better way of doing this as long as a complete example can be provided.
API code:
[HttpPost("Consume")]//*
public IActionResult ConsumeInventory([FromBody] Part part)
{
try
{
_repo.ConsumeInventory(part);
}
catch (Exception ex)
{
//ModelState.AddModelError("Error", ex.Message);
return NotFound("Not Enough Inventory");
}
return Ok(part);
}
Client side call:
public async Task<HttpResponseMessage> UpdatePartAsync(Part part, string post_url)
{
string jsonString = JsonConvert.SerializeObject(part);
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PostAsync("invcount/" + post_url, new StringContent(jsonString, Encoding.UTF8, "application/json"));
if (response.StatusCode == HttpStatusCode.NotFound)
{...//Would like to display "Not enough Inventory" in here}
return response;
}
Even when the API call is unsuccessful, you can read the content of the response by using HttpResponseMessage.Content.ReadAsStringAsync()
if (response.StatusCode == HttpStatusCode.NotFound)
{
// Would like to display "Not enough Inventory" in here}
var resposeContent = response.Content?.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<ServerResponse>(resposeContent);
// this guy will have your human-readable description or in your case, you can display the whole `result` string
Debug.WriteLine(result.Description);
}
And define you ServerResponse class as you designed your API. I regularly inherit all my API responses from the same base class so on the client side I can parse them first to check for custom directives from server (custom codes which are not always errors) message:
public class ServerResponse
{
public string Code {get;set;}
public string Description {get;set;}
}
ps: beware, HttpResponseMessage.Content could be null in certain cases.
Your question has nothing to do with Xamarin. In this case Xamarin app is just a front-end that consumes your API.
Generally speaking, you should generalise your error responses and if needed log the exception to DB for a further analysis. JSON example:
{
"Code": 1000,
"Description": "Human readable description",
"Id": "6a154fd3-4142-4a9c-80b5-c635e84123fa"
}
Code for internal error code in the system and Id for identifying the stack trace in the DB. So your end-user will get a nice human readable description and an Id that he could use to contact the support.
Technically, use a ExceptionFilterAttribute in order to catch and return a generalised error response to your clients. Reading Global Error Handling in ASP.NET Web API 2 may also give you a better overview.
In your client app after you get a response from the API you can check if the response has a success status code:
if (!response.IsSuccessStatusCode)
{
// Deserialize error message
}
P.S.: Stackoverflow is not a coding service, so there will be no a complete example. Hope you will get some ideas from my answer on how to handle it in a better way.
Good luck!
I started to learn Xamarin to develop application for iOS, Android and Windows Phone.
Seems a simple question but I don't know how to proceed with Xamarin.
I have the following lines of code:
public async Task<List<MyData>> GetItemsAsync()
{
HttpClient client = new HttpClient();
try {
string result = await client.GetStringAsync( "https://jsonplaceholder.typicode.com/posts/1" );
if(!string.IsNullOrWhiteSpace(result)) {
}
} catch( Exception ex ) {
return await Task.FromResult(new List<MyData>(0));
} finally {
client.Dispose(); //of course, I can use `using`
}
return await Task.FromResult(new List<MyData>(0));
}
When debug in Visual Studio Android Emulator(VisualStudio_android-23_x86_phone (Android 6.0 - API 23)), the line where starts with string result is stuck, nothing happens, no exception is thrown. I cannot use F10 in Visual Studio to inspect.
Also, I cannot use WebClient because System.Net doesn't include it.
Edit: If I remove await then I receive a general exception:
Where is my mistake ?
For those who meet the error:
Error: SecureChannelFailure (The authentication or decryption has failed.)
Please use specific handler for Android:
using (HttpClient myClient = new HttpClient(new AndroidClientHandler())) { }
or for iOS:
using(HttpClient myClient = new HttpClient(new CFNetworkHandler())) {}
References:
https://developer.xamarin.com/guides/cross-platform/macios/http-stack/
https://developer.xamarin.com/guides/android/application_fundamentals/http-stack/