I have a web application (ASP.Net 3.5) with a conventional 3 layer design. If the user clicks a button a postback happens, some middle and data layer code runs, and the screen is refreshed. If the user clicks the button multiple times before the first postback is completed my logic gets confused and the app can end up in an invalid state.
What are the best ways to prevent this?
I can use javascript to disable the button but this just hides the problem. How do I build my business and data layers to handle this?
The three most popular methods (which are often used in tandem) are:
Disable submit buttons once clicked/pressed;
Use POST+REDIRECT+GET to avoid back button issues; and
Replace history in the browser so you can't go back (but this should be used sparingly and with good reason).
If I was to be brutally honest, I would say that it sounds like you're the one confused about web postbacks, not your application (that's if you're the one who wrote it). ;-)
That said, in addition to other suggestions, what I would do in this case is place a "token" in hidden field in the form - like a GUID - that is posted back. Use this to track the work being done and only allow it to be used once. E.g. when posted back, place it in session storage. Each time a postback is performed check the session first for this token, and if it is there then do nothing. If it's NOT there, store it in session and do the work. When the session ends, tokens are thrown away automagically. Easy. Much better than some convoluted database token.
Oisin
Do disable submit button once clicked. This will prevent accidental double-click or more
I usually redirect to a different URL after postback to avoid accidental/intentional page refresh.
Finally in your DB insert method, check for identical data inserted within certain time frame (probably in seconds) before doing the insert. If duplicate data is found inserted within just seconds (or minutes. whatever makes most sense in your situation), show warning message and have user hit submit again if user feels it is not error. (This method makes most sense when you have user account and user is submitting data when logged in, so duplicate data check is done for the user.)
Check out this ASP.NET AJAX control called PostBack Ritalin from a fellow SO'r Dave Ward.
I have solved the problem writing a javascript disabling the click function button:
MyButton.Attributes.Add("onclick",
"javascript:this.onclick=function(){return false;};");
We have all seen the websites that disable "submit" buttons when you click on them. This is often done to prevent users from clicking the button multiple times.
Normally this is accomplished using an 'onclick' JavaScript event to disable the button. In ASP.NET, each server side item already has a onclick event handler which calls the server back for event processing.
To accomplish the same thing in ASP.NET, you could easily do:
btnSubmit.Attributes.Add("onclick", "this.disabled=true;" + GetPostBackEventReference(btnSubmit).ToString());
Where 'btnSubmit' is the name of the button in question. What happens here is we create an onclick event that does two things. Firstly, it disables the button in the users browser. The second thing it does is submit the normal postback event to the server.
Even i got the Same Problem I have resolved like Below.
After uploading a File If you Redirect to same page or some other page in your project this problem will be avoided.
For Example:
In My ASPX
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm.aspx.cs" Inherits="WebApplication.WebForm" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:FileUpload ID="FileUpload1" runat="server" />
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" />
</div>
</form>
</body>
</html>
Even i got the Same Problem I have resolved like Below.
After uploading the File If you Redirect to same page or some other page in your project. After Redirection Response will not be there once you redirected.
In My ASPX
In My Code Behind
public partial class WebForm : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
string path = Server.MapPath("~");
path = path + FileUpload1.FileName;
FileUpload1.SaveAs(path);
Response.Redirect("WebForm.aspx"); // Responce will be cleared. This Redirection will do the Trick
//Put the debugger and check it will work
}
}
Here, to show the success and error messages try to use sessions.
Related
I have an asp:Button that fires a code behind function on the OnClick event. In that OnClick event several things happen, and among those things I do a check in the database for if I need to ask the user a yes or no question. For that I need a message box. First I did it like this:
protected void MyButton_Onclick(object sender, EventArgs e)
{
// lots of stuff happening
bool iNeedToAskTheUser = INeedToAskTheUser(stuff);
if (iNeedToAskTheUser)
{
DialogResult result = MessageBox.Show("Do you want to fix all objects?", "Fix objects", MessageBoxButtons.YesNo);
if (result == DialogResult.Yes) // do stuff
}
// some other stuff
}
This works fine locally but not when deployed, so I figure I would need to use ScriptManager.RegisterStartupScript instead. I could just add javascript on the ASPX page that fires up a dialog and saves the response in a hidden control that I can then look at, but I don't want to fire up the dialog unless I have to, which I check for before I do the DialogResult in the code above. So I can't do that immediately when the user clicks the button.
Is there any way I can use ScriptManager.RegisterStartupScript in "the middle" of my _OnClick code so that I can choose whether or not to actually show the button, and then also know if the user clicked yes or no, (preferably) without doing a postback?
I've been thinking and testing two different solutions:
Use ScriptManager.RegisterStartupScript in code behind to fire a JavaScript confirm function on the ASPX page. The JavaScript function would set a value in a hidden control depending on if the user answered yes or no and then my code behind stuff would check the value of that hidden field and act upon that. The problem with that is that once ScriptManager.RegisterStartupScript fires it doesn't wait for the JavaScript function to "finish", ie wait for the user to reply to the confirm(). So the value in the hidden control will always be empty because the code behind gets to the check of that control before the user has a chance to respond to the confirm(). So that's a no go.
Use ScriptManager.RegisterStartupScript in code behind to open up a new ASPX page that asks the user the question and then does all the work in response to the user's answer in that page. The problem then is to pass the object that the new ASPX page needs to do work on in response to the user's response.
I'm sure there are great solutions using Ajax or jQuery but this is a fairly simple function that shouldn't take too long to develop, so that is kind of out of scope for this.
Instead I'll go with a solution where I know what the user will respond to the question before they click the button. (While silently muttering under my breath: "It's 2019 and there's no good way to fire up a yes/no dialog from code behind in a .Net web project...". I need to get back to not working with web).
How can i override, or extend, the standard WebForms WebForm_OnSubmit JavaScript function?
I am using HTML5 input types in an ASP.net WebForms web-site. The user-agent (e.g. Chrome, IE, Firefox) already correctly handles data sanitization, alternate UI, etc.
Normally, when a user clicks an <input type="submit"> button, the User-Agent will halt the submit, and show UI to indicate to the user that their input is going to be invalid:
The problem with WebForms is that it does not use a Submit button. Instead, everything can be done through a JavaScript postback, rather than submitting the form:
<asp:LinkButton ID="bbOK" Text="Save User" runat="server" OnClick="bbOK_Click"></asp:LinkButton>
Which, when rendered into client HTML, becomes a call to the JavaScript:
WebForm_DoPostBackWithOptions(...)
With the really long form being an Anchor tag:
<a id="Really_Long_Thing_ctl00_bbOK"
href='javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("ctl00$MainContent$VistaToolbar$ctl00$bbOK", "", true, "", "", false, true))'>
Save User
</a>
This means that the user-agent never gives the user a prompt that an entered element is invalid.
Even though the form is not being "submitted", it does still trigger an onsubmit event. I can manually trigger the HTML5 form validation using something like:
isValid = form.checkValidity();
Ideally i would hook that event in my ASP.NET WebForms site:
Site.master
<form runat="server" onsubmit="return CheckFormValidation(this)">
<script type="text/javascript">
function CheckFormValidation(sender)
{
//Is the html5 form validation method supported?
if (sender.checkValidity)
return sender.checkValidity();
//If the form doesn't support validation, then we just assume true
return true;
}
</script>
Except that the WebForms infrastructure deletes my onsubmit method handler, and replaces it with its own:
<form id="ctl01" onsubmit="javascript:return WebForm_OnSubmit();" action="UserProperties.aspx?userGuid=00112233-5544-4666-7777-8899aabbccdd" method="post">
So my own submit call is ignored. It appears that delving into WebForm_OnSubmit leads down a rabbit hole of ASP.NET WebForms validation infrastructure:
function WebForm_OnSubmit()
{
if (typeof(ValidatorOnSubmit) == "function" && ValidatorOnSubmit() == false)
return false;
return true;
}
which calls the function:
var Page_ValidationActive = false;
function ValidatorOnSubmit()
{
if (Page_ValidationActive) {
return ValidatorCommonOnSubmit();
}
else
{
return true;
}
}
which calls, which calls, which uses, which checks.
Just want HTML5 form validation with WebForms
The issue is that WebForms has a huge infrastructure for checking a form before submitting, and displaying errors to users through a standardized mechanism (and entire infrastructure that i do not use). It takes over the onsubmit event of forms, and it doesn't (necessarily) use an <input type="submit"> in the process.
What can i do to convince ASP.NET WebForms web-site to let the User-Agent validate the form?
See also
HTML validation and ASP.NET Webforms
How do I get Client-side validation of ASP.NET page to run?
Trigger standard HTML validation (form) without using submit button?
MSDN Magazine: Better Web Forms with HTML5 Forms
The web forms infrastructure deletes the handler because you attempt to add it directly on the element in the visual design environment, and web-forms is not designed to work this way.
Trapping the on-submit handler is easy, you just need to think a little creatively.
Whenever your trying to deal with anything like this in a web-forms environment, you have to learn to think about solutions that come after the web-forms page rendering pipeline. If you try to find a solution that works directly inside the web-forms engine, then web-forms will win every-time.
You can reproduce my exact test case here if you need too, or you can pick out the bits you need and re-use as required.
Step 1
Create a NEW web-forms project in C# (You can do it in VB if you want, but I'm putting my examples as C#), once your project has been created, right click on your application references, go into NuGet and install JQuery.
It is possible to do this with out JQuery, and just use native methods, but it's Sunday and I'm being lazy today :-)
Step 2
Add a new web-form to your project (using add new item) and call this 'Default.aspx', onto this form add some text, a text box a button and a label.
your code should look similar to:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="jsvalidation.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
Please enter your name :
<asp:TextBox ID="txtName" runat="server" data-jsname="username"></asp:TextBox>
<asp:Button ID="btnSubmit" runat="server" OnClick="btnSubmit_Click" Text="Submit" />
</div>
<asp:Label runat="server" ID="lblResult">...</asp:Label>
</form>
</body>
</html>
Then press F7 (or double click on the button) to go to the code behind editor for your form.
Step 3
Add your code for the button handler into your code behind. If your doing things exactly the same way as me, your code should look similar to this:
using System;
namespace jsvalidation
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{}
protected void btnSubmit_Click(object sender, EventArgs e)
{
if(Page.IsPostBack)
{
lblResult.Text = "Well hello there " + txtName.Text;
}
}
}
}
At this point you can test things work by pressing F5, and what ever you enter (or don't enter) in the text box, should appear with the message in the label below when you press the submit button.
Now we have a basic web-forms set up, we just need to intercept the form submit.
Step 4
As I said a moment ago, you need to think outside the web-forms box.
That means you need to run your intercept AFTER the page has been rendered and sent to the browser.
All of the JS that web-forms injects into the mix is usually executed before the page output is allowed to proceed, which means before the opening body tag.
In order to get your code to run after it, you need to put your script at the END of the html output so that it runs last.
add the following code just before the closing body tag, but AFTER the closing form tag
<script src="/Scripts/jquery-2.1.1.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('form').submit(function() {
var uninput = $('input[data-jsname="username"]');
var username = uninput.val();
if (username == "")
{
alert("Please supply a username!");
return false;
}
});
});
</script>
The more astute among you, will notice straight away that I'm not calling the HTML5 validation API, the reason is I'm trying to keep this example simple.
Where I check the username value is the important part.
All that matters is the anonymous function in which I perform the check returns true or false. (True is the default if you DON't return anything)
If you return a false, you'll prevent the post back taking place, thus allowing you to use whatever JS code you need to make the form fields change using the HTML5 validation API.
My personal preference is to use bootstrap (See the syncfusion free e-book range for my Bootstrap 2 book, and soon to be released Bootstrap 3 book), where you can use special markup and css classes in the framework such as "has-errors" to colour things appropriately.
As for the selection of the components using JQuery, well there's 2 things here you need to pay attention too.
1) You should NEVER have more than one form on a web-forms page, in fact if I remember correctly, your app will fail to compile if you do, or at the most it'll certainly throw an exception at run-time.
2) Beacuse you only ever have one form, then you can be very safe in making the asumption, that a generic 'form' selector in JQuery will only ever select your main form (Irrespective of the ID, name, lack or size thereof) and adding an onsubmit handler to it, will always attach this after web-forms has don'e it's thing.
3) Beacuse web-forms can (and usually does) mangle ID names, it's generally easier to use 'data attributes' to add custom bits to your tags. The actual ASP.NET js side validation does this exact same trick itself to allow JS code and .NET code to happily co-exist in the same page. There are ways to tell .NET how to name the ID's but generally you have to set lots of options in lot's of places, and it's very easy to miss one. Using 'data attributes' and the JQ attribute selector syntax is a much safer, easier to manage way of achieving the same thing.
Summary
It's not a difficult task, but I have to admit, it's not one that's immediately obvious if your not looking outside the fence that web-forms builds around you.
Web-forms is designed for rapid application development, primarily for devs used to the desktop win-forms model. While web-forms still has it's place these days, for new greenfield apps I would recommend looking at other options too, esp ones that build on the foundations that the new HTML5 specifications are trying hard to lay down and get right.
I'm a PHP/Rails developer and have inherited an ASP.NET application (and its maintenance). So I have a few simple questions.
1.) What's the makeup of a typical rendered(compiled?) HTML page in ASP.NET. That is, when a request is made what happens from the initial request to the time the HTML is displayed in the browser? I'm assuming some templates are combined and finally rendered but I'd like a more in-depth answer.
2.) I've been asked to remove a link from a Login form which is an aspx page. Looking at the aspx page itself it has an inherit statement, a link to the codebehind file, and links some other resources. Where do I actually remove the link from the Login page/template at? I've so far been unable to find exactly where the link is written so that I can remove it or comment it out.
Thank you!
That is, when a request is made what happens from the initial request
to the time the HTML is displayed in the browser?
I'd start learning about the ASP.Net Page Life Cycle.
I've so far been unable to find exactly where the link is written so
that I can remove it or comment it out.
I wouldn't do anything until you have at least a decent grasp of how ASP.Net works. It would be good to run through a few tutorials. ASP.Net has a nice Get Started section.
What's the makeup of a typical rendered(compiled?)
To give you a very simple instructions (trying) to help you fast understand it:
There is a page with the aspx tags, the asp.net is running the code behind and fill this tags with data.
After the filling with data on code behind, the asp.net is "running" the full page and if you have <% %> inside the aspx page, addition code runs that exist inside that.
This is a simple example:
public partial class Dokimes_StackOverFlow_Diafora : System.Web.UI.Page
{
public string cRenderMeAlso = "test";
protected void Page_Load(object sender, EventArgs e)
{
txtText.Text = "One Test";
}
}
<form id="form1" runat="server">
This will fill when the page is prepared
<asp:Literal runat="server" ID="txtText"></asp:Literal>
<br />
This will be render as the page reads out to send it to the browser
as php do
<%=cRenderMeAlso%>
</form>
Now in the place of the Literal control, you can have a full custom render control, that maybe a new complex part of a page with his elements and render.
Each page, master page, user control have a cycle of calls to help first pass all from Init() and prepare them, then pass all from Load(), and the other stage, giving the ability to initialize them in parallel - together.
Now, on PostBack the page have been keep some information's on ViewState that are posted together with the rest post data, and the Code behind use all that data to fill the controls. Also its fires on code behind any click event you have initialize on buttons and you can run some code there to do your work.
I've been asked to remove a link from a Login form
if you can not find that link is maybe on the standard login form that asp.net gives, the solution to that is to render the full template of the form, and remove it from there - but because there is the case to break the Login form, is better to not remove it and just hide it - because if you remove it and the code behind ask for it, it will throw an error - I mean for the standard asp.net forms login code that is part of the asp.net.
So if this is the case, render the login control as template (from design mode, do that on properties), see the link you search and ether make on code behind Link.Visible = false, ether remove it and delete on code behind all the reference on it.
I know that ASP.NET controls such as the button have the postback event model. And checking whether the page.IsValid is dependent on events and postback in order for the validation to kick in.
But what if I have a button using regular HTML inside my .aspx (and I don't want to use the asp.net button...please do not ask me why) yet still want to take advantage of calling Page.IsValid?
For example, lets say my .aspx page has 2 buttons:
<asp:ImageButton runat="server" ID="cmdPlaceOrder" OnClick="cmdPlaceOrder_Click" ImageUrl="images/someButton.gif" />
and that was there in the page, someone else had created that a while back. In the cmdPlaceOrder we check for Page.IsValid:
protected void cmdPlaceOrder_Click(object sender, EventArgs args)
{
if (!IsValid)
return;
... rest of logic
}
That's standard. Now what if I add a non-ASP.NET button like this in the .aspx page, used to place the order (but for a different kind of order seperate from the existing place order button above):
<img src="images/buttonPayGoogleCheckout.gif" alt="Pay with Google"/>
So on click of the hyperlink, the page posts back to itself (same url). I check the url for a querystring param flag that if set calls the method below that I created which is basically a similar method as the above but with a bit of different logic in it:
protected void PlaceGoogleCheckoutOrder()
{
if (!IsValid)
return;
... rest of logic here, but I can't get to it because there is no event model to allow IsValid to work
}
Obviously I'm not tying in an event model to this therefore once it hits the check for Page.IsValid it errors with the following message at runtime:
Page.IsValid cannot be called before validation has taken place. It should be queried in the event handler for a control that has CausesValidation=True and initiated the postback, or after a call to Page.Validate.
But I still want to be able to call this validation, the validation that's already been setup in our .aspx. I don't want to reinvent the wheel on this and I don't in this case want to use an ASP.NET based button (do not ask me why, I have my reasons and it's too long to get into that).
I want to know how I can still get that Page.IsValid check to work for a non-event driven button using . I'm not sure how to hook up an event to do so that still hooks in after the redirect and allows that code to still validate.
I tried adding Page.Validate(); inside my PlaceGoogleCheckoutOrder() method right before the check for Page.IsValid but I still get the same error.
After looking at MSDN on Page.Validate() (http://msdn.microsoft.com/en-us/library/0ke7bxeh.aspx) and it states "The validation group is determined by the control that posted the page to the server. If no validation group is specified, then no validation group is used."
So that's why nothing happened. So I'm not sure how to get ASP.NET in this case to know about the control (in this case my ) that posted the page to the server so that a validation group IS used. I guess I could add a runat="server" to my ...but doubt that's all I need to do here.
The trick with your normal button is that it never does a post back because your href attribute has some other url there. If you want to check if the page is valid, you have to post back to your page class first to make that check and then redirect from there.
What you can do to make this happen with a normal anchor (<a >) tag is process that anchor's onclick event in javascript, do any client side work you want, and then call the __doPostBack() javascript function.
You can see an example of how to call __doPostBack() on msdn here:
http://msdn.microsoft.com/en-us/library/aa720099(VS.71).aspx
It's from .Net 1.1, but still accurate.
Sometimes my ajax request is so fast that the user does not realize a ajax call was made. So I would like to force the UpdateProgress control to display for a minimum about of time, even if the ajax request has finsihed.
For example:
John browses the site and the ajax call takes 2 seconds to complete. I only want the UpdateProgress control to display for those 2 seconds.
Mary also browses the site but the ajax call takes > 0.5 seconds. So I want to display the UpdateProgress control for at least 1 full second.
Does anybody have any ideas on how to do this?
Edit
I have discovered that the AjaxLoadingPanel control offered by Telerik has this ability. It has a property called MinDisplayTime that does this very thing. It would be nice to know how to do this using the standard (Free) asp.net ajax controls.
Thanks
I had the same problem working on an intranet site where the UpdatePanel contents changed so quickly that I couldn't tell if an update had happened without debugging, or checking the database.
The way I tackled this problem was to let the UpdatePanel do its thing as before, but use an UpdatePanelAnimationExtender to briefly flash a change of background colour, for example, before fading back to normal, giving the user the impression that an action has happened. If this happens quickly, say 0.3 of a second, and an appropriate 'action' colour is chosen, this can be very effective.
<ajaxToolkit:UpdatePanelAnimationExtender ID="myUpdatePanelExtender" runat="server" TargetControlID="myUpdatePanel">
<Animations>
<OnUpdating> ... </OnUpdating>
<OnUpdated> ... </OnUpdated>
</Animations>
</ajaxToolkit:UpdatePanelAnimationExtender>
You'll need to get the AJAX Control Toolkit, but if you're doing Asp.Net AJAX work, you'll be better off having it, if you don't already.
For what to place within the <OnUpdating> tag etc., see the Asp.Net Ajax Control Toolkit site page 'Using Animations'.
protected void Button1_Click(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(3000);
Label1.Text = DateTime.Now.ToString();
}
http://www.asp.net/Ajax/Documentation/Live/tutorials/ProgrammingUpdateProgress.aspx
you could work around the latter by keeping track of the time your server side code takes to execute (or perhaps even better by measuring the ajax execution time, but that's more tricky)
the idea is this
long startTime = System.DateTime.Now.Ticks;
//this process can take some time, especially when executed for the first time
//it gets data and binds it to a control
FetchData();
//if this takes not enough time, add an extra second to allow the UpdateProgress to display
if (TimeSpan.FromTicks(DateTime.Now.Ticks - startTime).TotalSeconds < 1)
System.Threading.Thread.Sleep(1000);