Skip to main content

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; }
    }
}

Run Demo: Validation

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.