MVC - Excel download on button click Cannot redirect after HTTP headers have been sent - asp.net-mvc

Firstly, I know there are multiple posts related to this error but nothing is helping so far.
I am getting an error "Cannot redirect after HTTP headers have been sent." in my MVC razor application when I make a call to SomeController.
How do I fix this?
[HttpPost]
public ActionResult SomeController(Object abc)
{
Helper.somemethod("","excel");
return View(abc);//I tried this
return RedirectToAction("SomeController"); //I tried this also
}
public static void somemethod(string settocken, string filenames, List<Sample> samples)
{
//Extra logic for excel that uses List<Sample> to generate excel.
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.Cookies.Add(new HttpCookie("downloadToken", settocken));
HttpContext.Current.Response.ContentType = "Application/x-msexcel";
HttpContext.Current.Response.AddHeader("Content-Disposition", String.Format("attachment; filename={0}.xlsx", filenames));
using (MemoryStream ms = new MemoryStream())
{
try
{
book.Save(ms);
HttpContext.Current.Response.BinaryWrite(ms.ToArray());
}
catch
{
}
finally
{
}
HttpContext.Current.Response.End();
}
}

#CoolArchTek http communication loop will be ended after HttpContext.Current.Response.End() call, so you can't add anything after that, however as far as I know there is no possibility in http to do what you want, so I would recommend you render your view with javascript which will initiate file download by client's browser

I would suggest, have an iframe inside view where you want to redirect to and show the data. And set the excel file path(or the action which returns excel file) as source for that iframe.
With this, as soon as the page loads, download begins.
Hope it helps.

Related

Kendo UI Async Upload not working in Internet Explorer

I'm trying to use the Kendo UI Upload (MVC wrapper) in async mode. Things seem to work fine in Chrome, but no such luck in IE (as of now only tested in IE 9). When it initiates the upload, I can see it hitting my action method and the request contains the data I expect, but nothing is actually being saved.
Code samples are below:
_EditForm.cshtml (where the upload is)
#(Html.Kendo().Upload()
.Name(string.Format("upload{0}", "background"))
.Multiple(true)
.Events(evt => evt.Success("refreshBackgroundImages"))
.Messages(msg => msg.DropFilesHere("drag and drop images from your computer here")
.StatusUploaded("Files have been uploaded"))
.Async(a => a.AutoUpload(true)
.SaveField("files")
.Save("UploadImage", "Packages", new { siteId = Model.WebsiteId, type = "background" })))
Controller ActionMethod
[HttpPost]
public ActionResult UploadImage(IEnumerable<HttpPostedFileBase> files, Guid siteId, string type)
{
var site = _websiteService.GetWebsite(siteId);
var path = Path.Combine(_fileSystem.OutletVirtualPath, site.Outlet.AssetBaseFolder);
if (type == "background")
{
path = Path.Combine(path, _backgroundImageFolder);
}
else if (type == "image")
{
path = Path.Combine(path, _foregroundImageFolder);
}
foreach (var file in files)
{
_fileSystem.SaveFile(path, file.FileName, file.InputStream, file.ContentType, true);
}
// Return empty string to signify success
return Content("");
}
Well as another post said, "Welcome to episode 52,245,315 of 'Why Does Internet Explorer suck so badly':
Turns out that when you do file.FileName on an HttpPostedFileBase in Internet Explorer, it thinks you want the whole path of the file on the local machine. It's obviously an IE only thing as Chrome and Firefox seem to have it right.
Make sure to do the following when you only want the actual FileName:
var filename = Path.GetFileName(file.FileName);
The problem is when you actually try to save a file and send back a success response from the server. I don't think your demos are doing any of that. The iframe in ie9 does not receive the response from the server. The browser thinks the response is a download even though it's just a plain text json response. I debugged it down to the fact that the on load event on the iframe never gets fired so the onload handler that needs to handle this response is not doing anything. In all other browsers this is working.
Source: http://www.kendoui.com/forums/kendo-ui-web/upload/async-uploader-and-ie-9-not-working.aspx

Multi Post for Action with return File in ASP.NET MVC

Assume this code in One of my Actions
[HttpPost]
public ActionResult Generate (Params){
....
InsertOneRawToDB();
return RedirectToAction("Index", new { info = info });
}
So every thing is OK yet but when I change return to:
InsertOneRawToDB();
byte[] myfile = GenerateAZipFile();
return File( myfile , "application/zip" , "MyFileName");
In this case I see a weird behavior: before return, One raw inserted to DB, and after return another raw inserted, it seems the InsertOneRawToDB called again.
Does any one know about this? what happening here?
PS: I use Visual Studio 2012 RTM + ASP.NET MVC4
PS: OK I Use IDM (Internet Download Manager) to download zip file and that cause MultiPost on this Action So How can I handle this?
Based on your conclusion that the problem is related to using a download manager... That's what download mangers do. They create multiple connections to the file.
One thing you could do is store a session variable that says "Already started downloading", and then only insert the record the first time.
however if the user legitimately downloaded the file multiple times then you would only get one record.
Another option would be to examine the Http headers and look for the "Range" header, which is what is used to download a file in multiple pieces (or resume a file). You would then have to take the Range parameters and only return the portion of the file requested.
Here's an example of how to do a Ranged download: http://tpeczek.com/2011/10/range-requests-in-aspnet-mvc.html
I'm actually quite surprised that this hasn't come up before... I guess most people don't do database actions in a download action or notice it.
You can redirect to new action and in this new action return file.
[HttpPost]
public ActionResult Generate (Params){
....
InsertOneRawToDB();
return RedirectToAction("GetFile"};
}
[HttpGet]
public ActionResult GetFile
{
byte[] myfile = GenerateAZipFile();
return File(myfile, "application/zip", "filename.ext");
}

Redirect after EndInvoke in ASP.NET MVC

Hi have the following code on my view (JQuery):
$.post('<%=Url.Content("~/Customer/StartLongRunningProcess")%>');
Wich invokes an asynchronous call (C#):
public void StartLongRunningProcess()
{
ProcessTask processTask = new ProcessTask();
processTask.BeginInvoke(new AsyncCallback(EndLongRunningProcess), processTask);
}
Finally, the result of the call:
public void EndLongRunningProcess(IAsyncResult result)
{
ProcessTask processTask = (ProcessTask)result.AsyncState;
string id = processTask.EndInvoke(result);
RedirectToAction("~/SubscriptionList/SubscribedIndex/" + id);
}
The redirect is ignored. Response.Redirect also fails, since the HTTP headers has been already sent. I've tried change the window.location from javascript, this works, but I'm unable to pass the parameter id by ViewData. Any idea to resolve this?
Are you sure the headers have already been sent? I'm not really up on asynchronous controllers, but I would doubt that it would start sending any headers right away. My first thought would be that a redirect response to an ajax call isn't handled by the browser. You will probably need to implement some logic that sends back a result with the URL and have your success delegate in jQuery look for that piece of data and then do the redirect through javascript (i.e. window.location).
HTH

What is the MVC way of simultaneously sending a file and redirecting to a new page?

I have a form which users must fill out and submit. The controller action does some work and decides the user can have a file and so redirects to another action which is a FilePathResult.
[CaptchaValidator]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection collection)
{
// do some stuff ...
return RedirectToAction("Download");
}
[AcceptVerbs(HttpVerbs.Get)]
public FilePathResult Download()
{
var fileName = "c:\foo.exe";
return File(fileName, "application/octet-stream", "installer.exe");
}
What I would like to do is redirect the user to another page which thanks the user for downloading the file but I'm not sure how to accomplish that in a "MVC-like" way.
The only way I can think of off the top of my head is to skip the Download action and instead redirect to the ThankYou action, and have the ThankYou view use javascript to send the file. But this just doesn't seem very MVC to me. Is there a better approach?
Results:
The accepted answer is correct enough but I wanted to show I implemented it.
The Index action changes where it redirects to:
return RedirectToAction("Thankyou");
I added this controller (and view) to show the user any "post download information" and to say thanks for downloading the file. The AutoRefresh attribute I grabbed from link text which shows some other excellent uses.
[AutoRefresh(ControllerName="Download", ActionName="GetFile", DurationInSeconds=3)]
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Thankyou()
{
return View();
}
The action which get redirected to is this same as it was before:
[AcceptVerbs(HttpVerbs.Get)]
public FilePathResult GetFile()
{
var fileName = "c:\foo.exe";
return File(fileName, "application/octet-stream", "installer.exe");
}
Just add a header to your response, in the action for your redirected page.
Googling came up with this header:
Refresh: 5; URL=http://host/path
In your case the URL would be replaced with the URL to your download action
As the page I was reading says, the number 5 is the number of seconds to wait before "refreshing" to the url.
With the file being a download, it shouldn't move you off your nice redirect page :)

HOW? Controller return nothing/current view

SHORT:
How do I make a controller return the current view or just simply do nothing?
LONG:
I have a partial view where i've created an imageslider.
It contains a link which sends a request to a controller to get the next image (using ajax).
The controller fetches the next image, stores it in ViewData and sends back a partial view (the one above).
Now, what I do today is that when the controller reaches the last image it re-return the very same image (by refetching it), but still creates a new view, that is, the client/browser re-parses the "same" data.
This seems somewhat non-optimal.
What I'd like to do is that when controller reaches the last image it should simply do nothing.
If I return null then the view is updated with empty contents.
I want the view/client/browser to retain whatever it has and the controller to simply do nothing.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult GetNextImage(...)
{
if(Request.IsAjaxRequest())
{
if(CURRENT_IMAGE != LAST_IMAGE)
{
Image image = GetNextImage(...);
var partialViewResult = new PartialViewResult();
partialViewResult.ViewName = "ImageSlide";
partialViewResult.ViewData.Model = image;
return partialViewResult;
}
else
{
// DO NOTHING, HOW?
}
}
return RedirectToAction("Error", "Home");
}
You can return an EmptyResult if you want it to do nothing...
return new EmptyResult();
If you're using the AjaxHelper you can avoid the update by supplying an unsuccessful status code (e.g. 404 or whatever is most appropriate), that'll stop it replacing your div as the javascript in MicrosoftMvcAjax.js explicitly checks for a successful response before updating any elements:-
this.ControllerContext.HttpContext.Response.StatusCode = 404;
return new EmptyResult();
Ultimately the best way to avoid it is to design the partial view so it avoids the problem in the first place (like you mention yourself).
I ran into this problem today. I wanted to find a solution for how to deal with double-clicks on the client side trying to reenter the controller action on the server side while it was still processing. If a user entered that action, I wanted it to just ignore the request and do nothing on the browser side.
Solution looks like this:
public async Task<ActionResult> MyAction()
{
if(!CanEnterAction(nameof(MyAction))) return new HttpStatusCodeResult(204);
try
{
// Do long running stuff
return ValidActionResult();
}
finally
{
ExitedAction(nameof(MyAction));
}
}
Returning a status code of 204 basically does nothing to the page displayed in the browser. The actual result eventually makes it back to the browser when the action is complete.
This question is old, but I wasn't able to find an answer anywhere on StackOverflow. I figured it had to be possible since a FileResult doesn't really affect the current page, either, other than saving a file.
I would use
return new HttpStatusCodeResult(204);
this way you would stay on the same page and there is no post back.
Here is the defination
The HTTP 204 No Content success status response code indicates that the request has succeeded, but that the client doesn't need to go away from its current page
Assuming that you are using MicrosoftMvcAjax, you could send back a JavascriptResult that alerts the user that they have reached the end of the slider. If the response is javascript rather than content, the MicrosoftMvcAjax handler executes it instead of replacing the DOM contents.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult GetNextImage(...)
{
if(Request.IsAjaxRequest())
{
if(CURRENT_IMAGE != LAST_IMAGE)
{
Image image = GetNextImage(...);
var partialViewResult = new PartialViewResult();
partialViewResult.ViewName = "ImageSlide";
partialViewResult.ViewData.Model = image;
return partialViewResult;
}
else
{
return JavaScript( "alert('No more images');" );
}
}
return RedirectToAction("Error", "Home");
}
Of course, you'd probably want to be more creative and use the jQuery dialog plugin or something rather than an alert.
Ok, I've got it.
Not a solution to my question but still solves the problem.
I'll simply not show the "Next" link when the view shows the last image.
Can't believe I didn't think of it earlier...
Thanks for your efforts guys

Resources