Improved 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. Remote validation has always been possible in Razor Pages using either the RemoteAttribute, which requires an MVC controller to work, or by writing custom client-side script. One almost completely overlooked feature that was included in ASP.NET Core 3.0 was the introduction of a Razor Pages-specific validation attribute that removes this reliance on MVC controllers or custom code for remote validation.

Remote validation in ASP.NET (Core) relies on Unobtrusive AJAX, so you will need to install that first. The easiest way to do this is via LibMan. Right click on the lib folder in wwwroot, choose Add » Client-side Library, and then choose jsdelivr as the source, and type in jquery-ajax-unobtrusive, You should see it appear in the list of packages available:

Jquery Unobtrusive AJAX

A common use case for remote validation is to check whether a user name or email address already exists in the database, if your business rules do not allow duplicates. To better focus on applying remote validation, this demo features a form with just the one input. Here is an example PageModel class that includes a property named Email:

public class RemoteValidationTestModel : PageModel
{
    [BindProperty]
    public string Email { get; set; }
    public void OnGet()
    {
 
    }
}

And here is a form that includes an input for the Email property, and a validation tag helper.

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

As I mentioned before, remote validation relies on unobtrusive AJAX, so you need to reference jQuery, Unobtrusive AJAX and the Unobtrusive Validation library in the same page as the form. How you do that depends on where else in the application you might need these libraries, but the code below illustrates how to include all of them in a single page within a @section block:

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

Now you need to add a handler method to perform the validation. The handler method itself must provide a JSON response indicating whether validation succeeded or failed (true or false). The following example uses a named handler which is added to the same PageModel as before:

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

In the real world, this handler would compare the submitted email address to database records, but in demo land, it checks to see if the email is one of three hard coded values, returning true if not (i.e. the email is a valid value) and false if it is a duplicate.

The final step is to apply the new remote validation attribute, which is called a PageRemoteAttribute. It shares many of the same properties as the MVC RemoteAttribute:

PropertyDescription
AdditionalFieldsA comma separated list of additional fields that should be included in the validation request
ErrorMessageThe error message to be displayed in the event of validation failure
ErrorMessageResourceNameThe name of the Resource where the error message is stored, if one is used
ErrorMessageResourceTypeThe type of the resource used to store the error message
HttpMethodThe HTTP verb to be used for the request (GET or POST). Default is GET

The PageRemote attribute also includes a couple of other properties: PageName and PageHandler. The PageName is the name of the page that the request should be sent to, and the PageHandler is the name of the handler method that should be invoked. In both cases, if they are omitted, "ambient values" will be used i.e. the current page, and whichever conventionally named handler method that responds to the HTTP verb that is used.

The PageRemote attribute is applied to the property that needs validating. In this example, the configuration of the PageRemote attribute is as follows:

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

The handler method that performs validation is set up to respond to POST requests. It is in the same page as the property to be validated so the PageName is omitted. The AdditionalFields property is set to include the hidden request verification token generated by the form tag helper. If you don't include this field for POST requests, the request will fail with a 400 Bad Request status code.

And that's all there is to it. If you run the page and enter one of the hardcoded email addresses, the change event fires the AJAX request and validates the value:

Remote Validation in Razor Pages

Remote Validation in Razor Pages

Summary

The PageRemote attribute was introduced in ASP.NET Core 3.0 to no fanfare whatsoever (until now). It's a nice addition, in that it removes the need for controllers in your Razor Pages application for performing remote validation simply and effectively.