Validate User Input
- 6 minutes to read
Input validation helps reduce security threats/external attack vectors. Always validate user input to mitigate risks, unauthorized data manipulation, and injection attacks.
Follow the steps below to validate user input when using DevExpress ASP.NET MVC Extensions.
#1. Add Validation Attributes
Apply validation attributes to model properties and specify validation rules for server-side/client-side validation.
When you bind an input form to a model, you allow the controller to edit all properties associated with the model. Even if the form does not display an editor bound to a property, a threat actor can forge a request to change this property value. Use the Bind
attribute’s Exclude
property to prevent a model property from being editing.
The following example specifies validation attributes and prevents the Salary
property value from being editing:
using System.ComponentModel.DataAnnotations;
namespace Validation.Models {
[Bind(Exclude = "Salary")]
public class Employee {
[Required(ErrorMessage = "First name is required")]
[StringLength(15, ErrorMessage = "Must be under 15 characters")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Email is required")]
[EmailAddress(ErrorMessage = "Email is invalid")]
public string Email { get; set; }
[Required(ErrorMessage = "Age is required")]
public int Age { get; set; }
public double Salary { get; set; }
}
}
#2. Implement Server-Side Validation
#2.1. Validate Bound Editors
In an action method, use the IsValid property to check model values against validation attributes:
public ActionResult Index(EmployeeItem employeeItem) {
if (!ModelState.IsValid) {
return Page();
}
// ...
}
#2.2. Validate Unbound Editors
You can implement custom logic to validate editor values not bound to model properties. To access an editor’s value on the server, use a controller action parameter with the same name as the editor. If the value is invalid, call the AddModelError method to register a validation error. To display error messages, add custom markup to the view.
The following code snippet validates the value of a text box extension not bound to a model:
public ActionResult Index(Employee userProfile, string UserPassword) {
if (ModelState.IsValid) {
if (!IsUserPasswordCorrect(UserPassword)) {
ModelState.AddModelError(nameof(UserPassword), "Password is not correct!");
}
else {
// ...
}
}
return View("Index", userProfile);
}
private bool IsUserPasswordCorrect(string value) {
// Implement custom validation here
}
@model Employee
@using (Html.BeginForm()){
var errorState = ViewContext.ViewData.ModelState["UserPassword"];
if (errorState != null && errorState.Errors.Count > 0) {
@Html.DevExpress().Label(s => {
s.Text = errorState.Errors[0].ErrorMessage;
s.ControlStyle.CssClass = "general-error-text";
}).GetHtml()
}
@Html.DevExpress().TextBox(settings => {
settings.Name = "UserPassword";
settings.Properties.Password = true;
settings.Properties.Caption = "Password";
}).GetHtml()
<!-- ... -->
@Html.DevExpress().Button(settings => {
settings.Name = "UpdateButton";
settings.Text = "Save Changes";
settings.UseSubmitBehavior = true;
}).GetHtml()
}
#2.3. Validate List Editors
A threat actor can submit a forged request to select an unavailable list value (values displayed within controls such as the ComboBox, ListBox, and/or TagBox).
To prevent request forgery, check if the selected list item belongs to the allowed range:
[HttpGet]
public ActionResult Index() {
return View("Index", ProductItems.GetAvailableForUserList()[0].Id);
}
[HttpPost]
public ActionResult Index(int productItemId) {
if (ModelState.IsValid) {
if (ProductItems.GetAvailableForUserList().FindIndex(i => i.Id == productItemId) == -1) {
ModelState.AddModelError("", "Invalid input.");
}
else {
// ...
}
}
return View("Index", productItemId);
}
@model int
@using (Html.BeginForm()) {
@Html.DevExpress().ComboBox(settings => {
settings.Name = "productItemId";
settings.Width = 180;
settings.Properties.Caption = "Choose a gift";
settings.Properties.TextField = "ProductName";
settings.Properties.ValueField = "Id";
settings.Properties.ValueType = typeof(int);
}).BindList(ProductItems.GetAvailableForUserList()).Bind(Model).GetHtml()
@Html.DevExpress().Button(settings => {
settings.Name = "UpdateButton";
settings.Text = "Update";
settings.UseSubmitBehavior = true;
}).GetHtml()
}
public class ProductItem {
public int Id { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
}
public static class ProductItems {
static List<ProductItem> items = new List<ProductItem>();
static ProductItems() {
items.Add(new ProductItem { Id = 1, Price = 10, ProductName = "Small headphones" });
items.Add(new ProductItem { Id = 2, Price = 100, ProductName = "Hi-fi headphones" });
items.Add(new ProductItem { Id = 3, Price = 500, ProductName = "Mobile phone" });
items.Add(new ProductItem { Id = 4, Price = 1000, ProductName = "Laptop" });
}
public static List<ProductItem> GetAllItems() {
return items;
}
public static List<ProductItem> GetAvailableForUserList() {
int userBonusLimit = 100;
return GetAllItems().FindAll(i => i.Price <= userBonusLimit);
}
}
#3. Implement Client-Side Validation
DevExpress ASP.NET MVC data editors support the following client-side validation options:
- Model-Based Validation
- Uses validation attributes applied to model properties.
- Unobtrusive Validation
- Combines validation attribute logic with client jQuery validation.
- JQuery Validation
- Uses a jQuery validation plugin without relying on ASP.NET MVC resources.
Important
You should always use client-side validation in conjunction with server-side validation. Client-side validation is designed to improve usability and optimize server load. If a threat actor manages to bypass client-side validation and sends an invalid value to the server, server-side validation helps prevent a threat actor from submitting invalid values.
The following example enables server-side and client-side unobtrusive validation:
@model UnobtrusiveValidationData
@{
Html.EnableClientValidation();
Html.EnableUnobtrusiveJavaScript();
}
@section AdditionalResources {
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
}
@using (Html.BeginForm("UnobtrusiveValidation", "Common", FormMethod.Post, new { id = "validationForm"})) {
@Html.DevExpress().FormLayout(settings => {
settings.Name = "RegistrationFormLayout";
settings.Width = Unit.Percentage(100);
settings.ControlStyle.CssClass = "formLayoutMaxWidth";
settings.UseDefaultPaddings = false;
settings.Items.Add(model => model.Name, CommonDemoHelper.FormLayoutItemSettingsMethod);
settings.Items.Add(model => model.Age, CommonDemoHelper.FormLayoutItemSettingsMethod);
settings.Items.Add(model => model.Phone, CommonDemoHelper.FormLayoutItemSettingsMethod);
settings.Items.Add(model => model.Email, CommonDemoHelper.FormLayoutItemSettingsMethod);
settings.Items.Add(itemSettings => {
itemSettings.ShowCaption = DefaultBoolean.False;
itemSettings.HorizontalAlign = FormLayoutHorizontalAlign.Right;
itemSettings.SetNestedContent(() => {
Html.DevExpress().Button(btnSettings => {
btnSettings.Name = "btnUpdate";
btnSettings.Text = "Send";
btnSettings.ControlStyle.CssClass = "flButton";
btnSettings.UseSubmitBehavior = true;
}).Render();
});
});
}).GetHtml();
}
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using DevExpress.Web.Mvc;
namespace DevExpress.Web.Demos {
public class UnobtrusiveValidationData {
[Display(Name = "Name:")]
[Required(ErrorMessage = "Name is required")]
[RegularExpression(@"(\S)+", ErrorMessage = "White space is not allowed")]
public string Name { get; set; }
[Display(Name = "Email:")]
[Required(ErrorMessage = "Email is required")]
[RegularExpression("\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*", ErrorMessage = "Email is invalid")]
public string Email { get; set; }
[Display(Name = "Phone:")]
[Mask("+1 (999) 000-0000", IncludeLiterals = MaskIncludeLiteralsMode.DecimalSymbol, PromptChar = '_', ErrorMessage = "Phone number is invalid")]
public string Phone { get; set; }
[Display(Name = "Age:")]
[Required(ErrorMessage = "Age is required")]
[Range(18, 100, ErrorMessage = "Must be between 18 and 100")]
public int? Age { get; set; }
}
}
#See Also
Read the following help topics for additional information:
Input validation does not protect your application against cross-site scripting (XSS) attacks. Refer to the following help topic to protect your web app from XSS attacks: HTML Encoding.