Remote Validation in Razor Pages

Remote Validation is a technique that uses client side script to validate user input on the server without posting the entire form. It is typically used to compare the user input with a dynamic list of values. One example of its use would be to prevent duplicate user names being submitted.

Remote validation requires three things:

  • The jQuery Unobtrusive Validation library
  • Application of the Remote or PageRemote attribute to the model property being validated
  • A remote resource (page handler method or MVC controller action) to perform the validation

Remote or PageRemote?

The RemoteValidation attribute has been around a long time in one form or another. It was introduced into MVC in the pre .NET Core days, and was the only way to perform remote validation in ASP.NET Core 1.x or 2.x. Being part of MVC, the RemoteValidation attribute depends on an MVC controller action to do its work. The PageRemoteValidation attribute was introduced in ASP.NET Core 3.0, and is designed specifically to work with a Razor Pages handler method.

So, if you are working with ASP.NET Core 2.x, or your validation end point is an MVC controller, you must use the RemoteValidation attribute. If you are working with ASP.NET Core 3.x or newer, AND your validation service is a Razor Pages handler method, you must use the PageRemoteValidation attribute.

Example

The following example features a Razor PageModel that only has one property, Email. This is bound to a text input in a form, which has Unobtrusive Validation and Unobtrusive AJAX enabled. The Unobtrusive Validation library is included as part of the standard Razor Pages project template, and included in a page via a partial. The Unobtrusive Ajax library needs to be installed, which you can do by editing the libman.json file in your project to include the following entry:

"libraries": [
    {
      "provider": "jsdelivr",
      "library": "[email protected]",
      "destination": "wwwroot/lib/jquery-ajax-unobtrusive/"
    }
  ]

The next block of code shows the PageModel:

public class RemoteValidationDemoModel : PageModel
{
    [BindProperty]
    public string Email { get; set; }
}

Followed by the form and the scripts section in the content page:


<form method="post">
    <input asp-for="Email" /> 
    <span asp-validation-for="Email"></span><br>
    <input type="submit"/>
</form>

@section scripts{ 
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <partial name="_ValidationScriptsPartial" />
    <script src="~/lib/jquery-ajax-unobtrusive/dist/jquery.unobtrusive-ajax.min.js"></script>
}

The form has its method set to post, which will result in a request verification token being included in the rendered HTML. It also includes a validation tag helper for outputting any validation messages relating to the Email property.

PageRemote Attribute Approach

The following handler method is declared in the same Razor page:

public JsonResult OnPostCheckEmail()
{
    var existingEmails = new[] { "[email protected]", "[email protected]", "[email protected]" };
    var valid = !existingEmails.Contains(Email);
    return new JsonResult(valid);
}

Remote validation attributes require that the validation end point returns a JSON response to indicate whether the submitted value is valid or not. Valid values are:

  • true
  • false
  • undefined
  • null
  • any other string.

The last four values indicate that validation has failed. If a string value other than the first four is returned, it is used as the error message.

This example returns true if the submitted email is not one that already exists.

The PageRemote attribute has the following properties:

Property Description
AdditionalFields A comma separated list of additional fields that should be included in the validation request
ErrorMessage The custom error message to be displayed in the event of validation failure
ErrorMessageResourceName The name of the resource where the error message is stored, if one is used
ErrorMessageResourceType The type of the resource used to store the error message
HttpMethod The HTTP verb to be used for the request (GET or POST). Default is GET
PageHandler The name of the handler method containing the validation, If omitted, the default handler for the HTTP verb will be used
PageName The name of the page. If omitted, the current page is used.

Taking into account that there is a named page handler method in the current page, the PageRemote attribute is applied to the Email property as shown below:

[PageRemote(
    ErrorMessage ="Email Address already exists", 
    AdditionalFields = "__RequestVerificationToken", 
    HttpMethod ="post",  
    PageHandler ="CheckEmail"
)]
[BindProperty]
public string Email { get; set; }

The AdditionalFields property must include the request verification token because request verification takes place by default in Razor Pages. If this field is not included in a POST request, the request will fail with a 400 Bad Request status code.

Once this configuration has been completed, the AJAX request is made when the value of the Email input is changed:

Remote Validation in Razor Pages

Remote Attribute Approach

The following action method is declared in an MVC controller:

public class ValidationController : Controller
{
    [HttpPost]
    [ValidateAntiForgeryToken]
    public JsonResult CheckEmail(string Email)
    {
        var existingEmails = new[] { "[email protected]", "[email protected]", "[email protected]" };
        var valid = !existingEmails.Contains(Email);
        return new JsonResult(valid);
    }
}

The body of the method and the return type is identical to the Razor Pages handler method example. The ValidateAntiForgeryToken attribute is optional in MVC, but recommended.

The Remote attribute shares the following properties with the PageRemote attribute:

  • AdditionalFields
  • ErrorMessage
  • ErrorMessageResourceName
  • ErrorMessageResourceType
  • HttpMethod

It has three overloads. The first takes the name of a route, the second takes the name of an action method and controller, and the third takes an area name in addition to the action and controller names.

Using the second overload, this is how the Remote attribute is configured on the Email property based on the controller action above:

[Remote(
    "checkemail",
    "validation",
    ErrorMessage ="Email Address already exists", 
    AdditionalFields = "__RequestVerificationToken", 
    HttpMethod ="post"
)]
[BindProperty]
public string Email { get; set; }

In either case, Remote or PageRemote, if no error message is provided either via the ErrorMessage or ErrorMessageResourceName properties, or in the JSON response, the default error message is "'name of property' is invalid"

Using Resource Files For Error Messages

Resource (.resx) files enable you to centralise data, making it easier to maintain. Resource files are particularly useful when used as part of a localisation strategy. Both the PageRemote and the Remote attributes provide support for error messages stored in resource files.

When adding a resource file, you must change the access modifier (highlighted below) to public. Otherwise the resource will not be accessible to the Razor Pages application.

Resource Files in Razor Pages

This example shows a file named ErrorMessages.resx, which has been added to a folder named Resources. It has one entry with a key of "DuplicateEmail". The name of the folder becomes the namespace for the resource, so when you configure the PageRemote attribute, you can either include the namespace as part of the type:

[PageRemote(
    ErrorMessageResourceType = typeof(Resources.ErrorMessages),
    ErrorMessageResourceName = nameof(Resources.ErrorMessages.DuplicateEmail), 
    AdditionalFields = "__RequestVerificationToken", 
    HttpMethod ="post",  
    PageHandler ="CheckEmail"
)]
[BindProperty]
public string Email { get; set; }
...

Or you can add a using directive to reference the namespace and omit it from the attribute configuration:

using MyApp.Resources;
...
[PageRemote(
    ErrorMessageResourceType = typeof(ErrorMessages),
    ErrorMessageResourceName = nameof(ErrorMessages.DuplicateEmail), 
    AdditionalFields = "__RequestVerificationToken", 
    HttpMethod ="post",  
    PageHandler ="CheckEmail"
)]
[BindProperty]
public string Email { get; set; }
...
Last updated: 11/11/2019 09:16:43

© 2019 - Learn Razor Pages.
All rights reserved.