First of all, thanks for reading.
I will describe my situation as explicitly as I can.
I have a page where users can leave comments.
Here's the commenting flow
A-1. 'comment' button is clicked
A-2. a modal popup with a textbox is shown using ModalPopupExtender in ajaxtoolkit.
A-3. User types a comment in the textbox, and click "ok".
However, when user is not logged in, expected behavior changes.
B-1. 'comment' button is clicked
B-2. a Login modal-popup with id & pwd textbox is shown.
B-3. User types ID & pwd, and click ok.
B-4. Comment-modal-popup is shown
B-5. user types a comment and click ok.
I have a PROBLEM handing this case.
When B-3 occurs, page is posted back, i log the user in, update session object, and I Response.Rediect() the page to itself to display correct logged-in status (i have to..).
After redirect, in Page_Load(), I need to check some values to show Comment-Modal-Popup.
But I'm not sure how..
Here's what i considered
ViewState
i just can't use it since the page was redirected not posted back.
QueryString
I could have add "showCommentPopup=1" on URL when redirecting, but that will leave unwanted QueryString in URL. I don't want users to misuse it.
Session
I actually used Session object. Before redirection, I set Session[ "ShowCommentPopup" ] to true. In Page_Load() if it is set, i remove it and show the popup.
using Session like i did doesn't work correctly when user opens same page in multiple tabs.
user opens two tabs(in Firefox) with same URL
user follows steps from B-1 to B-3 in first tab.
before the page is redirected between B-3 and B-4, user refreshes second tab.
if the timing is right, comment-popup is shown in the second tab.
I expect to hear great insights from stackoverflow..
I haven't tried this but I think if you store your ShowCommentPopup flag in the HttpContext.Items collection instead of the session and then use Server.Transfer instead of Response.Redirect you should be able to achieve the desired results.
HttpContext.Items is a dictionary that can be used to store data whose lifetime is the lifetime of the request. This means a second request from a different tab or window will have a different HttpContext.Items dictionary.
Server.Transfer is somewhat like Response.Redirect in that it allows you to load a "different" URL instead of the original. However, while Reponse.Redirect initiates a new request, Server.Transfer transfers the existing request to the new page on the server.
A better explanation of the differences between Response.Redirect and Server.Transfer can be found here.
Example
bool showCommentPopup = false;
if (HttpContext.Current.Items["ShowCommentPopup"] != null)
{
showCommentPopup = (bool)HttpContext.Current.Items["ShowCommentPopup"];
}
//...
HttpContext.Current.Items["ShowCommentPopup"] = true;
You've clearly thought your solutions through! I'm guessing the problem with the Session was that they could comment on a different page than the one they logged into. You could get around this by storing the session var, not as a bool, but as the page to show it on:
var uniqueString = this.ToString() + uniquePageID;
if (Session["ShowCommentPage"].ToString() == uniqueString)
//show modal & remove session var
Now your program only "breaks" when the user visits the same object in two different windows, logs in on Window #1, and refreshes on Window #2. And it's not really breaking since they wind up commenting on the same object either way.
The reason I used uniquePageID, is cause I'm figuring you have a template page ("showObject.aspx") with arguments on which to show ("showObject.aspx?objectID=3"). In order to make sure the comment is left on the same ID, it needs to be present in uniqueString
Related
...in comparison to requests of normal link click behaviour. I thought I might be able to use this to throw away some cached stuff on the serverside. For a more technical orientated target audience this could be a relative natural way of clearing a cache for i.e. graphs and charts.
To be clear - I am using ASP.NET MVC
I've just checked the three browsers you're likely to care about, and all three add extra cache headers in the request when you refresh the page. Conceivable you could check for those headers to throw out some server-side cache. It also seems a logical and natural way to do it.
IE: adds "Pragma: no-cache"
Chrome: adds "Cache-Control: max-age=0" and "If-Modified-Since: Tue, 17 Dec 2013 10:16:22 GMT" (disclaimer: time may vary)
Firefox: adds "Cache-Control: max-age=0"
I just checked this by refreshing this page in all three browsers, and checking Fiddler. It is possible there is more sophisticated logic going on that I haven't caught on to.
Since it's in MVC, I would use the TempData to achieve this. On the first load of your method, you can set a value in the TempData so on the next load(the refresh) you would have a value set in this.
Let say you have a Add method, I think that should do the trick :
public virtual ActionResult Add(Model model)
{
if(TempData.ContainsKey("yourKey"))
{
//it means that you reload this method or you click on the link
//be sure to use unique key by request since TempData is use for the next request
}
TempData.Add("yourKey", true);
return View(model);
}
Add this script to your head section:
<script>
$(window).keydown(function (e) {
if (e.which == 116) {
e.preventDefault();
var l = window.location;
var lp = (l + "?").split('?');
window.location = lp[0] + "?f5=1&" + lp[1];
return false;
}
});
</script>
In server side just check if Request["f5"]=="1"
If you prefer not to modify the URL, you may add a temporary cookie. something like this:
<script>
$(window).keydown(function (e) {
if (e.which == 116) {
AddCookie("F5","1")
}
});
</script>
On page load check if the cookie exists and delete it for next request.
You could compare the current session ID to the last page that was loaded and then compare the IsPostBack command.
If IsPostBack is true you know the user has clicked something on that page to post the page back.
If the last page that was retrieved for the current session ID was not the same as the page you are currently loading then they arrived at this page from another page.
If the last page that was retrieved is the same for the current session ID as the current page you are processing then this was likely a refresh (F5).
The only problem would be that you would detect F5 the same as someone putting their cursor in the address bar and hitting the Enter key once the page had finished loading, but I doubt this would be a problem for you.
EDIT:
There seems to be some confusion on the comments about how this system would work so let me explain further. As was pointed out to me IsPostBack is not available in MVC so you have to test for post backs as shown here:
ASP.NET MVC - Is IsPostBack still here?
Let us consider the three ways in which you can end up at a page, any page. For our examples let us assume we want to detect refreshes on page X.
You can get to Page X these ways:
1. User presses a button on page A which takes you to page X.
2. User presses a button on Page B which posts you back to page B (post back).
3. User presses F5 or reloads the page some other way. - this is the one we want to detect..
There is scenario 4 which is 'user comes to page X from another site' but this would start a new session so let us not consider this.
Everytime a user loads a page you store that page somewhere along with the SessionID. The SessionID is constant for any one user for the duration of the session time out. There are some caveats to this such as accessing from different browsers on a single machine but I do not want to confuse matters.
Scenario 1:
User loads page A, we look in our memory and there are no records at present. We store 'Page A' and the sessionID in memory.
User clicks button on Page A.
User is redirected, posts or transferred to Page B.
Page B loads, we check the 'IsPostBack' flag, it may or may not be true at this point. If it is we know it is not a refresh, if it is false we need to continue to test as follows. we look in our memory and there is a record for 'Page A' for the current Session ID (remember the session ID does not change between requests for any given user).
because the previous page was 'Page A' and we are on Page B we know this is NOT a refresh.
We store 'Page B; and the sessionID in memory (in practice we either erase or update the previous record pointing to Page A).
Scenario 2:
User loads page B, we store 'Page B' and the sessionID in memory.
User clicks a button on page B.
Page B loads, we check 'IsPostBack' flag. It is true so we know this is not a refresh.
Scenario 3:
User loads Page B, we store 'Page B' and the sessionID in memory.
User refreshes the page or reloads it by putting their cursor in the address bar and hitting enter.
Page B loads, we check the 'IsPostBack' flag, it is false so we need to continue to test as follows. we look in our memory and there is a record for 'Page B' for the current Session ID. Because the previous page was 'Page B' and we are on Page B we know this IS a refresh.
By using this approach you can detect refreshes. If you are using MVC you can test Request.HttpMethod=="POST"
The only problem you get is if the user does a POST, you do a redirection, then the user goes back one page and refreshes from there are is will resubmit the form, sending the POST again. This will be detected as a fresh submission when it is actually a refresh. This can be eliminated using a Nonce approach or something similar.
I have an ASP.NET Web Form (C#) that users fill out, it has a couple of drop down lists that cause post backs and some validation that causes post backs. I also use a couple of different update panels. One panel is visible for user input and the other panel appears after the user clicks submit and the data is added to the database. The confirmation panel (the last panel) also displays a confirmation number to the user.
After the last panel displays, I would like to prevent the page from reloading when the user presses F5 or refresh. The reason is, I don't want the user to accidentally click refresh or F5 and lose the confirmation number and message. When the user presses F5, the browser interprets it as wanting to load a new page and at that point the session is cleared (as indicated in code below).
My first thought was to reprint the message and confirmation number and make sure that the panel holding it stayed visible. I was going to do this in the else section of the code below - but since there are multiple postbacks and they all trigger the page load, that doesn't work, unless there is someway to determine if the postback is the result of a form element or the f5 key.
protected void Page_Load(object sender, EventArgs e)
{
try
{
if (!IsPostBack)
{
Session.Clear();
GetList();
}
}
catch (Exception ex)
{
//Do something();
}
}
I've seen a few different posts that recommend using Javascript, but I am avoiding that if possible, in the remote chance that someone has javascript turned off.
Here is the flow of the program:
User opens form, fills in fields in the contactpanel and the clicks submit. The fields are validated using ASP.NET server validation controls and if everything is good, the data is sent to the database and a confirmation number is returned. Finally, the contactpanel is set to visibility=false and the confirmationpanel is set to visibility=true.
Hope this helps. Appreciate any suggestions.
Thanks,
Move your content that's on the final screen (the confirmation) to an entirely new page. Whatever data is saved to get to that confirmation should go in some type of data storage (like a database), and then whatever ID or variables the visitor needs to pull that information should go in a Session var. This way when the user refreshes the page, they will still see the content as you intend. It's pretty much how we had to do all forms back in the day anyway.
For instance:
~/form_page.aspx
- User lands
- Fills in information
- Post backs save data as the user progresses
- Save all data collected to a resource such as a database
- Save an ID to access that information to a Session variable
- Redirect user to...
~/form_thankyou.aspx
- On load, get the Session variable needed to pull the information
- Retrieve information from the resource
- Display results to user
(That's not actual code, I was just having trouble with the formatting.)
Using this technique will also make it easier on your SEO/Metrics team to track conversions (even though there are plenty of other ways).
Rather than using the Session object or storing to the database, I am storing temporary variables that I need persisted to custom ViewState variables. For example, ViewState("MyField1") = 1
When the user hits the browser Rrefresh button, Page.IsPostback is back to False and the ViewState is gone.
My question is. If the user can blow away the Viewstate by refreshing, why would anyone use it?
I know that a Refresh reposts the last submitted page, by why is Page.IsPostback reset to False and the ViewState blown away?
Flame me if you want for creating a potential dup question, but I've read other postings here, and it ain't sinking in...
Update to original post:
I now think that it has to do with postbacks that are performed as a result of clicking on Buttons that are within an UpdatePanel. Can someone help shed some light on this?
When a client refreshes their browser, it re-submits the last full page request issued by the client (which may be a GET or a POST). It does not ever resubmit AJAX requests such as those produced by update panel event triggers ("partial page postbacks").
The fact that Page.IsPostback is false when you refresh the page means that your original request is a GET, so here's what's probably happening:
1) During the initial request, the client sends no form data to the server - hence no hidden field containing view state data (Understanding ASP.NET View State is pretty detailed, but a great read if you want to really understand what's going on). While processing this request, ASP.NET may send some view state back to the client, but the original request is just a URL.
2) When the user clicks a button within an UpdatePanel, they trigger a partial postback during which MyField is set to 1. The UpdatePanel changes the client's view state to reflect the new value.
At this point, if the user submits a POST request by normal means, such as clicking a button, the view state will contain the updated information.
If the user clicks 'Refresh' though, they re-submit the original request from step 1, with no form data and therefore no view state.
Where do you set your ViewState? And where do you re-read your ViewState value? Maybe oyu check its content before asp.net calls the LoadViewState() method.
User hitting refresh and using updatepanel will not work together very well. I quess this is why people say that WebForms provides a leaky abstraction on web programming and some are moving to mvc.
If you're not interested in migrating, I'd give you the advice that do not use updatepanel for too long or big operations, where you can assume that user might refresh the page. Use it for small things like dropdown2 items changing when selection on dropdown1 changes.
Wrapping lots of functionality in one updatepanel will cause trouble, if you just depend on viewstate.
Your question is, "Why would anybody use it."
Viewstate comes in handy for data you know is generated by a post back. Hitting refresh is not a post back, but a fresh request.
So lets say you are browsing a datagrid and you need to know certain bits of data about what they have clicked, on the click event you could store that data in the viewstate and process it during other times in the page life cycle, or subsequent post backs.
ViewState's advantage is that it is just embedded into the HTML, so it is all client side. Where as SessionState is server side, and if you store a great amount of data in the session you can cause your web or db server to work harder to handle that data.
Hope this helps.
Don't know why it works but I had a similair problem and solved it by putting this line in the form_load:
me.myProperty = me.myProperty
where
Public Property myProperty() as String
Get
If Not IsNothing(ViewState("data")) Then
Return CType(ViewState("data"), String)
Else
Return String.Empty
End If
End Get
Set(value As String)
ViewState("data") = value
End Set
I am using VS 2005, C# 2, ASP.Net 2.0
I am unable to find out how to track that user pressed F5/Ctrl+F5/ Open a new Window(Ctrl + N) in ASP.Net.
I know that there is a Page.IsPostBack property, which tells that a page is loaded in response to an action taken by user.
I am just curious to know, that why isn't there a property as IsRefresh or Page.IsRefresh in ASP.Net, which will return true,
whenever user takes any of the above actions.
Is there a way to know this?
Actually my problem is that i have a DLL using which all of my aspx pages are inherited, I have to
insert some values in a table whenever the page is opened for the first time that's it, if user just opens the page or do not take any action,
an entry should be inserted into the database, but as far as I have tried, I controlled it anyhow using the Page.IsPostBack property, but I got stuck
in the refresh case, as it is inserting records unconditionally.
Similar to using a function in Global.asax (as others have suggested) you could use a session variable "flag". When the page first loads set a session variable and then just check against it in your page load function:
if (Session("visited") != "true"
//page has not been visited, log visit to DB
Just make sure you set the session flag sometime after the above check during the page load.
It won't be exact (sessions can timeout while a page is active, users can completely leave the site and come back in the same browser and the session stays alive, etc) but for your tracking it is much better than counting every page hit in the DB.
Perhaps you want the Session_Start method in the Global.asax file, which will be triggered once at the start of each user session?
In your Global.asax file, add or edit the method:
void Session_Start(object sender, EventArgs e)
{
}
why isn't there a property as IsRefresh or Page.IsRefresh in ASP.Net
Because ASP.NET cannot possibly know. The browser does not send any information that could allow it to determine whether the page is being requested due to a refresh or normal load. You will need to reconsider your requirements: what is the actual purpose of the database logging?
Session_Start method in Global.asax file is fired every time when a browser session is started. You can use this method to count number of unique users on your website.
Session_End method in Global.asax is fired when a session ends (explicitly or timedout). So you can decrement the count here.
Hope the above to example uses of these methods helps you understand how you can use them.
Because of the stateless nature of HTTP protocol there is no way to tell apart the initial load from the refresh
As has already been said. This isn't possible. A request issued due to a refresh is no different to a request issued the first time the page is loaded.
It sounds to me like you are trying to track page views somehow. This is certainly possible though it will require some work on your part. Your best bet is probably to log the URL of the page. You may also want to include the query string in order to differentiate between page loads for different pieces of data (if this happens in your application). You will also want to log the ID of the current user, and the ID of their session.
You can then make sure that you don't insert two page views for the same user for the same page in the same session, effectively filtering out any reloads of a page.
You do need to be aware that this isn't the same as detecting a refresh, what you are detecting is two page views in the same session, this could be a refresh, or it could be use of the back button, or just reloading from the address bar.
My suggestion would be to create a cookie on very first load, then on Page_Load check to see if the cookie exists. If it does, don't insert the record. You can use Session_End to destroy or create the cookie as someone suggested if that works with your application's architecture.
I have written an application in ASP.net, that is designed to let the user add records to a database. The page is set up that when a user adds a record, the ID number of the newly added record is set in session, the page Response.Redirects to a "Thank you for submitting" page, then redirects back to the original page to allow further edits. Users can also use the Back button on this screen to go back to the original record adding page, which allows them to make edits to the data.
However, I have found that storing the ID in session isn't a terribly good solution, as a user might try to create two documents in different tabs or windows. I have also tried setting the ID in a literal control, but this causes the problem that when the user uses the Back button, the literal control isn't set to the ID, and new records get added instead of one being edited.
Is there any kind of solution for this?
I'd recommend storing your ID in the QueryString. After the record is added, redirect to your "thankyou" page, which then I am guessing contains a link to the edit form which you will generate with the ID in the querystring. When that link is followed, the edit page shouild pull the ID out of the query string in order to load up the correct record to edit.
Your add and edit form can even be the same page, when an ID is provided in the querystring, your form knows to edit that record, otherwise your form adds a new record.
Silly question, why can the user use the back button to edit the data just accepted in a post?
If the edit previously posted data is a common scenario why not just redirect to a page when the data is accepted that lets them edit it. Then if the hit the back button they would be going back to the original "clean" insert/add new data page.
This would give the following flows
Add->[Post]->Edit->.....
Add->[Post]->Edit->[Back button]->Add->[Post]->Edit->[Post]->Edit....
Have you tried adding the ID in the querystring? Then you could read it, and add it to the session as needed (say on a user clicking the back button).
Seems like a lot of problems allowing editing of an object in a page rendered when using the back button. Would it be too much to give them an edit button instead?
The controls save their state in the ViewState. If you choose to use SessionState instead of ViewState to store the information, then the controls will save their state in the session state and it won't work properly with multiple tabs.
I have not yet found a way to bypass this issue while still using SessionState. Our solution was to use the normal ViewState.
I've tried storing the ID in the querystring (which is mostly fine for editing), but the problem with that is when the information is stored in session for when they use the Back button. If the user does the following:
User creates a record (1st record), the ID is passed along in the querystring, and temporarily stored in session.
User creates another record (2nd record), the ID is passed along in the querystring, temporarily stored in session.
User uses the Back button on the first record to go to the page that doesn't have the querystring.
It's probably a far-fetched scenario, but it's one that may happen. The only solution I have is to block the usage of the Back button to go back to the adding page, by using window.history.forward() in JavaScript. But this as a solution is terrible.
My question for you is why are you storing anything in the session to begin with? If you can avoid storing anything in the session, I think you will be better off altogether.
Having thought about this, does the following sound like a decent solution to the problem I outlined above?
When first adding a record, store a timestamp of when the add page was accessed in a hidden field.
This timestamp is passed through session when the user clicks save. Along with the ID.
If the user opens another tab at the same time and saves, then the new page's timestamp gets passed through session.
If the user tries to access the add page of first record (using the back button), the system looks up session, and sees if there is a timestamp, and whether it matches the one in the hidden field for that page.
If it doesn't match, then the user gets a prompt, and told to edit the record properly.
Does this sound reasonable, or too overly complex?