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.