I worked in ASP.net MVC for the past 1.5 years. Where I used enterprise application block for server side validation. I loved the way that viewmodels bind to the view's controls and validation working in that way. But now I am working in a project which is purely webforms without MVC.
Here jQuery is used for client side validation and there is no server side validation at all. I was stressing the importance of server side validation and was planning to use enterprise library for the same.
For some reasons(may be due to the fact I worked in ASP.NET MVC recently) I am getting stuck up at a point.
In my webforms application will my validations necessarily contain the same logic for both client side and server side. Or in general what are the best practices for coding validations in ASP .net web forms?
I wanted to follow the widely accepted practise. Also are there any new concepts in for webforms validation which are better than enterprise library. samples should help me understand.
I think you'd want to validate more rather than less...server-side checks in addition to client-side checks are a good idea (especially for an externally facing application). You want to be especially careful with textboxes. Remember users can shut off scripts (and totally circumvent your client-side scripts).
It's easy enough to implement server-side validation with Custom Validator controls that fire the ServerValidate event.
I haven't worked with the enterprise library, so I can't answer anything about the validation routines it provides.
You can use CustomValidators for anything, they are my favorite!
If you use HTML5 attributes like required="required" you get client side feedback for free.
You can utilize them also to perform server side validation like so:
<asp:ValidationSummary runat="server" id="vSummary" />
<asp:TextBox runat="server" id="txtReq" required="required" />
<asp:DropDownList runat="server" id="ddlReq" required="required">
<asp:ListItem text="..." value="" />
<asp:ListItem text="Yes" value="1" />
<asp:ListItem text="No" value="0" />
</asp:DropDownList>
<asp:Button runat="server" id="cmdSubmit" text="Submit" />
Code Behind Functions:
private void buildRequiredWebControls(List<WebControl> lst, Control c)
{
if (c is WebControl)
if (String.Compare((c as WebControl).Attributes["required"] ?? String.Empty, "required", true) == 0)
lst.Add((c as WebControl));
foreach (Control ch in c.Controls)
buildRequiredWebControls(lst, ch);
}
/* --------------------------------------------- */
private Boolean addAllFieldsRequired(List<WebControl> controls)
{
foreach (WebControl w in controls)
{
if (w as TextBox != null)
if (String.IsNullOrWhiteSpace((w as TextBox).Text)) return false;
if (w as DropDownList != null)
if (String.IsNullOrWhiteSpace((w as DropDownList).SelectedValue)) return false;
}
return true;
}
/* --------------------------------------------- */
private void cmdSubmit_Click(object sender, EventArgs e)
{
vSummary.ValidationGroup = "StackOverflow";
Page.Validate("StackOverflow");
List<WebControl> requiredFields = new List<WebControl>();
this.buildRequiredWebControls(requiredFields, this);
Page.Validators.Add(new CustomValidator()
{
IsValid = this.addAllFieldsRequired(requiredFields),
ErrorMessage = "Please complete all required fields.",
ValidationGroup = "StackOverflow"
});
if (Page.IsValid)
{
//Good to Go on Required Fields
}
}
Beats the very monotonous alternative, which is to manually insert them into the html after every control:
<asp:ValidationSummary runat="server" id="vSummary" ValidationGroup="StackOverflow" />
<asp:TextBox runat="server" id="txtReq" required="required" />
<asp:RequiredFieldValidator runat="server" ControlToValidate="txtReq" ErrorMessage="Please Fill Out Required Field" Text="*" ValidationGroup="StackOverflow" />
<asp:DropDownList runat="server" id="ddlReq" required="required">
<asp:ListItem text="..." value="" />
<asp:ListItem text="Yes" value="1" />
<asp:ListItem text="No" value="0" />
</asp:DropDownList>
<asp:RequiredFieldValidator runat="server" ControlToValidate="ddlReq" ErrorMessage="Please Fill Out Required Field" Text="*" ValidationGroup="StackOverflow" />
<asp:Button runat="server" id="cmdSubmit" text="Submit" />
Related
I need to be able to apply multiple validation groups to some of the controls on my page, because for each of the three submit "modes", there is a different set of required fields.
I have three submit buttons, and each one has its own validation group (reject, approve, and save). "Reject" doesn't actually have any required fields currently, but may in the future. "Save" only requires "first name" and "last name", while "approve" requires "first name", "last name", as well as "group name".
I tried simply putting multiple validation groups in the attribute value (as in the code below), but the validation didn't fire at all.
I also tried not specifying a group at all in an attempt to just have the validation always fire, but again again, it didn't fire at all.
<div>
<label for='<%= txt_FirstName.ClientID %>'>First Name</label>
<asp:TextBox ID="txt_FirstName" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="rfv_FirstName" runat="server" text="Required"
ControlToValidate="txt_FirstName" ValidationGroup="approve save">
</asp:RequiredFieldValidator>
</div>
<div>
<label for='<%= txt_LastName.ClientID %>'>Last Name</label>
<asp:TextBox ID="txt_LastName" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="rfv_LastName" runat="server" text="Required"
ControlToValidate="txt_LastName" ValidationGroup="approve save">
</asp:RequiredFieldValidator>
</div>
<div>
<label for='<%= ddl_GroupName.ClientID %>'>Group Name</label>
<asp:DropDownList ID="ddl_GroupName" runat="server"></asp:DropDownList>
<asp:RequiredFieldValidator ID="rfv_GroupName" runat="server" text="Required"
ControlToValidate="ddl_GroupName" ValidationGroup="approve">
</asp:RequiredFieldValidator>
</div>
<div>
<asp:Button ID="btn_Reject" runat="server" Text="Reject"
ValidationGroup="reject" OnClick="btn_Reject_Click" />
<asp:Button ID="btn_Approve" runat="server" Text="Approve"
ValidationGroup="approve" OnClick="btn_Approve_Click" />
<asp:Button ID="btn_Save" runat="server" Text="Save"
ValidationGroup="save" OnClick="btn_Save_Click" />
</div>
I'm certain I can achieve the desired effect with some additional code, or duplicated validator controls for each validation group, but I was hoping the webforms framework would have a simpler, built-in way to do this.
UPDATE (2018-01-02):
The question "Is it possible to assign Multiple Validation Groups to a single Validation Control?" is a special case of my own. It has a single control in multiple groups, whereas I have multiple controls with multiple overlapping groups. Additionally, the answer proposes having a validation control for each input/group combination. This is a terrible solution because, among other things, it violates the DRY (don't repeat yourself) programming principle and makes maintenance/enhancements more difficult.
The above also links the question "How to validate against Multiple validation groups?". That question is a completely different issue (validation groups within validation groups), and its answer proposes custom javascript that would need to be written for every submit button, on every page that requires multiple validation groups.
I am looking for a general case solution that I can write/implement once and apply to the entirety of the web application, and thus I do not consider my question a duplicate of the above mentioned questions.
UPDATE (2018-01-03):
Between other questions/articles I've encountered while researching my problem, as well as the gracious assistance of a.bajorinas and Sunil, I've established that it is not possible to have multiple validation groups on a validation control out-of-the-box, and custom code will be required to achieve the desired effect. I've updated the question accordingly, to assist anyone looking to achieve this in the future.
I'll be testing a solution that incorporates elements from both a.bajorinas' and Sunil's answers and will hopefully be able to provide an update afterward.
The only thing I can think of for your desired use case, is setting ValidationGroup for your validators in code behind on button click, keep in mind that this would first perform a postback and then validate the controls which might not be ok for you.
protected void btn_save_click(object sender, EventArgs e){
rfv_FirstName.ValidationGroup = "save";
Page.Validate("save");
if (Page.IsValid)
{
//logic if validators pass
}
}
This will let you reuse validators for multiple groups.
Also in my linked posted, one of the comments to the selected answer is "It is not possible to assign multiple groups to a validation control"
ASP.Net does not support multiple validation groups. However, with a little bit of JavaScript code you can make it support multiple validation groups.
What you need to do is override the standard JavaScript function of IsValidationGroupMatch. This function is part of the standard validation library in ASP.Net.
At bottom of your aspx page, just add the script tag given below. Note that this script is just before closing form, body and html tags, which is important when overriding a JavaScript function.
<script type="text/javascript">
window["IsValidationGroupMatch"] = function (control, validationGroup) {
if ((typeof (validationGroup) == "undefined") || (validationGroup == null)) {
return true;
}
var controlGroup = "";
var isGroupContained = false;
if (typeof (control.validationGroup) == "string") {
controlGroup = control.validationGroup;
var controlGroupArray = [];
if (validationGroup.indexOf(",") > -1) {
controlGroupArray = validationGroup.split(",");// validationGroup.split(",");
}
for (var i = 0; i < controlGroupArray.length; i++) {
if (controlGroupArray[i].trim() == controlGroup.trim()) {
isGroupContained = true;
}
}
}
return (controlGroup == validationGroup || isGroupContained);
}
</script>
</form>
</body>
</html>
Once you do this, then you can add a comma delimited list of validation groups for the button that validates multiple validation groups as in example below. In example below a button with id of btnMultipleValidationGroups is validating group1 as well as group2.
<div>
TextBox1 :
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ErrorMessage="TextBox1 needs input" ControlToValidate="TextBox1" ForeColor="Red" ValidationGroup="group1"></asp:RequiredFieldValidator>
<br />
<br />
TextBox2 :
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ErrorMessage="TextBox2 needs input" ControlToValidate="TextBox2" ForeColor="Red" ValidationGroup="group2"></asp:RequiredFieldValidator>
<br />
<br />
TextBox3 :
<asp:TextBox ID="TextBox3" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator3" runat="server" ErrorMessage="TextBox3 needs input" ControlToValidate="TextBox3" ForeColor="Red" ValidationGroup="group3"></asp:RequiredFieldValidator>
<br />
<br />
</div>
<asp:Button ID="btnMultipleValidationGroups" runat="server" Text="Validate group1 and group2" ValidationGroup="group1,group2" OnClick="btnMultipleValidationGroups_Click" />
<asp:Button ID="btnGroup1" runat="server" Text="Validate only group1" ValidationGroup="group1" OnClick="btnGroup1_Click" />
<asp:Button ID="btnGroup2" runat="server" Text="Validate only group2" ValidationGroup="group2" OnClick="btnGroup2_Click" />
I am using asp.net validation controls for validating user input. What i want is to change the label text with the error message generated by the validation control.
<asp:Label ID="Label1" runat="server" Text="Whats your name"></asp:Label>
<asp:TextBox ID="nameB" Width="322px" Height="30px" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" ValidationGroup="business" runat="server" ErrorMessage="Please tell us your name." ControlToValidate="nameBuisness" CssClass="errMsg" Display="Dynamic"></asp:RequiredFieldValidator>
Thank you.
One way is to handle the submit-button's OnClientClick-event and call a javascript function like:
<script type="text/javascript">
function displayValidationResult() {
// Do nothing if client validation is not available
if (typeof (Page_Validators) == "undefined") return;
var LblName = document.getElementById('LblName');
var RequiredName = document.getElementById('RequiredName');
ValidatorValidate(RequiredName);
if (!RequiredName.isvalid) {
LblName.innerText = RequiredName.errormessage;
}
}
</script>
<asp:Label ID="LblName" runat="server" Text="Whats your name"></asp:Label>
<asp:TextBox ID="TxtNameBusiness" Width="322px" Height="30px" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredName" ValidationGroup="business"
runat="server" ErrorMessage="Please tell us your name." ControlToValidate="TxtNameBusiness"
CssClass="errMsg" Display="None"></asp:RequiredFieldValidator>
<asp:Button ID="BtnSumbit" runat="server" Text="Submit"
OnClientClick="displayValidationResult()" ValidationGroup="business" />
I've used some of the few ASP.NET client validation methods available. I've also renamed your controls to somewhat more meaningful and added a submit-button.
ASP.NET Validation in Depth
If your requirement is that you want to do this validation using the built-in ASP.Net validation controls, then you will need to use the ASP.Net CustomValidator control. This cannot be done using the ASP.Net RequiredFieldValidator control. To do the validation you specified, put a CustomValidator control on on your page so that the markup looks like this:
<asp:Label ID="Label1" runat="server" Text="Whats your name"></asp:Label>
<asp:TextBox ID="nameB" Width="322px" Height="30px" runat="server"></asp:TextBox>
<asp:CustomValidator ID="CustValidator" runat="server" ControlToValidate="nameB"
ValidateEmptyText="true" ErrorMessage="Please tell us your name."
onservervalidate="CustValidator_ServerValidate" Display="none" >**</asp:CustomValidator>
<asp:Button ID="btnSubmit" runat="server" Text="Submit" onclick="btnSubmit_Click" CausesValidation="true" />
You then need to write your custom validator. The server-side validation code looks like this:
protected void CustValidator_ServerValidate(object source, ServerValidateEventArgs args)
{
if (string.IsNullOrWhiteSpace(args.Value))
{
Label1.Text = CustValidator.ErrorMessage;
args.IsValid = false;
}
else
{
Label1.Text = "Whats your name";
args.IsValid = true;
}
}
This custom validator will give you the behavior you desire.
In general when you use a CustomValidator control, you should do both server-side validation (for security) and client-side validation (for a better UI experience). To get client-side validation, you will need to write a client-side function in javascript and/or jQuery to do similar validation and then assign it to the ClientValidationFunction of the custom validator.
Further information on the CustomValidator control can be found in the MSDN documentation.
I have 20 RadioButtonLists on a page.
I need to create a validation method to ensure that at least one of these RadioButtonLists has an item selected.
What kind of validation would I need to use for this?
EDIT: Updated question based on comments and clarification.
If you are validating against multiple RadioButtonLists then you need to use a CustomValidator and implement the server side check.
Here is some test markup:
<asp:RadioButtonList ID="rblstTest1" runat="server" ValidationGroup="Test">
<asp:ListItem Text="Test 1" Value="1" />
<asp:ListItem Text="Test 2" Value="2" />
<asp:ListItem Text="Test 3" Value="3" />
</asp:RadioButtonList>
<br /><br />
<asp:RadioButtonList ID="rblstTest2" runat="server" ValidationGroup="Test">
<asp:ListItem Text="Test 1" Value="1" />
<asp:ListItem Text="Test 2" Value="2" />
<asp:ListItem Text="Test 3" Value="3" />
</asp:RadioButtonList><br />
<asp:Button ID="btnTestRb" runat="server" ValidationGroup="Test" Text="Test RBL"
OnClick="btnTestRb_Click" />
<asp:CustomValidator runat="server" ValidationGroup="Test" ID="cvTest"
ControlToValidate="rblstTest1" OnServerValidate="cvTest_ServerValidate"
ValidateEmptyText="true" Enabled="true" display="Dynamic" SetFocusOnError="true"
ErrorMessage="You must select at least one item." />
Use the following extension method to find all the RadioButtonList controls (Source):
static class ControlExtension
{
public static IEnumerable<Control> GetAllControls(this Control parent)
{
foreach (Control control in parent.Controls)
{
yield return control;
foreach (Control descendant in control.GetAllControls())
{
yield return descendant;
}
}
}
}
Then implement the server side CustomValidator check:
protected void cvTest_ServerValidate(object sender, ServerValidateEventArgs e)
{
int count = this.GetAllControls().OfType<RadioButtonList>().
Where(lst => lst.SelectedItem != null).Count();
e.IsValid = (count > 0);
}
I have tested the above example and it seems to do exactly what you need. You should be able to switch it to VB pretty easily. Hope this solves your problem.
You could set a default for your RadioButtonList which would mean a user could never not select an option and you would not need all the validation code
<asp:RadioButtonList ID="RadioButtonList1" runat="server">
<asp:ListItem Selected="True">Never</asp:ListItem>
<asp:ListItem>Twice A Week</asp:ListItem>
<asp:ListItem>Every Day Baby!</asp:ListItem>
</asp:RadioButtonList>
EDIT
As pointed out in a comment below this would not be sufficient by itself as a means of validation. It is good practice to validate all user input on the server-side also.
I use an extension method that works for ListControls
public static bool IsAnyItemSelected(this ListControl input) { return input.Items.Cast<ListItem>().Aggregate(false, (current, listItem) => current | listItem.Selected); }
I have the following asp.net code:
<asp:TextBox CssClass="mf" runat="server" ID="mail1" Width="270" />
<asp:RequiredFieldValidator ID="rfv1"
runat="server"
ControlToValidate="mail1"
Display="Dynamic" />
Now I want to show an image (or text) if the validation passes on the fly (no postback)
A jQuery solution is also fine by me, but that wouldn't be so safe if javascript is disabled.
Without relying on an external framework you can tap into the ASP.Net Client Side Validation framework to handle extended/advanced functionality. In this way you are working with the framework, augmenting it instead of replacing it.
A tiny bit of JavaScript is all that is needed to enable the behavior you want.
<asp:TextBox CssClass="mf" runat="server" ID="mail1" Width="270" OnChange="showValidationImage();" />
<asp:RequiredFieldValidator ID="rfv1"
runat="server"
ControlToValidate="mail1" ClientIDMode="Static"
Display="Dynamic" >
<img src="../Image/fail.jpg" />
</asp:RequiredFieldValidator>
<img id="imgPass" src="../Image/pass.jpg" style="visibility:hidden" />
<script language="javascript">
// This function will be called whenever the textbox changes
// and effectively hide or show the image based on the state of
// the client side validation.
function showValidationImage() {
if (typeof (Page_Validators) == "undefined") return;
// isvalid is a property that is part of the ASP.Net
// client side validation framework
imgPass.style.visibility = rfv1.isvalid ? "visible" : "hidden";
}
</script>
<br />
<asp:Button ID="btnSubmit" runat="server" Text="Submit" />
Notice here that I am using a feature of ASP.Net 4 with ClientIDMode="Static" if you are not using .Net 4 then you will need to use the standard <%= rfv1.ClientID %> server side include to get at the client id inside your script.
I have the following very simple form:
<asp:UpdatePanel ID="ClaimRewardsForm" runat="server">
<ContentTemplate>
<span class="largeBold">Select jacket weight:</span><br />
<asp:RadioButtonList runat="server" ID="JacketWeight">
<asp:ListItem Value="Lightweight" Text="Lightweight (fleece)" />
<asp:ListItem value="Heavyweight" Text="Heavyweight (cotton)" />
</asp:RadioButtonList>
<br />
<span class="largeBold">Select size:</span><br />
(Men's sizes only)<br />
<asp:DropDownList ID="JacketSize" runat="server">
<asp:ListItem Value="Small" Text="Small" />
<asp:ListItem Value="Medium" Text="Medium" />
<asp:ListItem Value="Large" Text="Large" />
</asp:DropDownList><br />
<br />
<asp:ImageButton ID="SubmitButton" runat="server" ImageUrl = "~/Content/Images/submitButton.png" onclick="SubmitButton_Click" />
</ContentTemplate>
</asp:UpdatePanel>
In my button's click handler, I have:
protected void SubmitButton_Click(object sender, EventArgs e)
{
if (IsValid)
{
using (var work = UnitOfWorkFactory.Create())
{
var id = new Guid(Session["id"].ToString());
var account = UserAccounts.Get(id);
if (account == null)
throw new Exception("Invalid user account id.");
account.RewardInfo.Clear();
account.RewardInfo.Add(new RewardInfo()
{
UserAccount = account,
JacketWeight = JacketWeight.SelectedValue,
JacketSize = JacketSize.SelectedValue
});
work.Commit();
}
//ClaimRewardsForm.Update();
//ScriptManager.RegisterStartupScript(this, GetType(),
// "confirmation", "ClaimRewards.showConfirmation();", true);
}
}
I'm not modifying the form fields in any way whatsoever, but I'm still getting the following error:
505|error|500|Invalid postback or callback argument. Event validation is enabled using <pages enableEventValidation="true"/> in configuration or <%# Page EnableEventValidation="true" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.|
Since I'm not modifying teh controls at all during the post back, I cannot for the life of me figure out why it's acting as if I had. Any thoughts?
This guy here is your culprit. You'll have to figure out what you are posting to cause it to block the request or disable EventValidation if you understand the security risks.
I don't see anything in your posted code but <> in option values messes with it for sure.
Stupid me. I'm using a DropDownList adapter to allow for optiongroup elements within my list but that was causing an invalid postback because it modifies the elements in the list behind the scenes without registering the modified values for event validation. I modified the adapter to perform the registration and it works fine now.