RazorPages Page Remote not working on model

1.6k Views Asked by At

as per https://www.mikesdotnetting.com/article/343/improved-remote-validation-in-razor-pages I followed the tutorial and implemented the PageRemote. However it does not work if applied to a property of a model and I use the model as property.

public class Draft
{
    public int Id { get; set; }
    [PageRemote(ErrorMessage = "Invalid data", AdditionalFields = "__RequestVerificationToken", HttpMethod = "post", PageHandler = "CheckReference")]
    public string Reference { get; set; }

}

[BindProperty]
public Draft Draft { get; set; }

public JsonResult OnPostCheckReference()
{            
    var valid = !Draft.Reference.Contains("12345");
    return new JsonResult(valid);
}

on my page

<tab>
    <tab-item icon="fas fa-arrow-left" url="@Url.Page("../Index")"></tab-item>
    <tab-item icon="fas fa-list" url="@Url.Page("Index")"></tab-item>
    <tab-item icon="fas fa-plus" is-active="true"></tab-item>
</tab>
<form method="post">
    <card>
        <card-header icon="fas fa-plus" title="Draft"></card-header>
        <card-body>

            <input asp-for="Draft.Reference" />
            <span asp-validation-for="Draft.Reference" class="text-danger"></span>

        </card-body>
        <card-footer>
            <button class="btn btn-success"><i class="fas fa-plus"></i> Adicionar </button>
        </card-footer>
    </card>
</form>
@section Scripts{

    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
    <script src="~/lib/jquery-ajax-unobtrusive/dist/jquery.unobtrusive-ajax.min.js"></script>

}
3

There are 3 best solutions below

1
Mike Brind On BEST ANSWER

Remote validation on nested model properties is not straightforward. The framework prefixes all additional fields with the name of the model, so request verification fails, resulting in a 400 error.

The work around is to separate the field that you want to validate remotely from the sub-model, and make it a first class property of the PageModel instead. Then, if ModelState is valid, assign the value to the nested model.

public class Draft
{
    public int Id { get; set; }

    public string Reference { get; set; }

}

[BindProperty]
public Draft Draft { get; set; }

[BindProperty, PageRemote(ErrorMessage = "Invalid data", AdditionalFields = "__RequestVerificationToken", HttpMethod = "post", PageHandler = "CheckReference")]
public string Reference {get;set;}

public JsonResult OnPostCheckReference()
{            
    var valid = !Reference.Contains("12345");
    return new JsonResult(valid);
}

Then in the form:

<input asp-for="Reference" />
<span asp-validation-for="Reference" class="text-danger"></span>
3
Daniel Leach On

Remote validation on nested model properties doesn't allow you to specify additional fields on a parent object. The __RequestVerificationToken is always on the root of the model. The source for jquery.validate.unobtrusive.js is looking for fields prefixed with *. and prefixes the model name to them. The asp-for tag helper is adding *. to the beginning of the fields.

You can circumvent this prefixing of *. by manually specifying the attribute in html and removing AdditionalFields from the attribute.

PageRemoteAttribute:

public class Draft
{
    public int Id { get; set; }
    [PageRemote(ErrorMessage = "Invalid data", HttpMethod = "post", PageHandler = "CheckReference")]
    public string Reference { get; set; }

}

Html:

<input asp-for="Reference" data-val-remote-additionalfields="__RequestVerificationToken" />

0
Sam On

The solution above where you specify the data-val-remote-additionalfields attribute directly on the input appears to no longer work for .Net 7. The tag builders seem to be overwriting the value and putting *. in front. This happens whether additional fields are specified in the data annotation or not.

Since I couldn't figure out a way to control the output via annotations and attributes, I wrote a simple script to find all inputs with the data-val-remote-additionalfields attribute and strips out the prefix for the request verification token.

We still use jquery because the FluentValidation clientside adapters rely on it. This script uses the $(function(){}) short-hand to run when the DOM is ready. You can easily replace it with a DOMContentLoaded event listener. The rest is Vanilla JS.

$(function () {
    var remoteValidators = document.querySelectorAll('[data-val-remote-additionalfields]');
    if (remoteValidators) {
        remoteValidators.forEach(val => {
            var value = val.getAttribute('data-val-remote-additionalfields');
            value = value.replace('*.__RequestVerificationToken', '__RequestVerificationToken');
            val.setAttribute('data-val-remote-additionalfields', value);
        });
    }
});

NOTE: This script needs to be placed after jQuery, but before the validation libraries.