Error using MvxMultiPartFormRestRequest to post image/data to API controller - android

I am building an Android app which has a form that user can post image for an item.
So the post data is an int field and an image.
I use MvvmCross Network plugin to post and got below error. I am a beginner and I do not know where I did wrong: mobile app code or API controller code?
error = {System.Net.WebException: The remote server returned an error: (500) Internal Server Error.
at System.Net.HttpWebRequest.CheckFinalStatus (System.Net.WebAsyncResult result) [0x00000] in <filename unknown>:0
at System.Net.HttpWebRequest.SetResponseData ...
This is mobile app code:
This is select image code:
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if ((requestCode == PickImageId) && (resultCode == Result.Ok) && (data != null))
{
_imgUri = data.Data;
_imgPath = GetPathToImage(_imgUri);
_contentType = ContentResolver.GetType(_imgUri);
}
}
Then click Submit button
private void btnSubmit_Click(object sender, EventArgs e)
{
MemoryStream stream = new MemoryStream();
ContentResolver.OpenInputStream(_imgUri).CopyTo(stream);
_vm.Submit(_imgPath, _contentType, stream);
}
This is Submit function:
public void Submit(string fileName, string contentType, MemoryStream stream) {
//Post data
int itemId = 1;
List<MvxMultiPartFormRestRequest.IStreamForUpload> streams = new List<MvxMultiPartFormRestRequest.IStreamForUpload>();
streams.Add(new MvxMultiPartFormRestRequest.MemoryStreamForUpload("userFile", fileName, contentType, stream));
var client = Mvx.Resolve<IMvxJsonRestClient>();
var r = new MvxMultiPartFormRestRequest("https://.../api/ItemUserImage");
r.FieldsToSend.Add("itemId", itemId.ToString());
r.StreamsToSend.AddRange(streams);
client.MakeRequestFor<MyResponse>(r, (result) =>
{
Mvx.Resolve<IUserInteraction>().Alert(result.Result.ResponseText, null, TitleInformation);
}, (error) =>
{
//I met error here
});
This is my API controller:
public class ItemUserImageController : ApiController
{
public async Task<HttpResponseMessage> PostFormData()
{
Response response = new Response();
response.ResponseCode = 1;
response.ResponseText = "step0-";
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
response.ResponseText += "step1-";
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
response.ResponseText += "step2-";
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
response.ResponseText += "step3-";
try
{
// Read the form data.
await Request.Content.ReadAsMultipartAsync(provider);
response.ResponseText += "step4-";
foreach (var key in provider.FormData.AllKeys)
{
foreach (var val in provider.FormData.GetValues(key))
{
response.ResponseText += string.Format("{0}: {1}-", key, val);
}
}
response.ResponseText += "step5-";
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
response.ResponseText += string.Format("{0} - Server file path: {1}-", file.Headers.ContentDisposition.FileName, file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK, response);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, response.ResponseText ,e);
}
}
}
Please help. Thank you.

This "bug" could be lots of things. Really the best way to resolve it is to get in there with some debugging tools, to set breakpoints in both the client and the ASP.Net app and to see what the communication is.
To set breakpoints in the client app, use Visual or Xamarin Studio.
To see the raw HTTP traffic between the client app and the server, use Fiddler - see https://stackoverflow.com/a/25412339/373321 (this assumes you have a 4.4 or later Android device)
To set breakpoints in the server app, use Visual Studio and try exposing the website from your development box beyond localhost using IISExpress settings - see http://johan.driessen.se/posts/Accessing-an-IIS-Express-site-from-a-remote-computer
Once you start debugging this, I'm sure you'll quickly
Beyond that, the only "spider sense tingle" I got looking through your client code was a slight concern that you might need to reset the current position in your MemoryStream back to the start (but I haven't thought this fully through).

Related

Updating Xamarin.Forms breaks Prism navigation for Android

I have a Xamarin app made up of several pages, and I'm using Prism with AutoFac. I'm unable to update Xamarin.Forms without breaking navigation on the Android project only. It works fine on iOS.
I started with Xamarin.Form 3.1, and I cannot update to anything beyond that. My main page is a login page - when that is successful I navigate to the home page like so:
try
{
await _navigationService.NavigateAsync(new Uri($"/NavigationPage/{nameof(HomePage)}", UriKind.Absolute));
}
catch (Exception e)
{
Log.Error(e);
}
The navigation is not throwing any exceptions, and I'm not picking up any errors anywhere. Release notes for Xamarin 3.2 doesn't provide any clues either. I don't even know if this is a Xamarin or Prism issue. A few days of debugging and I feel no closer to figuring this out.
Has anyone else experienced this? or have any idea what could be going wrong?
Edit 1:
I finally isolated the issue - the fix was to call BeginInvokeOnMainThread when I navigate. But a few things still don't make sense to me:
This should raise an exception, so I must be hiding it somewhere. Is there anything obvious in the code below [This is the first time I've used Async, so seems likely I'm doing something wrong there]?
Why did this work with Xamarin 3.1 on not later versions
My logging confirms that the original navigation code was running on the main thread, but it still failed.
The code:
We are doing client-side google authentication with Azure, if that is successful we navigate to the home page.
First step, we connect to GooglePlay and authenticate the user
public void Login(MobileServiceClient client, Action<string, bool> onLoginComplete)
{
_client = client;
_onLoginComplete = onLoginComplete;
var signInIntent = Auth.GoogleSignInApi.GetSignInIntent(_googleApiClient);
((MainActivity)_context).StartActivityForResult(signInIntent, 1);
_googleApiClient.Connect();
}
The result comes to OnActivityResult in MainActivity.cs:
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == SignInId)
{
Log.Info("Received result from Google sign in");
var result = Auth.GoogleSignInApi.GetSignInResultFromIntent(data);
DroidLoginProvider.Instance.OnAuthCompleted(result);
}
}
Which calls the OnAuthCompleted method. There are a few paths here. If the token is valid we don't re-authenticate with Azure, and just retrieve the saved user details:
public void OnAuthCompleted(GoogleSignInResult result)
{
if (result.IsSuccess)
{
Log.Trace("Native google log in successful");
var signInAccount = result.SignInAccount;
var accounts = _accountStore.FindAccountsForService("monkey_chat");
if (accounts != null)
{
foreach (var acct in accounts)
{
if (acct.Properties.TryGetValue("token", out var azureToken) && acct.Properties.TryGetValue("email", out var email))
{
if (!IsTokenExpired(azureToken))
{
Log.Trace("Auth token is still valid");
_client.CurrentUser = new MobileServiceUser(acct.Username)
{
MobileServiceAuthenticationToken = azureToken
};
_onLoginComplete?.Invoke(email, true);
return;
}
Log.Trace("Auth token no longer valid");
}
}
}
// Authenticate with Azure & get a new token
var token = new JObject
{
["authorization_code"] = signInAccount.ServerAuthCode,
["id_token"] = signInAccount.IdToken
};
try
{
var mobileUser = Task.Run(async () =>
{
try
{
Log.Trace("Authenticating with Azure");
return await client.LoginAsync(MobileServiceAuthenticationProvider.Google, token).ConfigureAwait(false);
}
catch (Exception e)
{
Log.Error(e);
throw;
}
}).GetAwaiter().GetResult();
var account = new Account(_client.CurrentUser.UserId);
account.Properties.Add("token", _client.CurrentUser.MobileServiceAuthenticationToken);
account.Properties.Add("email", signInAccount.Email);
_accountStore.Save(account, "monkey_chat");
_googleUser = new GoogleUser
{
Name = signInAccount.DisplayName,
Email = signInAccount.Email,
Picture = new Uri((signInAccount.PhotoUrl != null
? $"{signInAccount.PhotoUrl}"
: $"https://autisticdating.net/imgs/profile-placeholder.jpg")),
UserId = SidHelper.ExtractUserId(mobileUser?.UserId),
UserToken = mobileUser?.MobileServiceAuthenticationToken
};
_onLoginComplete?.Invoke(signInAccount.Email, true);
}
catch (Exception ex)
{
_onLoginComplete?.Invoke(string.Empty, false);
Log.Error(ex);
}
}
else
{
_onLoginComplete?.Invoke(string.Empty, false);
}
}
My original OnLoginComplete[Not working]:
private async void OnLoginComplete(bool successful, bool isNewUser)
{
if (successful)
{
try
{
Log.Info("Starting navigation to home page");
await _navigationService.NavigateAsync(new Uri($"/NavigationPage/{nameof(HomePage)}", UriKind.Absolute)).GetAwaiter().GetResult();
}
catch (Exception e)
{
Log.Error(e);
}
}
}
New OnLoginComplete[Working]
private void OnLoginComplete(bool successful, bool isNewUser)
{
Device.BeginInvokeOnMainThread(() =>
{
if (successful)
{
try
{
Log.Info("Starting navigation to home page");
_navigationService.NavigateAsync(new Uri($"/NavigationPage/{nameof(HomePage)}", UriKind.Absolute)).GetAwaiter().GetResult();
}
catch (Exception e)
{
Log.Error(e);
}
}
});
}

Xamarin & Android - crash on exiting from method

I don't know what can I tell more.
I have this method:
public async Task<HttpResponseMessage> SendAsyncRequest(string uri, string content, HttpMethod method, bool tryReauthorizeOn401 = true)
{
HttpRequestMessage rm = new HttpRequestMessage(method, uri);
if (!string.IsNullOrWhiteSpace(content))
rm.Content = new StringContent(content, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.SendAsync(rm);
if (response.StatusCode == HttpStatusCode.Unauthorized && tryReauthorizeOn401)
{
var res = await AuthenticateUser();
if(res.user == null)
return response;
return await SendAsyncRequest(uri, content, method, false);
}
return response;
}
Nothing special.
client.SendAsync(rm) is executed, response.StatusCode is Ok.
Application just crashes when exiting from this method.
Output shows me just this assert:
12-16 20:09:22.025 F/ ( 1683): * Assertion at /Users/builder/jenkins/workspace/xamarin-android-d15-9/xamarin-android/external/mono/mono/mini/debugger-agent.c:4957, condition `is_ok (error)' not met, function:set_set_notification_for_wait_completion_flag, Could not execute the method because the containing type is not fully instantiated. assembly:<unknown assembly> type:<unknown type> member:(null)
12-16 20:09:22.025 F/libc ( 1683): Fatal signal 6 (SIGABRT), code -6 in tid 1683 (omerang.Android)
And nothing more.
client is HttpClient.
I have setting in my Android project: HttpClient Implementation set to Android.
Does anyone have any idea what could be wrong?
edit
SendAsyncRequest is used like that:
public async Task<(HttpResponseMessage response, IEnumerable<T> items)> GetListFromRequest<T>(string uri)
{
HttpResponseMessage response = await SendAsyncRequest(uri, null, HttpMethod.Get);
if (!response.IsSuccessStatusCode)
return (response, null);
string content = await response.Content.ReadAsStringAsync();
var items = JsonConvert.DeserializeObject<IEnumerable<T>>(content);
return (response, new List<T>(items));
}
Based on the provided example project code you provided
protected override async void OnStart()
{
Controller c = new Controller();
TodoItem item = await c.GetTodoItem(1);
TodoItem item2 = await c.GetTodoItem(2);
}
You are calling async void fire and forget on startup.
You wont be able to catch any thrown exceptions, which would explain why the App crashes with no warning.
Reference Async/Await - Best Practices in Asynchronous Programming
async void should only be used with event handlers, so I would suggest adding an event and handler.
based on the provided example, it could look like as follows
public partial class App : Application {
public App() {
InitializeComponent();
MainPage = new MainPage();
}
private even EventHander starting = delegate { };
private async void onStarting(object sender, EventArgs args) {
starting -= onStarting;
try {
var controller = new Controller();
TodoItem item = await controller.GetTodoItem(1);
TodoItem item2 = await controller.GetTodoItem(2);
} catch(Exception ex) {
//...handler error here
}
}
protected override void OnStart() {
starting += onStarting;
starting(this, EventArgs.Empty);
}
//...omitted for brevity
}
With that, you should now at least be able to catch the thrown exception to determine what is failing.
try to update your compileSdkVersion to a higher version and check.
also try following
> Go to: File > Invalidate Caches/Restart and select Invalidate and Restart

Signal R persistent connection on phonegap apps (android and ios)

I am using SignalR persistent connection for smooth communication between my client and server. All the web communications work really fine.
Now I am trying to do the same with my PhoneGap apps. It doesn't work. Is there any reason?
Here is my code
StartEvents = function () {
connection = $.connection(connectionString);
connection.logging = true;
connection.received(function (result) {
for (i = 0; i < result.events.length; ++i) {
}
});
connection.start().done(function () {
console.log("connected");
});
};
It does log out connected at the start when the app is connected the first time. But then as events keep happening nothing is pushed from the server.
Server code is this
public class ApiConnection : PersistentConnection
{
protected override Task OnConnected(IRequest request, string connectionId)
{
StartBroadcast(connectionId);
return base.OnConnected(request, connectionId);
}
}
private void StartBroadcast(string connectionId)
{
var timerCall = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1));
_subscription = timerCall.Subscribe(async res =>
{
List<dynamic> result = pull();
if (result != null && result.Count > 0)
await Connection.Send(connectionId, new
{
id = _newestId,
events = result
});
});
}

Using ZXing in Xamarin for Android, how do I stop continuous scanning right after I get my result?

I'm using ZXing in an Android app being developed in Xamarin to scan a QR code and start playing the corresponding audio file automatically.
My problem is that when I get a result from scanning, it takes some time for the audio player activity to load so it gets called twice or more due to subsequent successful scannings.
Is there a way to stop continuous scanning as soon as I get a correct result?
Here's the code:
//Start scanning
scanner.ScanContinuously(opt, HandleScanResult);
}
private void HandleScanResult(ZXing.Result result)
{
string msg = "";
if (result != null && !string.IsNullOrEmpty(result.Text))
{
msg = result.Text;
var playerActivity = new Intent(myContext, typeof(AudioActivity));
//-------------------------------------------------------------
// Prerequisite: load all tracks onto "Assets/tracks" folder
// You can put here qr code - track assignments here below
// msg: decoded qr code
// playerActivity.Putextra second parameter is a relative path
// under "Assets" directory
//--------------------------------------------------------------
//Iterate through tracks stored in assets and load their titles into an array
System.String[] trackArray = Application.Context.Assets.List("tracks");
bool trackFound = false;
foreach (string track in trackArray)
{
if (track.Equals(msg + ".mp3"))
{
playerActivity.PutExtra("Track", "tracks/" + msg + ".mp3");
for (int i = 0; i < PostList.postList.Count; i++)
{
if (PostList.postList.ElementAt(i).code.Equals(msg))
playerActivity.PutExtra("TrackTitle", PostList.postList.ElementAt(i).title);
}
myContext.StartActivity(playerActivity);
trackFound = true;
}
}
Thank you!
Old question but i'll post it anyway for anyone still looking for this information.
You need your scanner to be a class variable. This is my code:
public MobileBarcodeScanner scanner = new MobileBarcodeScanner();
private void ArrivalsClick(object sender, EventArgs e)
{
try
{
if (Arrivals.IsEnabled)
{
MobileBarcodeScanningOptions optionsCustom = new MobileBarcodeScanningOptions();
scanner.TopText = "Scan Barcode";
optionsCustom.DelayBetweenContinuousScans = 3000;
scanner.ScanContinuously(optionsCustom, ArrivalResult);
}
}
catch (Exception)
{
throw;
}
}
private async void ArrivalResult(ZXing.Result result)
{
if (result != null && result.Text != "")
{
// Making a call to a REST API
if (resp.StatusCode == System.Net.HttpStatusCode.OK)
{
int? res = JsonConvert.DeserializeObject<int>(resp.Content);
if (res == 0)
{
scanner.Cancel(); // <----- Stops scanner (Something went wrong)
Device.BeginInvokeOnMainThread(async () =>
{
await DisplayAlert("..", "..", "ΟΚ");
});
}
else
{
Plugin.SimpleAudioPlayer.ISimpleAudioPlayer player = Plugin.SimpleAudioPlayer.CrossSimpleAudioPlayer.Current;
player.Load("beep.wav");
player.Play(); // Scan successful
}
}
else
{
scanner.Cancel();
Device.BeginInvokeOnMainThread(async () =>
{
await DisplayAlert("..", "..", "ΟΚ");
});
}
}
}

System.Net.Http.HttpClient with AutomaticDecompression and GetAsync (timeout) vs GetStringAsync (working

I have the following code to make requests to a REST API, using Xamarin and an Android device:
public class ApiBase
{
HttpClient m_HttpClient;
public ApiBase(string baseAddress, string username, string password)
{
if (!baseAddress.EndsWith("/"))
{
baseAddress += "/";
}
var handler = new HttpClientHandler();
if (handler.SupportsAutomaticDecompression)
{
handler.AutomaticDecompression = DecompressionMethods.GZip;
}
m_HttpClient = new HttpClient(handler);
m_HttpClient.BaseAddress = new Uri(baseAddress);
var credentialsString = Convert.ToBase64String(Encoding.UTF8.GetBytes(username + ":" + password));
m_HttpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", credentialsString);
m_HttpClient.Timeout = new TimeSpan(0, 0, 30);
}
protected async Task<XElement> HttpGetAsync(string method)
{
try
{
HttpResponseMessage response = await m_HttpClient.GetAsync(method);
if (response.IsSuccessStatusCode)
{
// the request was successful, parse the returned string as xml and return the XElement
var xml = await response.Content.ReadAsAsync<XElement>();
return xml;
}
// the request was not successful -> return null
else
{
return null;
}
}
// some exception occured -> return null
catch (Exception)
{
return null;
}
}
}
If i have it like this, the first and the second call to HttpGetAsync work perfectly, but from the 3rd on the GetAsyncstalls and eventually throws an exception due to the timeout. I send these calls consecutively, there are not 2 of them running simultaneously since the results of the previous call are needed to decide the next call.
I tried using the app Packet Capture to look at the requests and responses to find out if i'm sending an incorrect request. But it looks like the request which fails in the end is never even sent.
Through experimentation i found out that everything works fine if don't set the AutomaticDecompression.
It also works fine if i change the HttpGetAsync method to this:
protected async Task<XElement> HttpGetAsync(string method)
{
try
{
// send the request
var response = await m_HttpClient.GetStringAsync(method);
if (string.IsNullOrEmpty(response))
{
return null;
}
var xml = XElement.Parse(response);
return xml;
}
// some exception occured -> return null
catch (Exception)
{
return null;
}
}
So basically using i'm m_HttpClient.GetStringAsync instead of m_HttpClient.GetAsync and then change the fluff around it to work with the different return type. If i do it like this, everything works without any problems.
Does anyone have an idea why GetAsync doesn't work properly (doesn't seem to send the 3rd request) with AutomaticDecompression, where as GetStringAsync works flawlessly?
There are bug reports about this exact issue:
https://bugzilla.xamarin.com/show_bug.cgi?id=21477
The bug is marked as RESOLVED FIXED and the recomended action is to update to the latest stable build. But there are other (newer) bugreports that indicate the same thing that are still open, ex:
https://bugzilla.xamarin.com/show_bug.cgi?id=34747
I made a workaround by implementing my own HttpHandler like so:
public class DecompressionHttpClientHandler : HttpClientHandler
{
protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip"));
var msg = await base.SendAsync(request, cancellationToken);
if (msg.Content.Headers.ContentEncoding.Contains("gzip"))
{
var compressedStream = await msg.Content.ReadAsStreamAsync();
var uncompresedStream = new System.IO.Compression.GZipStream(compressedStream, System.IO.Compression.CompressionMode.Decompress);
msg.Content = new StreamContent(uncompresedStream);
}
return msg;
}
}
Note that the code above is just an example and not a final solution. For example the request will not be compressed and all headers will be striped from the result. But you get the idea.

Categories

Resources