xamarin unfortunately app has stopped menu item selected - asynchronous

I have a problem, when user select the save option on the menu app stops with message 'unfortunately app has stopped'
my code is like this
private async void Save()
{
try
{
OpenWaitingDialog();
await ...//Save to sqlite methods
await ...//Post data to web service
}
catch(Exception e)
{
Log(e);
}
CloseWaitingDialog();
}
I have no idea why app is stopping even when I use try and catch, I use WebRequest to post data to my service
public static async Task<string> JsonPostAsync(string url, string jsonContent)
{
string result = string.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
Byte[] byteArray = encoding.GetBytes(jsonContent);
request.ContentLength = byteArray.Length;
request.ContentType = #"application/json";
request.Timeout = 600000;
//More code
I'm using hockey app to catch all error's but this case in particular is not catching anything
The app save the data to sqlite and post the data to my service, the weird thing is that is not showing the progress dialog when it crash and happends 1/50 times

Related

Aspose.Words Returning PDF as Stream does nothing (ASP.NET Web API)

We are exploring using Aspose.Words for some conversions in an on premise API.
This works perfectly for Excel sheets using Aspose.Cells.
[HttpPost]
[Route("convert/excel")]
public async Task<IActionResult> ConvertExcel(IFormFile fileToConvert)
{
var fileStream = new MemoryStream();
fileToConvert.CopyTo(fileStream);
var convertedFile = await pdfConverter.ConvertExcelAsync(fileStream);
return File(convertedFile, "application/octet-stream");
}
However when using exactly the same method for Aspose.Words it does nothing, literally nothing just continues for a few minutes and then times out. This is not a timeout issue with the conversion as the file is 200KB.
[HttpPost]
[Route("convert/word")]
public async Task<IActionResult> ConvertWord(IFormFile fileToConvert)
{
var fileStream = new MemoryStream();
fileToConvert.CopyTo(fileStream);
var convertedFile = await pdfConverter.ConvertWordAsync(fileStream);
return File(convertedFile, "application/octet-stream");
}
I have tried various forms of returning a file but no luck.
return new FileStreamResult(convertedFile, "application/pdf");
The actual conversion methods look like this.
public Task<Stream> ConvertWordAsync(Stream fileStream)
{
return Task.Factory.StartNew(() => ConvertWord(fileStream));
}
private Stream ConvertWord(Stream inputFile)
{
var doc = new Document(inputFile);
var outputFile = new MemoryStream();
doc.Save(outputFile, Aspose.Words.SaveFormat.Pdf);
//doc.Save(#"C:\ProgramData\foo.pdf", Aspose.Words.SaveFormat.Pdf); //THIS WORKS BUT NOT APPOPRIATE
return outputFile;
}
I have also updated it to support HttpGet and hard-coded a path to a file and in browser just get a Download failed - network error.
Is is possible that the Save method returns the memory stream at the end of the stream.
You should try the following immediately after the call to doc.Save
outputFile.Seek(0, SeekOrigin.Begin);

HTTP Handler async issue .NET 4.5.2

I am using .NET 4.5.2 for a web application and I have a HTTP handler that returns a processed image. I am making async calls to the process handler using jQuery and i have started getting the following error:
An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%# Page Async="true" %>. This exception may also indicate an attempt to call an "async void" method, which is generally unsupported within ASP.NET request processing. Instead, the asynchronous method should return a Task, and the caller should await it.
This is the handler code:
public void ProcessRequest(HttpContext context)
{
string CaseID = context.Request.QueryString["CaseID"].ToString();
int RotationAngle = Convert.ToInt16(context.Request.QueryString["RotationAngle"].ToString());
string ImagePath = context.Request.QueryString["ImagePath"].ToString();
applyAngle = RotationAngle;
string ImageServer = ConfigurationManager.AppSettings["ImageServerURL"].ToString();
string FullImagePath = string.Format("{0}{1}", ImageServer, ImagePath);
WebClient wc = new WebClient();
wc.DownloadDataCompleted += wc_DownloadDataCompleted;
wc.DownloadDataAsync(new Uri(FullImagePath));
}
private void wc_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
Stream BitmapStream = new MemoryStream(e.Result);
Bitmap b = new Bitmap(BitmapStream);
ImageFormat ImageFormat = b.RawFormat;
b = RotateImage(b, applyAngle, true);
using (MemoryStream ms = new MemoryStream())
{
if (ImageFormat.Equals(ImageFormat.Png))
{
HttpContext.Current.Response.ContentType = "image/png";
b.Save(ms, ImageFormat.Png);
}
if (ImageFormat.Equals(ImageFormat.Jpeg))
{
HttpContext.Current.Response.ContentType = "image/jpg";
b.Save(ms, ImageFormat.Jpeg);
}
ms.WriteTo(HttpContext.Current.Response.OutputStream);
}
}
Any idea what this means and I could do to overcome this?
Thanks in advance.
You code won't work as it is, because you create a WebClient inside the ProcessRequest method but don't wait for it to finish. As a result, the client will be orphaned as soon as the method finishes. By the time a response arrives, the request itself has finished. There is no context or output stream to which you can write the response.
To create an asynchronous HTTP Handler you need to derive from the HttpTaskAsyncHandler class and implement the ProcessRequestAsync method:
public class MyImageAsyncHandler : HttpTaskAsyncHandler
{
public override async Task ProcessRequestAsync(HttpContext context)
{
//...
using(WebClient wc = new WebClient())
{
var data=await wc.DownloadDataTaskAsync(new Uri(FullImagePath));
using(var BitmapStream = new MemoryStream(data))
{
//...
ms.WriteTo(context.Response.OutputStream);
//...
}
}
}
}

Web API Async Upload with XmlHttpRequest to get progress

I'm trying to drag and drop file upload with a progress bar.
I have a div which is listening to files being dropped on which is working perfectly.
I'm then..
//Setting up a XmlHttpRequest
xhr = new XMLHttpRequest();
//Open connection
xhr.open("post", "api/ImageUpload", true);
// Set appropriate headers
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("X-File-Type", uf.type);
xhr.setRequestHeader("X-File-Name", uf.name);
xhr.setRequestHeader("X-File-Size", uf.size);
This sends fine, with the stream as the body of the request to the Web API (not async).
[System.Web.Mvc.HttpPost]
public string Post()
{
Stream stream = HttpContext.Current.Request.InputStream;
String filename = HttpContext.Current.Request.Headers["X-File-Name"];
FileModel file = uploadService.UploadFile(stream, filename);
return file.Id.ToString();
}
I'm trying to chance the request to "public async Task< string> Post(){ }
If the method was using a multipart form on the page instead of XmlHttpRequest I would have used "await Request.Content.ReadAsMultipartAsync(provider)" but this doesn't seem to be populated at the time I need it.
So what is the correct was to handle and an Async call from XmlHttpRequest on a Web API in order to record progress during the request with XHR's progress event?
I have looked at a great deal of pages so far to find a solution but this is the page I have used primarily.
http://robertnyman.com/html5/fileapi-upload/fileapi-upload.html
Thanks for any help
Oliver
It looks like someone else had the same question with you and got an answer yet. please have a look at ASP.NET MVC 4 Web Api ajax file upload.
And here is an example from microsoft http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2.
I combined the two above solution together and worked for me (just adjust a little bit)
one line change in Javascritp
xhr.open("post", "api/upload", true);
Save the file using stream
public class UploadController : ApiController
{
public async Task<HttpResponseMessage> PostFormData()
{
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var fileName = Path.Combine(root, Request.Headers.GetValues("X-File-Name").First());
try
{
var writer = new StreamWriter(fileName);
await Request.Content.CopyToAsync(writer.BaseStream);
writer.Close();
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
}

Passing an image from a Wep Api service to another Web Api service

For security reasons, I am building two Web Api services. The first Web Api app will have access to an image generating service, and will act as a security proxy. The second Web Api app will call the first app from the internet and retrieve the image.
However, I can't seem to get to negotiate passage of the image correctly. My thought was to have the security proxy Web API to get the image, and then pass it as a byte array my other service which would allow a user to download the image. However, when my browser attempts to open the image, it is always corrupted.
Here is the security proxy getting the image, which I know is successful:
public byte[] Get(string invoice, string Customer)
{
object image;
try
{
image = _repo.GetImage(invoice, Customer);
}
catch (ApplicationException exc)
{
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(string.Format("No Image with Invoice Number = {0}", invoice.ToString())),
ReasonPhrase = "Image Not Found"
};
throw new HttpResponseException(resp);
}
catch (Exception exc)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
return (byte[])image;
}
This returns an array with a length of 40133.
The calling Web API service looks like this:
public HttpResponseMessage Get(string invoice, string Customer)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));
byte[] img = client.GetByteArrayAsync("http://localhost:1363/api/Image/" + invoice + "/" + Customer).Result;
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new ByteArrayContent(img);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/tiff");
var disposition = new ContentDispositionHeaderValue("attachment");
disposition.FileName = "ImageDocument.tif";
response.Content.Headers.ContentDisposition = disposition;
return response;
}
However, the length of the img byte array is 53514.
When the browser tries to open the image, it tells me it is corrupt. If I open the TIFF in notepad, I get :
"SUkqAAgAAAASAP4ABAABAAAAAAAAAAABBAABAAAAsAYAAAEBBAABAAAAvgQAAAIBAwABAAAAAQAAAAMBAwABAAAABAAAAAYBAwABAAAAAAAAAAcBAwABAAAAAQAAABEBBAABAAAAAAMAABIBAwABAAAAAQAAABUBAwABAAAAAQAAABYBBAABAAAAvgQAABcBBAABAAAAxZkAABoBBQABAAAA+AIAABsBBQABAAAA8AIAACgBAwABAAAAAgAAADEBAgA4AAAAuAIAADIBAgAUAAAApAIAAOiAAwABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADIwMTI6MTA6MDMgMDc6Mjc6MTkAS29mYXggc3RhbmRhcmQgTXVsdGktUGFnZSBUSUZGIFN0b3JhZ2UgRmlsdGVyIHYzLjAzLjAwMADIAAAAAQAAAMgAAAABAAAALcMjAGC+cBQRwhOKcIuzqZDzrHoxF8k+VAOR2cAgjhC5lQEI+VYoiIiIiIiIiJXLAazgaZvMBqEcNI0BoJwaTMGsjgqGA1yOGaUA0Hg0igC5qZ6I1GSsNMuGqeBrI+bBoNYQrfNIiMREWdl4zVWRERkQzVECBpNcRyMCz6PhQgZwQGQLjpCIWwgxERGLLYAx//zLWLx4IeDnBnxSGFMRgIeZ4zcaR+KuPM4KeZ6MBTqKcj8YjAQ4IejDoQ4eE07WGnra3p9w07Xhw1s7NHu+0/v+/SQf6/9+cjwp0Z0Z8KeCm4p4IGQwhoz4cwCFBZN8u8s5duXeXTLva7pN6J56l45sf8u8u
SNIP*
Anyone know what I am doing wrong?
Thanks!
Chris
Solved
If anyone is interested in the calling code that leverages the solution identified, here it is:
public HttpResponseMessage Get(string invoice, string Customer)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("image/tiff"));
byte[] img = client.GetByteArrayAsync("http://localhost:1363/api/Image/" + invoice + "/" + Customer).Result;
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new ByteArrayContent(img);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/tiff");
var disposition = new ContentDispositionHeaderValue("attachment");
disposition.FileName = "ImageDocument.tif";
response.Content.Headers.ContentDisposition = disposition;
return response;
}
With your above current return type (byte[]) of action, formatters of web api are probably handling them and hence you are seeing unexpected response.
can you try sending the image as a ByteArrayContent instead?(you need to have HttpResponseMessage as a return type here)
Example:
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new ByteArrayContent(..your byte array here...);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
return response;

MailChimp oauth2 in ASP.NET keeps returning invalid_grant

I am developing a new app that offers integration with MailChimp. Basically, it enables users to easily export their customer contact info directly to a MailChimp account (that is, to a specific mailing-list inside MailChimp).
All that works, and are somewhat irrelevant to my question.
For not asking the user to enter MailChimp-credentials every time, I'm about to implement the oauth2 authorization workflow as described here: http://apidocs.mailchimp.com/oauth2/
It works just fine in step 1-3, but step 4 is killing me.
It's my first time working with oauth, but I seem to understand the basics.
Here is my problem:
When I do the POST call to the https://login.mailchimp.com/oauth2/token -URI, to get the final access-token, I keep getting the error in JSON result: "invalid_grant"
I have checked the request and response streams, that my url is compiled correctly.
Here is my code in the controller:
(GrantEcoAccess is just to grant access to another app - the rest should be self-explaining)
public class HomeController : ApplicationController
{
private readonly string authorize_uri = "https://login.mailchimp.com/oauth2/authorize";
private readonly string access_token_uri = "https://login.mailchimp.com/oauth2/token";
private readonly string mailchimp_clientid2 = "xxx";
private readonly string mailchimp_secret2 = "yyy";
...
public ActionResult GrantEcoAccess()
{
//if exist: use saved token
var user = (Mailchimp_users)Session["user"];
if (!string.IsNullOrWhiteSpace(user.EcoToken))
return RedirectToAction("GrantMailChimpAccess");
// if !
var url = "https://secure.e-conomic.com/secure/api1/requestaccess.aspx?role=superuser&appId=MailChimp&redirectUrl=http://localhost:18017/Home/IncomingToken";
Redirect(url).ExecuteResult(ControllerContext);
return null;
}
public ActionResult IncomingToken(string token)
{
var user = (Mailchimp_users)Session["user"];
user.EcoToken = token;
EcoSession.DataSession.Refresh(System.Data.Objects.RefreshMode.ClientWins, user);
EcoSession.DataSession.SaveChanges();
return RedirectToAction("GrantMailChimpAccess");
}
public ActionResult GrantMailChimpAccess()
{
//if exist: use saved token
var user = (Mailchimp_users)Session["user"];
if (!string.IsNullOrWhiteSpace(user.MailChimpToken))
return RedirectToAction("Index", "Subscribe");
//if !
var url = string.Format("{0}?response_type=code&client_id={1}&redirect_uri=", authorize_uri, mailchimp_clientid2, "http://127.0.0.1:18017/Home/IncomingMailChimpToken");
Redirect(url).ExecuteResult(ControllerContext);
return null;
}
public ActionResult IncomingMailChimpToken(string code)
{
var url = "https://login.mailchimp.com/oauth2/token?grant_type=authorization_code&client_id=XX&client_secret=XX&code=" + code + "&redirect_uri=http://127.0.0.1:18017/Home/AuthComplete";
//var url = string.Format("?grant_type=authorization_code&client_id={0}&client_secret={1}&code={2}&redirect_uri={3}", mailchimp_clientid, mailchimp_secret, code, Url.Action("AuthComplete"));
Response.Clear();
StringBuilder sb = new StringBuilder();
sb.Append("<html>");
sb.AppendFormat(#"<body onload='document.forms[""form""].submit()'>");
sb.AppendFormat("<form name='form' action='{0}' method='post'>", access_token_uri);
sb.Append("<input type='hidden' name='grant_type' value='authorization_code'>");
sb.AppendFormat("<input type='hidden' name='client_id' value='{0}'>", mailchimp_clientid2);
sb.AppendFormat("<input type='hidden' name='client_secret' value='{0}'>", mailchimp_secret2);
sb.AppendFormat("<input type='hidden' name='code' value='{0}'>", code);
sb.AppendFormat("<input type='hidden' name='redirect_uri' value='{0}'>", "http://127.0.0.1:18017/Home/AuthComplete");
// Other params go here
sb.Append("</form>");
sb.Append("</body>");
sb.Append("</html>");
Response.Write(sb.ToString());
Response.End();
return null;
}
public ActionResult AuthComplete(string access_token, string expires_in, string scope)
{
if (string.IsNullOrWhiteSpace(access_token))
throw new Exception("Could not authorize user with MailChimp");
var user = (Mailchimp_users)Session["user"];
user.MailChimpToken = access_token;
EcoSession.DataSession.Refresh(System.Data.Objects.RefreshMode.ClientWins, user);
EcoSession.DataSession.SaveChanges();
return RedirectToAction("Index", "Subscribe");
}
}
It is step 4 that is killing me, not step 5.
You should send your request parameters in the body using post, if you were using curl php you would do this:
$value = http_build_query($params); //params is an array
curl_setopt($ch, CURLOPT_POSTFIELDS, $value);
Value should look like this:
grant_type=authorization_code&client_id=635959587059&client_secret=0da3e7744949e1406b7b250051ee1a95&code=1edf2589e664fd317f6a7ff5f97b42f7&redirect_uri=http%3A%2F%2F192.168.1.8%2Foauth%2Fcomplete.php
Notice that you should create a body request in the form a query string, don't send json, they wont't find your params.
If you get an invalid grant response or something after doing this, check that the redirect uri you used to get the first code is EXACTLY the same as the one you are sending to get the token.
Also, to the ones using PHP, to match what the mailchimp documentation states use this:
curl_setopt($ch, CURLOPT_USERAGENT, 'oauth2-draft-v10');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
If you don't want to implement it yourself, there is this nice wrapper, which handles oAuth2 AND RESTapi-calls to MailChimp.
https://github.com/jamierytlewski/eepOAuth2-MVC
Step 4 is "Your application must make an out-of-band request to the access_token_uri using the code"
The main point here is "out of band".
You have to build and send a post request server-side.
The client should not have your mailchimp_secret
Your IncomingMailChimpToken could look like this :
public ActionResult IncomingMailChimpToken(string code)
{
string mcPostData = String.Format(
"grant_type={0}&client_id={1}&client_secret={2}&code={3}&redirect_url={4}",
System.Web.HttpUtility.UrlEncode("authorization_code"),
System.Web.HttpUtility.UrlEncode(mailchimp_clientid2),
System.Web.HttpUtility.UrlEncode(mailchimp_secret2),
System.Web.HttpUtility.UrlEncode(code),
System.Web.HttpUtility.UrlEncode("http://127.0.0.1:18017/Home/AuthComplete")
);
WebRequest request = WebRequest.Create(access_token_uri);
// Set the Method property of the request to POST.
request.Method = "POST";
request.ContentType = "application/json";
byte[] byteArray = Encoding.UTF8.GetBytes(mcPostData);
request.ContentLength = byteArray.Length;
// Get the request stream.
Stream dataStream = request.GetRequestStream();
// Write the data to the request stream.
dataStream.Write(byteArray, 0, byteArray.Length);
// Close the Stream object.
dataStream.Close();
// Get the response.
WebResponse response = request.GetResponse();
// Get the stream containing content returned by the server.
dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
// Cleanup the streams and the response.
reader.Close ();
dataStream.Close ();
response.Close ();
// parse the json responseFromServer to extract token, expires_in and scope
// and call AuthComplete with these params
}

Resources