Validation

Validation of data is of fundamental importance in CRUD applications. You need to be sure that the data that the client is sending you is what you expect! This is not just for security reasons, protecting your data from misuse and intentional corruption, but also simply from unintentional mistakes in submitting data. Strings limited to a certain length, dates in a particular format and required fields are all common uses of data validation.

The Editor .NET libraries provide two different validation methods:

  • Field based, where each individual value submitted is independently validated: Field.Validator().
  • Global validation, where the data submitted by the client-side can be validated as a whole: Editor.Validator().

Field validation is the one you will most commonly work with - for example checking that an e-mail address field actually contains an e-mail address, and Editor provides a number of ready to use validators for the most common data types as well as the ability to specify your own. Global validation can be useful when checking dependencies between fields and conflicts in the existing data set.

Field validation

The Editor .NET libraries provide a Validator() method for the Field class and a number of pre-built validation methods in the Validation class. Each of these validation methods returns a delegate which is executed when required to validate submitted data.

Validation options

Each validation method provided by the Validation class can optionally accept parameters to tell it how to validate data (for example the MinLen method will accept an integer to indicate the minimum length of an acceptable string), but all optionally accept a ValidationOpts class instance. This class defines a number of options that are shared between all validation methods.

The ValidationOpts class has three parameters that can be used to alter the validation behaviour:

  • Empty: (bool) How to handle empty data (i.e. a zero length string):
    • true (default) - Allow the input for the field to be zero length
    • false - Disallow zero length inputs
  • Message: (string) the error message to show if the validation fails. This is simply "Input not valid" by default, so you will likely want to customise this to suit your needs.
  • Optional: (bool) Require the field to be submitted or not. This option can be particularly useful in Editor as Editor will not set a value for fields which have not been submitted - giving the ability to submit just a partial list of options.
    • true (default) - The field does not need to be be in the list of parameters sent by the client.
    • false - The field must be included in the data submitted by the client.

Multiple validators

It can often be useful to use multiple validators together, for example to confirm that an input string is less than a certain number of characters and also that it is unique in the database. Multiple validators can be added to a field simply by calling the Field.Validator() method multiple times. Rather than overwriting the previous validator it will in fact add them together. They are run in the sequence they were added, and all validators must pass for the data to be accepted as valid.

As an example consider the following code which will check the min and max length of the data, and also that it is unique:

new Field( 'stock_name' )
    .Validator( Validation.MinLen( 10 ) )
    .Validator( Validation.MaxLen( 12 ) )
    .Validator( Validation.Unique() );

Ready to use field validators

The Validation class in the Editor .NET libraries has a number of methods which can be used to perform validation very quickly an easily. These are:

Basic

  • None( ValidationOpts cfg=null ) - No validation is performed
  • Basic(ValidationOpts cfg=null) - Basic validation - only the validation provided by ValidationOpts is performed
  • Required(ValidationOpts cfg=null) - The field must be submitted and the data must not be zero length. Note that Editor has the option of not submitting all fields (for example when inline editing), so the NotEmpty() validator is recommended over this one.
  • NotEmpty(ValidationOpts cfg=null) - The field need not be submitted, but if it is, it cannot contain zero length data
  • Boolean(ValidationOpts cfg=null) - Check that boolean data was submitted (including 1, true on, yes, 0, false, off and no)

Numbers

  • Numeric(ValidationOpts cfg=null, string culture="en-US") - Check that any input is numeric.
  • MinNum(Decimal min, string culture="en-US", ValidationOpts cfg=null) - Numeric input is greater than or equal to the given number
  • MaxNum(Decimal max, string culture="en-US", ValidationOpts cfg=null) - Numeric input is less than or equal to the given number
  • MinMaxNum(Decimal min, Decimal max, string culture="en-US", ValidationOpts cfg=null) - Numeric input is within the given range (inclusive)

Strings

  • Email(ValidationOpts cfg=null) - Validate an input as an e-mail address.
  • Ip(ValidationOpts cfg=null) - Validate as an IP address.
  • MinLen(int min, ValidationOpts cfg=null) - Validate a string has a minimum length.
  • MaxLen(int max, ValidationOpts cfg=null) - Validate a string does not exceed a maximum length.
  • MinMaxLen(int min, int max, ValidationOpts cfg=null) - Validate a string has a given length in a range
  • NoTags(ValidationOpts cfg=null) - Don't allow HTML tags
  • Url(ValidationOpts cfg=null) - Validate as an URL address.
  • Values(ValidationOpts cfg=null, IEnumerable<object> = null) - Allow only values which have been specified in an array of options. This could be useful if you wish to have free-form input or event a select list, and want to confirm that the value submitted is within a given data set (see also the DbValues() method if valid values are stored in a database). Note that the values given in the IEnumerable are checked against the submitted data as case-sensitive data (i.e. `"A" != "a").
  • Xss(ValidationOpts cfg=null) - Check to see if the input could contain an XSS attack. This used the Field's XSS formatting function to determine if the input string needs to be formatted or not.

Date / time

  • DateFormat(string format, ValidationOpts cfg=null) - Check that a valid date input is given

Database

  • DbValues(ValidationOpts cfg=null, string column = null, string table = null, Database db = null, IEnumerable<object> valid = null) - Allow only a value that is present in a database column. This is specifically designed for use with joined tables (i.e. ensure that the reference row is present before using it), but it could potentially also be used in other situations where referential integrity is required (Requires Editor 1.5.4 or newer).
  • Unique(ValidationOpts cfg=null, string column = null, string table = null, Database db = null) - Ensure that the data submitted is unique in the table's column
  • Unique<T>(ValidationOpts cfg=null, string column = null, string table = null, Database db = null) - As above but gives typing information for the comparison that the database server should perform. You might need to set this to use a string type for example.

One-to-many (Mjoin)

Note that these methods are for use with the Mjoin.Validator() method (Editor 1.9 and newer). Please see the Mjoin documentation for more details.

  • MjoinMinCount( int min, ValidationOpts cfg=null ) - Require that at least the given number of options / values are submitted for the one-to-many join.
  • MjoinMaxCount( in tmax, ValidationOpts cfg=null ) - Require that this many or less options / values are submitted for the one-to-many join.

Custom field validators

If the provided methods above don't suit the kind of validation you are looking for, it is absolutely possible to provide custom validation methods. The Validator() Field method will accept a delegate that returns a string and accepts the following input parameters:

  1. object - The value to be validated
  2. Dictionary<string, object> - The collection of data for the row in question
  3. ValidationHost - Information about the host Field and Editor instances

Note that the result value is the error message used. If null is returned, the validation is seamed to have passed (i.e. there are no validation errors).

The anonymous functions and lambda expressions available in newer versions of C# are excellent for defining validation methods quickly and easily. A custom validation method that simply ensures a string is of a certain size (5 characters in this case) might look like the following with a lambda expression:

new Field("StaffId")
    .Validator( (val, d, host) => Convert.ToString(val).Length < 5
        ? "Input must be 5 characters or more"
        : null
    )

Examples

Use the Validation.MinNum() method to validate an input as numeric and greater or equal to a given number (no validation options specified, so the defaults are used):

new Field("age")
    .Validator(Validation.MinNum( 16 ))

As above, but with ValidationOpts used to set the error message:

new Field("age")
    .Validator(Validation.MinNum( 16, new ValidationOpts{
        Message = "Minimum age is 16"
    } ))

This time validating an e-mail address which cannot be empty, and an error message is provided:

new Field("email")
    .Validator(Validation.Email( new ValidationOpts{
        Empty = false,
        Message = "An e-mail address is required"
    } ))

A join with dbValues which will accept an empty value, which is stored as null on the database:

new Field("users.site")
    .Options("sites", "id", "name")
    .Validator(Validation.DbValues())
    .SetFormatter(Format.NullEmpty());

Allow only certain values to be submitted:

new Field( "group" )
    .Validator(Validation.Values(null, new[] { "CSM", "FTA", "KFVC" }))

Global validators

You may also find it useful to be able to define a global validator that will execute whenever a request is made to the server and the Editor.Process() method is executed. This method can be used to provide security access restrictions, validate input data as a whole or even to check that a user is logged in before processing the request.

Function

The function that is given to the Editor.Validator() method has the following signature:

  1. Editor - The Editor instance that the function is being executed for
  2. DtRequest.RequestTypes - The action being performed
  3. DtRequest - The data submitted by the client.

The return value from the function is a string. If the validation fails the string should contain an error message that will be shown the end user. If the validation passes the return value should be an empty string.

Note that this function is executed only once when the Editor.Process() method is called, rather than once per submitted row.

Examples

// Allow read only access based on a session variable
new Editor(db, "table")
    .Model<TableModel>()
    .Validator( (editor, action, data) => {
        if ( action != DtRequest.RequestTypes.DataTablesGet && Session["ReadOnly"] ) {
            return "Cannot modify data";
        }
        return "";
    } )
    .Process(request)
    .Data();
// Create and edit with dependent validation
new Editor(db, "table")
    .Model<TableModel>()
    .Validator( (editor, action, data) => {
        if ( action == DtRequest.RequestTypes.EditorCreate || action == DtRequest.RequestTypes.EditorEdit ) {
            foreach (var pair in data.Data)
            {
                var values = pair.Value as Dictionary<string, object>;

                if ( values["country"].ToString() == "US" && values["location"].ToString() == "London UK" )
                {
                    return "London UK is not in the US";
                }
            }
        }
        return "";
    } )
    .Process(request)
    .Data();

Validation reference documentation

The Editor .NET reference documentation details the pre-built validation methods available and also the interfaces used by the libraries if you wish to develop your own.