MVC 3 ContentResult binary file with firefox strange issue - caching

I've got an MVC 3 app that allows users to upload files with some data entry stuff. I've set up a controller that fetches those documents and buffers them out to the user like so
[OutputCache(Duration = 1200, VaryByParam = "id")]
public ContentResult GetNarrative(int id)
{
Response.Clear();
Response.BufferOutput = true;
Response.ContentType = "application/octet-stream";
var narrative = attachRepo.GetNarrative(id);
if (narrative == null || narrative.Narrative == null)
return null;
Response.AddHeader("Content-Disposition",
string.Format("attachment;filename={0}",
Server.UrlEncode(narrative.Filename)));
Response.OutputStream.Write(narrative.Narrative.ToArray(),
0, narrative.Narrative.ToArray().Length);
Response.OutputStream.Flush();
return Content("");
}
This works fine and well, the interesting thing is that when I have the output cache line, my firefox download dialog looks like this
However when I comment out the output cache line it looks like the expected dialog
This isn't really a blocking issue, as it works just fine in IE and Chrome just downloads by default, but I am curious why this would be happening and if anyone has experienced this and worked around it.
Thanks!

I've found that firefox ignores the filename in the Attachment. It tends to use he URL to set the filename.
Try putting at the end of the URL ?filename.docx
And see if it fixes things.

Related

Cannot make XBAP cookies work

I am trying to make a XBAP application communicating with a webservice with login.
But I want the user to skip the login step if they already logged in the last seven days.
I got it to work using html/aspx.
But it fails continuously with XBAP.
While debugging, the application is given full trust.
This is the code I have so far to write the cookie:
protected static void WriteToCookie(
string pName,
Dictionary<string, string> pData,
int pExiresInDays)
{
// Set the cookie value.
string data = "";
foreach (string key in pData.Keys)
{
data += String.Format("{0}={1};", key, pData[key]);
}
string expires = "expires=" + DateTime.Now.AddDays(pExiresInDays).ToUniversalTime().ToString("r");
data += expires;
try
{
Application.SetCookie(new Uri(pName), data);
}
catch (Exception ex)
{
}
}
And this is what I have to read the cookie:
protected static Dictionary<string, string> ReadFromCookie(
string pName)
{
Dictionary<string, string> data = new Dictionary<string, string>();
try
{
string myCookie = Application.GetCookie(new Uri(pName));
// Returns the cookie information.
if (String.IsNullOrEmpty(myCookie) == false)
{
string[] splitted = myCookie.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
string[] sub;
foreach(string split in splitted)
{
sub = split.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
if (sub[0] == "expires")
{
continue;
}
data.Add(sub[0], sub[1]);
}
}
}
catch(Exception ex)
{
}
return data;
}
The pName is set with:
string uri = "http://MyWebSiteName.com";
When the user authenticate the first time, I call the WriteToCookie function and set it with 7 days to expire.
It looks like everything is fine as I get no exception of error messages. (I have a break point in the catch)
After that, I close the session and start it again.
The first thing I do is a ReadFromCookie.
Then I get an exception with the following message: No more data is available
So my application is sending the user automatically back to the login screen.
I also tried to do a ReadFromCookie right after the WriteToCookie in the same session, and I get the same error.
Application.SetCookie(new Uri("http://MyWebSiteName.com/WpfBrowserApplication1.xbap"), "Hellllo");
string myCookie2 = Application.GetCookie(new Uri("http://MyWebSiteName.com/WpfBrowserApplication1.xbap"));
It seems to me that the cookie is not even written in the first place.
So I am guessing I am doing something wrong.
Maybe the uri I am using is wrong. Is there a specific format needed for it?
Just like you need a very specific format for the expire date.
I have been searching quite a lot of internet for a good sample/tutorial about using cookies with XBAP, and I could not find anything really well documented or tested.
A lot of people say that it works, but no real sample to try.
A lot of people also handle the authentication in html, then go to the XBAP after successfully reading/writing the cookies.
I would prefer a full XBAP solution if possible.
To answer some questions before they are asked, here are the project settings:
Debug:
Command line arguments: -debug -debugSecurityZoneURL http://MyWebSiteName.com "C:\Work\MyWebSiteName\MyWebSiteNameXBAP\bin\Debug\MyWebSiteNameXBAP.xbap"
Security:
Enable ClickOnce security settings (Checked)
This is a full trust application (selected)
I also created a certificate, and added it the 3 stores like explained in "publisher cannot be verified" message displayed
So I do not have the warning popup anymore. I just wanted to make sure that it was not a permission issue.
Finally found the answer to this problem.
Thanks for this CodeProject I was finally able to write/read cookies from the XBAP code.
As I had guessed, the URI needs to be very specific and you cannot pass everything you want in it.
What did the trick was using: BrowserInteropHelper.Source
In the end the read/write code looks like:
Application.SetCookie(BrowserInteropHelper.Source, data);
string myCookie = Application.GetCookie(BrowserInteropHelper.Source);
It looks like you cannot use ';' to separate your own data.
If you do so, you will only get the first entry in your data.
Use a different separator (ex: ':') and then you can get everything back
The data look like this:
n=something:k=somethingElse;expires=Tue, 12 May 2015 14:18:56 GMT ;
The only thing I do not get back from Application.GetCookie is the expire date.
Not sure if it is normal or not. Maybe it is flushed out automatically for some reason. If someone knows why, I would appreciate a comment to enlighten me.
At least now I can read/write data to the cookie in XBAP. Yeah!

GWT FormPanel method replaced by input parameter

We've discovered a strange new bug in a GWT application I'm maintaining, and I'm not sure when it became an issue. Possibly with a new Firefox version.
We're sending a POST request to the server using a FormPanel, essentially like many examples I've seen online. But since we actually want a PUT request, one of the hidden input parameters is named "method" and has a value of "put".
Now, when I look at the request in Fiddler coming from Firefox, it is being transformed into a GET request with all the parameters in the QueryString. In IE and Chrome, the parameters are in the body of a POST request.
I've displayed the value of FormPanel.getMethod() in an alert, and in IE and Chrome the string "post" is displayed, whereas in firefox it is showing "object HTMLInputElement". Unfortunately, hosted mode debugging does not work with this project.
It obviously looks like the FormPanel's getMethod() function is returning the hidden input parameter named method instead of the actual form's method in Firefox.
Technically I should avoid changing the servlet as this is from an OpenSource project that we use, though I've found I can fix the issue by changing the hidden input parameter's name to "_method" on both ends.
Has anyone ever seen anything like this? I can't find anything in Google.
UPDATE: We're using GWT 2.3 in case that helps
Some insight can be found here Are the PUT, DELETE, HEAD, etc methods available in most web browsers?
I would also suggest using XMLHttpRequest. In this case you [most probably] don't have to change anything on the server side.
In case if you use Submit button, you can write in its clickHandler function:
submitMyForm(yourTextBox.getText(), self);
// self - is the instance of main class (named UploadForm here), needs to be passed here for future reference
and then some more (you can adapt this for your needs):
private native void submitMyForm(String text, UploadForm handler)/*-{
var fd = new FormData();
fd.append("textValue", text);
var xhr = new XMLHttpRequest();
var upload = xhr.upload;
readyStateChangeHandler = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
var serverResponse = eval(xhr.responseText); // optional
handler.#com.project.UploadForm::onUploadIsDone(Lcom/google/gwt/core/client/JavaScriptObject;)(serverResponse);
} else {
handler.#com.project.UploadForm::onUploadFailed(I)(status);
}
}
};
xhr.onreadystatechange = readyStateChangeHandler;
xhr.open("PUT", yourActionUrlHere);
xhr.send(formData);
}-*/;

Clicking Open Word Document tries to reconnect to Controller action while downloading

I have a requirement to download a file from the server, but open inline if possible. I'm currently doing;
Response.AddHeader("Content-Disposition", string.Format("inline; filename={0}", documentFileName));
result = new FileStreamResult(new FileStream(documentFilePath, FileMode.Open), "application/msword");
I've put application/msword in there right now, because that's what I'm having a problem with. When I click Open on the word document, it's as if the document makes multiple calls back to the action, but there is no session and no database so it crashes. When the user is running this, they see a long hang, the "Downloading" dialog finally appears in word and they have to cancel it. The document is there and is valid but this is not desirable.
Pdfs, pngs etc. download fine. Can anybody explain this behavior, and give me some hints as to how I fix it?
Update:
The action basically looks like;
[HttpPost]
public FileResult View(int id, int source)
{
var document = GetDocumentFromDatabase(id, source);
documentFilePath = Path.Combine( documentsDirectory, document.Name);
documentName = document.Name;
Response.AddHeader("Content-Disposition", string.Format("inline; filename={0}", documentFileName));
result = new FileStreamResult(new FileStream(documentFilePath, FileMode.Open), "application/msword");
return result;
}
I've trimmed it down, as I can't share the specifics, but the full idea is there.
Answer:
I have a lookup of available content-types, in there I have defined whether the file is inline or attachment, and when I detect a word document, I set it to attachment. No more error. PDF opens in the browser still because I set it to inline.
I use:
public ActionResult GetAttachment(int id)
{
var attachment = _repository.GetAttachByID(id);
if (attachment != null)
{
Response.AppendHeader("Content-Disposition",string.Format("inline; filename={0}",attachment.FileName));
return File(attachment.File, attachment.MimeType, attachment.FileName);
}
else
{
return null;
}
}
Regards

BackgroundTransferRequest WP7

I am using the Background Transfer to upload Photographs to my Web Service. As the Photograph uploads can consume significant time and memory, I thought it might be a nice idea to use the background transfer request to accomplish this. After the photo is uploaded, I want to obtain the Id of the uploaded photo and then use it for post-processing. However, it turns out I can't do that in a background transfer request.
Per my understanding, Background Transfer works using the following logic ONLY:
You have to obtain the file you want to upload and then save/copy it to your app's Isolated Storage under the folder: shared/transfers. This is extremely important. Apparently, using file in a different location didn't work for me. Maybe it isn't the shared/transfers as much as it is a 'relative' path. But I would stick to the same conventions.
After you have saved the file in that location, your background request can be created based on that. It doesn't look like you can pass POST CONTENT other than the file contents, so any other parameters like file name, mime type etc. will need to be passed as QUERY String parameters only. I can understand this, but it would've been nice if I could pass both as POST Content. I don't think HTTP has a limitation on how this works.
Here is some code for creating a request using Hammock:
string url = App.ZineServiceAuthority + "articles/save-blob?ContainerName={0}&MimeType={1}&ZineId={2}&Notes={3}&IsPrivate={4}&FileName={5}";
url = String.Format(url, userId, "image/jpg", ZineId, txtStatus.Text, true, UploadFileName);
var btr = new BackgroundTransferRequest(new Uri(url, UriKind.Absolute));
btr.TransferPreferences = TransferPreferences.AllowCellularAndBattery;
btr.Method = "POST";
btr.Headers.Add("token", IsolatedStorageHelper.GetTravzineToken());
btr.UploadLocation = new Uri(#"/shared\transfers/" + UploadFileName, UriKind.Relative);
btr.TransferStatusChanged += new EventHandler<BackgroundTransferEventArgs>(btr_TransferStatusChanged);
btr.TransferProgressChanged += new EventHandler<BackgroundTransferEventArgs>(btr_TransferProgressChanged);
BackgroundTransferService.Add(btr);
In my case, I am literally passing all the necessary parameters using the query string. On a successful save, my Web Service returns back the Id of the Photo I just uploaded. However:
There is NO way (or at least I know of) to obtain and evaluate the RESPONSE. The Background Transfer Request Event handlers do not expose a RESPONSE.
Here are my event handlers:
void btr_TransferProgressChanged(object sender, BackgroundTransferEventArgs e)
{
bool isUploading = e.Request.TotalBytesToSend > 0 ? true : false;
lblStatus.Text = isUploading ? "Uploading" + e.Request.BytesSent.ToString() + " sent" : "Done";
}
void btr_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
{
if (e.Request.TransferStatus == TransferStatus.Completed)
{
using (IsolatedStorageFile iso =
IsolatedStorageFile.GetUserStoreForApplication())
{
if (iso.FileExists(e.Request.UploadLocation.OriginalString))
iso.DeleteFile(e.Request.UploadLocation.OriginalString);
}
BackgroundTransferService.Remove(e.Request);
if (null != e.Request.TransferError)
{
MessageBox.Show(e.Request.TransferError.Message);
}
else
{
lblStatus.Text = "Done baby done";
}
}
}
So now my question is, how does anyone do any sort of POST Processing in such scenarios?
Can anyone please tell me the line of thought behind designing such an inflexible class?
Any thoughts on how I could get around this issue would be appreciated.
Also, does anyone have any working examples of a homegrown BackgroundTransfer?
Haven't tried it but why not set a download location like this:
btr.DownloadLocation = "myDownloadFile.html";
btr.UploadLocation = "myUploadFile.jpg";
...
If the request is completed read the file "myDownloadFile.html" where your response has been stored and delete it afterwards.

Sitecore Extranet login and PDF converter or Webclient

I'm having a problem using Winnovatives PDFConverter on pages that are protected by Extranet security (which is based on ASP.Net Membership).
I've tried several different approaches, but the following I can get to work on my local machine, but not anywhere else.
Code for login page, this code should bypass the login process for:
// check that the current "user" isn't logged in and is the Winnovative UserAgent
if (!Sitecore.Context.IsLoggedIn && Request.UserAgent.Contains(".NET CLR"))
{
//Login with a dummy user I've created
Sitecore.Security.Authentication.AuthenticationManager.Login("extranet\\pdf", "pdf", true);
//redirect to former page
}
The page that generates the PDF uses this code:
private void PDFPrint(string url)
{
PdfConverter pdfConverter = new PdfConverter();
pdfConverter.LicenseKey = "our license";
url = Request.Url.Scheme + "://" + Request.Url.Host + url;
byte[] downloadBytes = pdfConverter.GetPdfFromUrlBytes(url);
HttpResponse response = HttpContext.Current.Response;
response.Clear();
response.AddHeader("Content-Type", "binary/octet-stream");
response.AddHeader("Content-Disposition", "attachment; filename=" + Sitecore.Context.Item.Name + ".pdf" + "; size=" + downloadBytes.Length.ToString());
response.Flush();
response.BinaryWrite(downloadBytes);
response.Flush();
response.End();
}
The Exception I'm getting is this:
"Could not get the metafile from url. Could not get image from url.The URL is not accessible.."
I've also tried this trick from the Winnovative FAQ to no avail:
http://www.winnovative-software.com/FAQ.aspx#authenticationQ
I've also tried to use WebClient or HttpWebRequest to retrieve the content.
But nothing I do seems to work other than locally.
Basically I want to create a way of either getting Winnovatives converter to use the current logged in user, my custom "pdf" user og some other way of getting the html from the response.
I hope this question isn't too vague, but I find it kinda hard to ask. But basically I want to get some html content from a page on a Sitecore solution I control, which is protected by Sitecore normal Extranet security. This html content should be in string or byte[] format.
Help me Stackoverflowers, you're my only hope! :P
I contacted Sitecore to ask if they had a solution.
Their solution was to create a Processor that would set an active user based on some criteria.
This is the code I made for my site (it's probably not the best solution as UserAgent can be spoofed):
public class MyResolver : HttpRequestProcessor
{
// Methods
public override void Process(HttpRequestArgs args)
{
var userAgent = args.Context.Request.UserAgent ?? "";
SiteContext site = Sitecore.Context.Site;
if (site.Name == "site_name_in_webconfig" && userAgent.Contains("this_should_only_be_in_thepdfcreators_userAgent"))
{
Sitecore.Security.Accounts.User pdfuser = Sitecore.Security.Accounts.User.FromName("extranet\\theUser", true);
AuthenticationManager.SetActiveUser(pdfuser);
}
}
}
and then add the following to the web.config, before the UserResolver:
<processor type="Namespace.MyResolver, Assembly" />
I hope this will help some others out there.
I've found a similar issue on the ASP.NET forums and the answer to that was to use a newer version of the PDF tool: SessionState Problems ?

Resources