ASP.NET 2.0 PageComplete Event troubles and File downloading - asp.net

I am trying to place an action to happen after an entire .aspx page displays. The "action" is a function that uses the Response object to send a file to the user.
More detailed information:
I am trying to replicate the behavior of a link on the page, from a sidebar. I.E. I have a link on the main page for the Export action, and it works fine -- since the page is already displayed before the user clicks it. But when the user is on a sidebar, clicks the link, it should take them back to this main page and then send the file after it displays.
I did some research and thought that using the PageComplete event would be well-suited for this, so I created my event handler and put the call to the export code (it keys off of a query string when loaded from the sidebar) inside my PageComplete event handler. But it behaves just the same way - the browser download box pops up and the page is never loaded before or after.
If it helps to understand what I'm doing here is a snippet of the code used to send the list to the user.
Response.Clear();
Response.BufferOutput = true;
Response.ContentType = "application/ms-excel";
Response.AppendHeader("content-disposition", "attachment;filename=MyList.xls");
Response.Write(listManager.ExportLists(mycode));
Response.End();
I would prefer a way to use a page event to load the page, rather than tinkering with this logic. But, if there is a clean and easier way to get the file sent, and it allows for loading the page then sending the file, that would be fine too.
Is there another Page event I can use besides PageComplete, or is it possible I am missing something?
EDIT: Sorry about the verbosity. I realize that I can't change the way HTTP requests work - I'm only looking for an acceptable solution that achieves more or less the same results. It seems like the way to go is to force a refresh of the page after a couple of seconds (thus ensuring that it loads before the file download code is executed) -- so I am looking for a way to do this as the first answer suggests - refresh to a download. (It doesn't have to be delayed either, if there's a way to refresh with no waiting)
Why doesn't this code work?
private void Page_LoadComplete(object sender, System.EventArgs e)
{
if (Request.QueryString["action"] != null)
{
if (Request.QueryString["action"] == "export")
{
Response.Redirect("MyHome.aspx?action=exportnow", false);
}
if (Request.QueryString["action"] == "exportnow")
{
ExportMasterList();
}
}
}
What it's supposed to do: after page loading is complete, do a Response.Redirect, reloading itself with a different query string. When it reaches the Page LoadComplete event again, the second time it will trigger the function which writes out the file.
What it actually does: Apparently repeats the same problem twice... it goes back to the same problem, how do you execute an action after the page loads, or wait until the page completely finishes loading, then trigger a refresh which will execute the action? Is there no way for ASP.NET to do something by itself without the user clicking on something?
If that's the case, then an auto-refresh after 2 seconds would also be acceptable... but I'm not sure how to do that.

The server can only return one object to the user, a file download or a page. The best you can manage is to return the user to a page that refreshes to a file download.

Related

ASP.Net WebForms - how to mitigate Thread Aborted exception when using async code + Response.Redirect

I am working on an older ASP.Net Web Forms application. I am looking to introduce async code to help modernise it. However have encountered an impase.
The application make uses of Response.Redirect(true) in the Master Page to end a session when doing things like checking whether the user is logged in. This ends the response and redirects user back to the login page.
The trouble is when async code is used on the page. Eg:
protected async void Page_Load(object sender, EventArgs e)
{
await _userService.GetUser();
if (!IsPostBack)
{
...
}
}
The page blows up when the redirect is hit in the master page with: System.Threading.ThreadAbortException: Thread was being aborted.
I understand why this happens from this question: Why Response.Redirect causes System.Threading.ThreadAbortException? but I am unsure how to get around this issue. I would like to use async code in the page and also allow the Response.Redirect to end the response without processing the rest of code.
I tried to use:
Response.Redirect(url, false);
Context.ApplicationInstance.CompleteRequest();
But then the page execution continues. Is there any way around this issue on WebForms? Or do I need to revert to non async code? There does not seem to be a way like in MVC to return RedirectToAction which stops page execution?
From my understanding it seems like async support is available for WebForms - how are other projects getting around this kind of issue? especially in scenarios where we need to stop page execution (for instance if user is logged out?)
Well, since that code call is to run and go off and do its own thing?
Then of course that code can't update the page, since the page will render, and be send off and down to the client side.
Your code behind as a general rule can't wait.
So, start that process outside of the web form - after all, that async code has nothing really to do with the page, right? Since the page will have LONG been rendered and send down to the browser side while that code goes off and runs and does whatever it supposed to do, right? of course the code can't wait, and you can't hold up the page being sent down to the browser, since if you do that, then your code not async anymore, is it?????
so, do this:
{
Thread mypthread = new Thread(_userService.GetUser(););
mypthread.Start();
}
After all, that routine has nothing to do with the current page, and the current page can't wait, since if it waits, then the page will be held up until all that processing is done and THEN MAKE the trip down to the browser side.
So, you can't block or hold up the round trip.
Page post back - browser travels up to server.
Code behind runs, new page is rendered, maybe code behind changes things on that page.
Page travels down back to browser and is now just sitting there.
So, if you going to fire off some other routine that you don't want to wait for? Sure, just start it in a new thread OUT SIDE of that all important round trip. But you certainly can't fire off some async code, but THEN wait for it to complete, and THEN let the page continue being rendered. As noted, to do so would defeat the whole purpose of async code.

Validation using navigation

I have some pages in my website and a left menu control. Control helps to navigate from one page to another.
My query is -> While user try to navigate to another page, I want to impose some validation like in the current page if the form is not saved, user will be asked to save it by using a confirm messagebox and if user presses no button, then user will be allowed to navigate otherwise, system will first save the details and then navigate.
Edit - My page is a content page, I meant, this is using a master page.
Use the following steps
window.onbeforeunload = confirmExit;
and a function that stops/continue the page execution.
function confirmExit() {
var email= document.getElementById("email");
if (email.value != "")
return "You have attempted to leave this page. If you have made any changes to the fields without clicking the Save button, your changes will be lost. Are you sure you want to exit this page?";
}
The way I would do this is to have an onbeforeunload javascript event fire which gives the user the choice to save the form. I personally would also poll the form saving data back whist they are completing it. I think this is the method SO uses.
There is a pretty decent example over on Code Project that may help http://www.codeproject.com/KB/aspnet/AutoSaveFormData.aspx
EDIT:
If you only want to call the save method you can mark it with the [WebMethod] filter and call it using XmlHttpRequest or jQuery's $.post

Back button refresh page

There are a few similar questions posted here but none that really addresses my needs.
I have a list of items on one page, lets call it masterlist.aspx. If I click on one these list items another page appears, i.e. details.aspx?id=something.
The page that appears has a formview control in edit mode. If the user wants to edit the data they hit an edit linkbutton and, the form is sent into edit mode, they then edit the data and click the save button, saving the data and putting the formview back in view mode.
The issue is if the user uses the browser back button to go back to the masterlist.aspx page the page is not updated, it's pulled out of the browser cache.
I have played around with the HTTP headers cache settings but can't get anything that works on all major browsers. On some browsers I get web page expired warnings. Another option is to somehow trigger a page refresh (or partial page refresh) when the page loads using client side code, but I haven't been able to figure out how to do this.
Is there any other approach or has anyone been successful with the two approaches above, or is there some way of avoiding the issue completely.
I have to do something like this in a catalog where the browse page needs to be loaded from the DB on every load because when you hit a product page it calls out to a 3rd party to get updated info, and then save it if it should be updated. This is so when you hit the back button like you're saying the data is reloaded. What I've done is added this into the page and it seems to work fine in all browsers.
public class ProductBrowser : Page
{
protected override void OnInit(EventArgs e)
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
Response.Cache.SetExpires(DateTime.MinValue);
base.OnInit(e);
}
}
What about capturing the Back button keypress event, then instead of allowing it do a browser.history.back(), you can do a document.location(...) call.
Alternatively, you could capture the back event and trigger a post-back, which could do a Response.Redirect("yourpage"). This would force the browser to issue a GET statement for the designated page, and make it refresh
With Javascript a complete solution may not be possible, but there is a workaround.
http://www.boutell.com/newfaq/creating/backbutton.html
It uses a timeout function that repeatedly forces the browser to perform a 'Forward' action. Even if someone clicks the Back button, it'll bring back to the actual page, thereby nullifying the Back operation. It may cause a flicker though.
I have played around with the HTTP headers cache settings but can't get anything that works on all major browsers. On some browsers I get web page expired warnings.
How do you get to that masterlist page? If you issue an HTTP GET and prevent clients and proxies to cache it you'd have no issues navigating back to it (and still getting an updated version).
That leaves you with server side caching (with proper invalidation when any of those items change) or no caching at all.

Back button must not go to previous page after signing out

I am developing an asp.net web site and I am not using inbuilt authentication controls of asp.net. I have created manually tables for users for site.
What I want is as follows
After logging in user can access the pages (that is already done)
When user press sign out (user goes to specific page - example - default.aspx)
Now when user press "back" button of browser, it must not go to previous page (that is done in Yahoo pages - I want to implement the same)
To prevent users from seeing the previous page when pressing the back button you need to instruct the browser not to cache this page:
Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
Response.Cache.SetValidUntilExpires(false);
Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
You could put this code in all authenticated pages, thus preventing them from being cached on client browsers.
For a page not to be cached the browser needs to respond appropriately to caching instructions, but there is no guarantee that this will work on every browser! (An appropriately evil person could write their own browser to ignore caching information, or write a proxy to strip it out...)
So you can't get this to work 100% of the time, but you're always going to face the problem that a user can easily take a screenshot, print out a page, save a copy on their disk, etc. once you've fed a page to them anyway...
the answer for you question is:
for When user press sign out. ( user goes to specific page - example - default.aspx )
you can add a LinkButton as Signout link and in the click event handler you can write
Response.Redirect("Default.aspx");
for Now when user press "back" button of browser It must not go to previous page
//add the following code to your code behind of the page
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
string strDisAbleBackButton;
strDisAbleBackButton = "<script language="javascript">\n";
strDisAbleBackButton += "window.history.forward(1);\n";
strDisAbleBackButton += "\n</script>";
ClientScript.RegisterClientScriptBlock(this.Page.GetType(), "clientScript", strDisAbleBackButton);
}
refer to csharpdotnetfreak.blogspot.com

Showing page load progress with JavaScript

I am retrieving a list of files from a web service and displaying them as links. When someone clicks on the link, the file should start downloading. The problem is that no direct access to the file exists. Here is the process to download a file:
User clicks on link
Server calls web service to retrieve file
Browser is loading for as long as it takes to bring the file back from the web service
Browser finally offers file to user for download
To counter the unfavorable user experience of having to wait for the web service before the download starts, I would like to display a "Loading..." feedback dialog when a link is clicked, and have the dialog go away when the file is ready.
I am using jQuery, and have been playing around with adding an iframe to the page to load the file, but the problem I am having is I don't know how to determine that the file is ready.
I am looking for tips, not only on the client side to give ample feedback, but perhaps also on the server side (using ASP.Net), just in case I can do something to streamline the process. Thank you.
Update: To clarify, this is for downloading a file to a user's machine. What I'm specifically looking for is a way to determine when the file is ready for download. So, when step #4 above takes place, I'd like to remove the "Loading..." message.
It sounds like you need to expose a new request URL on your server, whose only job is to reply whether the web-service-retrieval has completed (or if not, how far it has got).
Have AJAX poll that service every few seconds, and remove the "Loading..." message when it replied that the file is ready.
The client should send the session ID (maybe also a file ID), so that the server knows which file is being asked about. The server side will need to be able to contact the running web-service-retrieval process/thread to ask it how far it got. (If it can't find the thread then presumably it has finished.)
you can use the complete option for $.ajax function in jquery which should get called when the request completes
You can have jQuery to wait for the click of the link and when the link is clicked show the display loading message.
Example that shows how to 'Loading - Please wait' Message Display Script you can follow.
EDIT-
Basically you want a progress bar monitor, which I have never seen it done with just javascript, but with flash or ajax.
Here is a File Upload Demo that should help.
If you're linking to an ASHX that is grabbing the file and writing it back to a stream, you can modify the links click event to display a message.
$(function () {
$("a.someClass").click(function () {
$("div.message").fadeIn(); // show a loading message
return true; // make sure the link still gets followed
});
});
The loading message can just be a spinner or something while waiting for the ASHX to write the response back.
Edit: Since you're not directly linking to the ASHX:
$(function () {
var ashxCallback = function (data, textStatus) {
// data could be: xmlDoc, jsonObj, html, text, etc...
this; // the options for this ajax request
// textStatus can be one of: "timeout", "error", "notmodified", "success", "parsererror"
alert(textStatus);
};
$("a.someClass").click(function () {
$("div.message").fadeIn(); // show a loading message
$.get("some.ashx?id=1234", "", ashxCallback);
return false;
});
});
Using jQuery.get() and jQuery.post().
Edit #2: I just had another thought. What if the Loading... message was in an iframe that redirected to the file? So you would display the iframe with jQuery, it would have the default message of Loading... and a little spinner animation, then set the iframe's src/location to the ASHX that takes forever to load. You could then give it an onload handler from the iframe's parent page that would hide/remove it. Let me know if you need sample code.
If preparation for the file takes less than default timeout of AJAX request, you may start AJAX request, displaying "Loading..." message to user. AJAX call then checks for file's availability, and when it becomes available - returns with the message (success or not) and gives a link for the file. AJAX request can even do file download, but that's for your option.

Resources