In an ASP.NET 3.5 site we have a relatively standard payment checkout progess that contains a number of pages that need to be visited in sequence (shopping basket, payment details etc)
Each page has a "Continue" button that redirects to the next page in the sequence.
I would like a way of managing the page sequence so that:
I can have a Master page that defines the "Continue" button and its code-behind OnClick event handler
If the user attempts to visit a page out of sequence (by typing the URL directly into their browser, for example) they get redirected to the correct page
This page sequence is nicely defined in one place in my code (in an enum for example)
Why not use the ASP.NET Wizard control?
Alternatively (and I haven't tried it so I can't say how well it works), you could use Windows Workflow to define a sequential workflow and let that control the order pages come up in. There's an article at http://www.devx.com/dotnet/Article/34732 that takes you through doing it this way.
Check the HttpRequest.UrlReferrer variable in each Page_Load method...
http://msdn.microsoft.com/en-us/library/system.web.httprequest.urlreferrer.aspx
... and don't forget to check for nulls! You can bounce them to where they are supposed to be, based on where they came from.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Session["PreviousPage"] = Request.UrlReferrer.ToString();
...
}
else
{
...
}
}
Related
I would like to know if I can prevent the event bubbling / propagation on the page load based on condition like:
protected override void OnLoad(EventArgs e)
{
// If the user press F5 from the browser
if (IsRefresh)
{
// Here I want to stop propagation/bubbling the events like OnClick of a submit button but without using the button object (I want it generic)
}
else
{
base.OnLoad(e);
}
}
Is this possible?
Prevent the event bubbling has two difficulties - detect the F5 and stop the events - i would simply use the Post/Redirect/Get-pattern:
protected void Button1_Click(object sender, System.EventArgs e)
{
// do what you need to do
// now redirect to the same page,
// then a browser-refresh via F5 won't trigger this event again
Response.Redirect(Request.Url.PathAndQuery);
}
I did once in ASP.NET WebForms project when user press on F5 it'll do something other than refresh, I done this by use jQuery keypress even for the page and call e.preventDefault(); check this
Update:
$(document).ready(){function(){
this.keydown(e){
if (e.which == 116){
e.preventDefault();
window.location.href = "http://foo.com/?IsReferesh=1";
}
};
)};
You can use the QueryString to check if user press F5 and the reflection in Code Behind. If I missed something, then I didn't fully understand your question.
Regarding the specifics of your question, how about inverting it? Maybe add a Load event listener (say, in page Init) only if the condition is not met.
Another option might be doing something like:
this.Load -= MyPageLoadMethod;
You are likely looking at the event itself, not the OnLoad method. Eitherway, I think it is possible, see some options here How to remove all event handlers from a control
There is a big "BUT" though...
Like most people here, I think you are not solving the right problem. I'm sorry the same applies for Emad's solution I think.
Tim nailed it. This answer will try to explain this option more.
The pattern Post-Get-redirect is very popular and very standard. In most of our applications, submitting the same form more than once either by re-clicking the submit button before page loading or refreshing the browser causing another POST call is wrong, so, the practice of redirect after a non-AJAX form submission has become so standard.
Everything else you may try will be no more of a dirty workaround than a proper solution.
--
Now, comes the problem of providing feedback to the user. Ruby On Rails provides a nifty helper for this called Flash helper. It's a message you can set before redirecting (or even in the same page if there is no redirect) and the next time you display it, it'll destroy itself, the next time you call it, you get nothing. so, you display nothing.
ASP.NET MVC has something like this which is TempData. TempData can be stored in many places, by default in the Session, so, if you are redirecting from an MVC action, you can set something like TempData['Message'] , and read it in the next Controller. ASP.NET MVC will handle removing it after the redirect.
So, how about Webforms? There's nothing to prevent you from doing it too. It really isn't rocket science
You can check for URL Referrer whether it is an edit page or not and display message based on that.
You can store a message in Session before redirect, and check for it in every page load, if found, you display it and delete it from Session
If Session is disabled, you can use cookies. You can check the date of the cookie before sending, so that if the redirect never happened (use network dropped or whatever) and use came to the page later in the time he doesn't see an outdated message.
You can also store in Cache (with some key related to user of course, as Cache is application wide), You can then set the Cache expiration to some short period to avoid the problem explained in cookies option
You can also google for Rails Flash-like for ASP.NET Webforms to see what reusable components other people came up with. This result for example came in my search:
http://highoncoding.com/Articles/542_Creating_Rails_Like_Flash_Feature_in__NET_The_Flash_Control.aspx
Check NuGet also maybe there's something there. Again, it's not hard to build your own anyway.
Update:
Another simple approach might be to just redirect to the same URL, this'll cause a GET request and will not run any handler, pretty close to the original implementation of Post-Get-Redirect (yours will be Post-Post-Redirect maybe, kidding), and pretty safe as well.
Something like:
Response.Redirect(Request.RawUrl);
Update 2
Just as mentioned above, if you are running after page init, say in page Load, you can always emove events in a way similar to:
this.SubmitButton.Click -= SubmitButton_Click;
You get the idea...
That's useful if what you want is stop particular events, while the previous bits of the answer were assuming you are trying to stop most of the events.
http://gurustop.net
protected override void OnLoad(EventArgs e)
{
if (!IsPostBack)
{
if (Request.UrlReferrer != null)
if (Request.UrlReferrer.PathAndQuery == Request.RawUrl)
{
// Show Message
}
}
}
protected void Button1_Click(object sender, System.EventArgs e)
{
Response.Redirect(Request.Url.PathAndQuery);
}
Note:
if (Request.UrlReferrer.PathAndQuery == Request.RawUrl) // does not work with chrome
so you can replace it with
if (Request.UrlReferrer.AbsolutePath == Request.Url.AbsolutePath)
static string REFRESH_CHECK_GUID = "REFRESH_CHECK_GUID";
protected void Page_Load(object sender, EventArgs e)
{
if (ViewState[REFRESH_CHECK_GUID] != null && !ViewState[REFRESH_CHECK_GUID].ToString().Equals(Session[REFRESH_CHECK_GUID].ToString()))
{
Response.Redirect("Login.Aspx");
}
Session[REFRESH_CHECK_GUID] = System.Guid.NewGuid().ToString();
ViewState[REFRESH_CHECK_GUID] = Session[REFRESH_CHECK_GUID];
I am using this code to check if the request came from a page , if not then redirect somewhere.
string referer = Request.ServerVariables["HTTP_REFERER"];
if (string.IsNullOrEmpty(referer))
{
Response.Redirect("/UnauthorizedAccess.aspx");
}
It is working , I don't know whether it is perfect the solution.However I am checking this on load event of one of my page.How can I make it check on every request.Should I check this for all my pages.Also it is a good approach.Can anybody point me in the right direction.Any suggestion is welcome.
If you have logic that you would like to be run on the OnLoad of a bunch of your pages. You should probably create a BasePage that derives from Page and have the logic inside. Then all the pages you want that logic in can derive from BasePage instead of the regular Page.
Another approach can be using Master Pages
Note: After reading OPs additional comments. One thing to look out for when using a Master Page is that the Master Page's Page_Load event happens AFTER the Content Page's Page_Load event.
In other words the lifecycle is like this:
Master Page Init Event
Content Page Init Event
Content Page Load Event
Master Page Load Event
If your response.redirect moves the user to another page with the same master page (and same "validation" check) you might find yourself in an endless loop :)
If you have lot of pages, with these kind of common codes, than one possible solution is creating your own MyPage class as a child of the standard Page class. In your MyPage you can use something like:
Page_Load(object sender, EventArgs e)
{
string referer = Request.ServerVariables["HTTP_REFERER"];
if (string.IsNullOrEmpty(referer))
{
Response.Redirect("/UnauthorizedAccess.aspx");
}
base.Page_Load(sender, e);
}
Then any of your pages can inherit from this own MyPage class instead of the .NET's standard one.
In this way the common code reside in one place. In case of any change you have to modify that only there.
Or another possibility, you can consider using Master Pages.
I need a function in global.asax file which gets called only once when user enter a page url. application_beginrequest gets called 50-60 times in a single page( as to render a page several requests go to server.)
I thought of a solution - I can write my function in global.asax and call it on page load of other pages but in that solution I need to call it in every page. I would prefer something which is to be done only in global.asax
You could test the request url in the Begin_Request function:
protected void Application_BeginRequest(object sender, EventArgs e)
{
// Perform some test on the request url that suits your case:
if (Request.Url.ToString().EndsWith(".aspx"))
{
// ...
}
}
Another variation on your approach would be create a subclass of Page which called your global method, or did the work itself and then have all your pages extend your class not Page directly.
public class AcmePage : Page{
// override/subscribe to one of the page events
}
...
public class HomePage : AcmePage
If you are just trying to count page views it may be worth looking at the pre built analytics services available, like the free google analytics. There are others around like CoreMetrics etc.
If I have a button inside one page for example "main.aspx". And upon being clicked I want the clicked method in "main.aspx.cs" to call a function from a different *.aspx.cs page but also redirect to that *.aspx page...is that possible?
A more concrete example of what you're trying to do would be useful. Otherwise you'll get all sorts of answers, many of which will be off the mark.
You should put common code in the App_Code folder. You should also not have any business logic inside a forms code-behind.
The fact that you need one page to call a method in another page indicates that you haven't done this. Pages are for displaying and interpreting actions, but they should not hold any of the business logic.
e.g. a lame example of a shopping cart.
ProductView.aspx - calls Cart.AddToCart(Product p)
CartView.aspx - displays all items in the cart, and the user
can update or remove items.
- calls Cart.RemoveItem(int cartIndex)
- calls Cart.UpdateItem(int cartIndex, int newItemCount)
Cart itself doesn't belong in either CartView.aspx or ProductView.aspx. It instead belongs in ~/App_Code/Cart.cs
So your solution could look something like
/ (root of your web folder)
Product/
ProductView.aspx
ProductView.aspx.cs
Cart/
CartView.aspx
CartView.aspx.cs
App_Code/
Cart.cs
Product.cs
Also, to add the App_Code folder if it's not already there, right-click the web project and select Add ASP.NET folder and choose App_Code
You need to understand the ASP.NET Page lifecycle and you'll see why this is not Kosher. If you really need to call a method from multiple pages, it sounds like a good candidate for some external class/object.
Alternately, instead of using a querystring parameter, you can set a Session flag and then redirect to this new page. Then clear the session flag when you call that page's method.
In a very basic way, for Main.aspx to go to Other.aspx and to pass some small amount of data on the querystring to indicate an action.
Main.aspx
protected void OnSomeButtonClicked(object sender, EventArgs e)
{
if( someCondition )
{
Response.Redirect("~/Other.aspx?action=runAway");
}
}
in Other.aspx
protected void Page_Load(object sender, EventArgs e)
{
if( !IsPostBack )
{
if( "runAway".Equals(Request.QueryString["action"] )
{
RunAway();
}
}
}
You should also read up on Server.Transfer versus Response.Redirect. Which one to use is situation dependent.
Also note that QueryString parameters can be altered easily by the user, so always verify them and never trust the user.
Why don't you just redirect with a querystring parameter and then execute the function on the destination page?
Instantiate an object from your other page class and call the method.
protected Button1_Clicked(Sender s, Eventargs e){
MyNamespace.Page_Other po = new MyNamespace.Page_Other();
po.Method1();
Response.Redirect("~/page_other.aspx");
}
When I have a child .ASCX control that needs to affect something in the parent page I'm not completely sure how I am supposed to handle the event flow in the case where I need to update something in the parent page.
What I've always ended up doing is putting logic into the 'Pre_Render' event handler.
Since this is processed after any child .ascx controls are processed I can be sure to render the ASPX page correctly before it displays. I just dont think this is a good design and I've always cringed when I've had to do it. But now there is stackoverflow so i can finally ask it!
For instance lets say I have a 'login control' ascx control in a page. The containing page displays a text label in the header bar for 'current logged in user'.
Lets say I click the 'login' button, which will then trigger my authentication and log my user in. The problem is that the text label in the parent page has already been rendered as 'No user logged in'. Thats no good!
By putting the logic into 'PreRender' it will be rendered after the user has logged in. I just dont like this because thats not what PreRender is for.
What is the intended best practice here that I'm missing? I know I could put an event handler on the user control, but that seems clumsy too because there'd be too much coupling.
PS. I'm just using this as an example. I'd had this problem numerous other times so please dont reply telling me how to implement login !
In your ascx.cs:
public delegate void NavigateEventHandler(int PID); // if you want a custom handler
public event NavigateEventHandler onNavigate;
In your page.aspx.cs:
protected void Page_Init(object sender, EventArgs e) {
eSelector1.onNavigate += new PostSelector.NavigateEventHandler(eSelector1_Navigate); }
public void eSelector1_Navigate(int PID) {
eSelector1.PopulateComments(eSelector1.m_PID); }