ヘッダー
C# Sample Programs
 

Custom validation

04/14/2024

This article deals with ASP.NET Core Razor Pages.

 

All sample programs in this article are based on the Visual Studio Project template "ASP.NET Core Web App" and you must add the Razor pages in th Pages folder.

 

 

Validation by inline code

I'd like to introduce example that validate user input by server side C#. For example using if.

In this example, we want to make the quantity mandatory only if the product name is entered.

InlineValidation.cshtml.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace WorkStandard.Pages.validation
{
    public class InlineValidationModel : PageModel
    {
        [BindProperty]
        [DisplayName("Product Name")]
        public string? ProductName { get; set; }

        [BindProperty]
        [DisplayName("Quantity")]
        public int? Count { get; set; }

        public void OnPost()
        {
            //▼Custom validation
            //Using ModelState.AddModelError before ModelState.IsValid,
            //you would append custom error.

            //If the product name is entered, check the quantity is entered too.
            if (!string.IsNullOrEmpty(ProductName))
            {
                if (!Count.HasValue || Count <= 0)
                {
                    ModelState.AddModelError(nameof(Count), 
                    "The product name is given, so you must input the quantity.");
                }
            }

            if (!ModelState.IsValid)
            {
                return;
            }

            System.Diagnostics.Debug.WriteLine($"No errors. {ProductName} {Count}");
        }
    }
}

Where Debug.WriteLine appear

 

InlineValidation.cshtml

@page
@model WorkStandard.Pages.validation.InlineValidationModel

<form method="post">
    <div class="mb-3">
        <label asp-for="ProductName"></label>
        <input asp-for="ProductName" class="form-control" />
        <span asp-validation-for="ProductName" class="text-danger"></span>
    </div>
    <div class="mb-3">
        <label asp-for="Count"></label>
        <input asp-for="Count" class="form-control" />
        <span asp-validation-for="Count" class="text-danger"></span>
    </div>
    <div>
        <input type="submit" class="btn btn-primary" />
    </div>
</form>

The CSS classes...

  • mb-3 Set little margin at bottom.→ Spacing
  • form-control Make the input a Bootstrap look.→ Forms
  • text-danger The string become red to indicate danger.→ Theme colors
  • btn btn-primary Give the button a Bootstrap look to indicate that it's a key feature.→ Buttons

 

 

Custome validation attribute

Following example shows how to create custom validation attribute SJISByteLength to check string byte count by Shift JIS encoding. The Shift JIS is often used to encode Japanese. So this is a validation attribute, that you can attach to any properties to validate automatically same as Required attribute etc.

Note: In Shift JIS encoding, all ASCII characters are same character code with ASCII, so the are 1 byte. Almoast Japanese characters like "あ" or "日" are expressed 2 bytes.

CustomValidation.cshtml.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace WorkStandard.Pages.validation
{
    public class CustomValidationModel : PageModel
    {
        [BindProperty]
        [DisplayName("Product Name")]
        [SJISByteLength(10, ErrorMessage = "{0} requires to be within {1} bytes as Shift JIS encoding.")]
        public string? ProductName { get; set; }

        public void OnPost()
        {
            if (!ModelState.IsValid)
            {
                return;
            }

            System.Diagnostics.Debug.WriteLine($"No errors. {ProductName}");
        }
    }

    public class SJISByteLengthAttribute: ValidationAttribute
    {
        public int ByteLength { get; init; }

        public SJISByteLengthAttribute(int byteLength) { 
            if (byteLength <=0)
            {
                throw new ArgumentOutOfRangeException($"{nameof(byteLength)} can not be less than 0 or equal to 0.");
            }

            this.ByteLength = byteLength;
            this.ErrorMessage = "DEFAULT ERROR MESSAGE. The error message is generated at FormatErrorMessage method.";
        }

        public override bool IsValid(object? value)
        {
            // Leave this check to Required attribute.
            if (value == null)
            {
                return true;
            }

            string strValue = (string)value;

            //System.Text.Encoding.GetEncoding("shift_jis") enables using Shift JIS encoding.
            System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
            var sjis = System.Text.Encoding.GetEncoding("shift_jis");

            //▼Check all characters in the text are supported by SJIS.
            //When converting a Unicode string to SJIS, unsupported characters are automatically become ? by default.
            //Compareing strings before and after, we can detect there are unsupported characters or not. 
            //But note, this is a simplified way, you have to consider more if this check is critical for your business.

            //Convert Unicode string to SJIS string. In .NET specification, all "string"s are Unicode.
            string sjisString = sjis.GetString(sjis.GetBytes(strValue));

            if (strValue != sjisString)
            {
                return false;
            }

            //▼Validate byte count
            if (sjis.GetBytes(strValue).Length > ByteLength)
            {
                return false;
            }

            return true;
        }

        public override string FormatErrorMessage(string name)
        {
            return string.Format(System.Globalization.CultureInfo.CurrentCulture, 
                ErrorMessage ?? "", name, ByteLength); 
        }
    }
}

Where Debug.WriteLine appear

 

CustomValidation.cshtml

@page
@model WorkStandard.Pages.validation.CustomValidationModel

<form method="post">
    <div class="mb-3">
        <label asp-for="ProductName"></label>
        <input asp-for="ProductName" class="form-control" />
        <span asp-validation-for="ProductName" class="text-danger"></span>
    </div>
    <div>
        <input type="submit" class="btn btn-primary" />
    </div>
</form>

Note: For example "あいうえお" is just 10 bytes as SJIS, "あいうえおか" is 12 bytes, "abcdefghij" is 10 byte.

 

The CSS classes...

  • mb-3 Set little margin at bottom.→ Spacing
  • form-control Make the input a Bootstrap look.→ Forms
  • text-danger The string become red to indicate danger.→ Theme colors
  • btn btn-primary Give the button a Bootstrap look to indicate that it's a key feature.→ Buttons

日本語版