Meisam Dehghan
Meisam Dehghan

Reputation: 188

Remote Validation in Asp.Net Core Razor Pages

I’m developing a web application using Razor Pages and Code First.

I know that in ASP.NET MVC, you can use Remote above a property referring to an action in a controller that validates the data without the whole page being posted back. But it doesn’t seem to work in Razor Pages as there’s no Controller and Action in ASP.NET Core Razor Pages.

So, How can I get remote validation done in Razor Pages?

Upvotes: 4

Views: 3691

Answers (5)

Majid Shahabfar
Majid Shahabfar

Reputation: 4829

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 endpoint 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.

Here is an example describing this in details:

Upvotes: 3

Nekko Rivera
Nekko Rivera

Reputation: 61

For anyone like me who finds this later and loses their mind trying to pass a property from their model onto the validation method, make the method signature look like so

public IActionResult IsCharacterNameAvailable([Bind(Prefix = "Character.Name")] string name)

Character is the model and Name is the property. Without adding the [Bind(Prefix = "")] before the parameter I was always receiving a null value. Hope this helps!

Upvotes: 6

AndyB
AndyB

Reputation: 101

In the base class that RemoteAttribute derives from there's a protected GetUrl() method than can be overriden. Therefore I created my own MyRemoteAttribute class

public class MyRemoteAttribute : RemoteAttribute
{
    /// <summary>
    /// Initialise an instance of the <see cref="MyRemoteAttribute"/>
    /// </summary>
    /// <param name="handler">The name of the Razor Page Handler</param>
    /// <param name="page">The Razor Page name</param>
    public MyRemoteAttribute(string handler = null, string page = null)
    {
        Handler = handler;
        Page = page;
    }

    /// <summary>
    /// Gets/sets the url to use for remote validation
    /// </summary>
    public string Url { get; set; }
    public string Page { get; private set; }
    public string Handler { get; private set; }

    protected override string GetUrl(ClientModelValidationContext context)
    {
        // Use an URL is specified
        if (!string.IsNullOrEmpty(Url)) return Url;

        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }
        if (string.IsNullOrEmpty(Handler))
        {
            throw new InvalidOperationException("No Handler specified");
        }

        var services = context.ActionContext.HttpContext.RequestServices;
        var factory = services.GetRequiredService<Microsoft.AspNetCore.Mvc.Routing.IUrlHelperFactory>();
        var urlHelper = factory.GetUrlHelper(context.ActionContext);

        var page = Page?? context.ActionContext.RouteData.Values["page"] as string;

        Url = urlHelper.Page(page, Handler);
        if (Url == null)
        {
            throw new InvalidOperationException();
        }

        return Url;
    }
}

In my app which is using areas, creates a url /BusinessPartners/Clients/Create?handler=CheckUnique

To use decorate your model property with [MyRemote(Url="/Something/Somecheck")] to use the specified URL, or [MyRemote("CheckUnique")] to specify the Razor Page Handler. The handler should be named OnGet<handlername> and needs to return a JsonResult with true for passing validation, false or null if validation fails.

The handler in the Razor Page is:

public IActionResult OnGetCheckUnique(string shortName)
{
    var found = db.Queryable<BusinessPartner>().Any(a => a.ShortName == shortName);
    return new JsonResult(!found);
}

This is the same as you would do for the RemoteAttribute with the exception on the slightly modified naming convention.

I like my validation to be close to the point where it is used so therefore I've put it in the same page. I also have use a single [BindProperty] for a model class just to keep things neat and manageable.

Upvotes: 3

Meisam Dehghan
Meisam Dehghan

Reputation: 188

I added the following in my model class:

 [Remote(action: "IsNationalIdValid",controller:"Validations")]

I created 'Controllers' folder in my Razor Pages project and added a controller(ValidationsController) with the following method:

        public IActionResult IsNationalIdValid(string nationalId){}

However,when I tried to go to the page where this validation was supposed to work,I got the following exception:

No URL for remote validation could be found in asp.net core

Thanks to a reply to the same thread in Asp.Net forum,I figured out the answer: All I needed to do was to add the following code in Startup.cs file of my Razor Pages project in order to configure the route.

app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });

Hope this answer will help someone else as well.

Upvotes: 4

Brian Ogden
Brian Ogden

Reputation: 19212

Looks like there is a feature request for remote validation in ASP.NET Core Razor Pages but it is not priority:

https://github.com/aspnet/Mvc/issues/8245

Upvotes: 2

Related Questions