5

I'm really confused by this. I have a Razor Page on ASP.NET Core 2 that has a required property called SchemaId. I've tried marking it as [Required], [BindRequired], and [Required(AllowEmptyStrings = false)], yet when I post my form, I see that SchemaId is null and yet ModelState.IsValid == true. Here's the Upload.cshtml.cs:

namespace Uploader.Pages
{
    public class UploadModel : PageModel
    {
        private IUploader _uploader;

        public UploadModel(IUploader uploader)
        {
            _uploader = uploader;
        }

        [BindProperty]
        public IEnumerable<IFormFile> UploadedFiles { get; set; }

        [Required(AllowEmptyStrings = false)]
        [BindProperty]
        [BindRequired]
        public string SchemaId { get; set; }


        public void OnGet(string schemaId = null)
        {
            SchemaId = schemaId;
        }

        public async Task<IActionResult> OnPostAsync()
        {
            // SchemaId is NULL right here!
            if (!ModelState.IsValid) // Yet IsValid = true!
            {
                return Page();
            }

            // Use _uploader to actually upload the file

            return RedirectToPage("/Next", new { uploadId = uploadId, schemaId = SchemaId });
        }
    }
}

Relevant extract from Upload.cshtml file:

<div asp-validation-summary="All"></div>
<form enctype="multipart/form-data" method="POST" asp-page="Upload">
    <input class="inputfile" type="file" id="UploadedFiles" multiple="multiple" name="UploadedFiles">
    <label for="UploadedFiles">
        <span>Choose a file...</span>
    </label>
    <input type="hidden" asp-for="SchemaId">
    <button type="submit" class="inputfilesubmit">Upload</button>
</form>

How can I get the model validation to work properly?

Set
  • 47,577
  • 22
  • 132
  • 150
Seafish
  • 2,081
  • 2
  • 24
  • 41
  • ``. Where do you give this property a value? – Cristian Szpisjak Nov 05 '17 at 07:16
  • [Possible duplicate](https://stackoverflow.com/questions/39922133/why-bindnever-attribute-doesnt-work), this answer says `[FromBody] annotation "overrides" the BindBehaviourAttribute` – Mike Mat Nov 05 '17 at 07:25
  • 1
    are you using `AddMvcCore` or `AddMvc`? Cause if the first one then it should be `AddMvcCore().AddDataAnnotations()`, – Set Nov 05 '17 at 10:27
  • @CristianSzpisjak It originally gets populated from the query string when navigating to the page (in the OnGet method above), and then is bound to the hidden form field. If the user then posts with a null or empty SchemaId I need it to throw a validation/required error and re-render the page – Seafish Nov 05 '17 at 15:37
  • @MikeMat Really I don't think I should even need to mess with the `[BindRequired]` stuff at all - I would think that adding `[Required]` would be sufficient for the validator to check that the field is present. I just added `[BindRequired]` to see if it would help (it didn't). Plus, I'm not using `[FromBody]` here. – Seafish Nov 05 '17 at 15:39
  • @Set Good thought - I'm using `AddMvc()` though – Seafish Nov 05 '17 at 15:40
  • @Seafish My apologies, if you are not using [FromBody] attribute it should work. If you can show us the controller, I think that will be helpful. – Mike Mat Nov 05 '17 at 23:14
  • @MikeMat No problem, appreciate the help. The PageModel code I've provided is, effectively, the controller since it's using Razor pages (new with .NET core 2). – Seafish Nov 06 '17 at 01:32
  • @Set What's the purpose for removing the razor-pages tag? That's exactly the technology that's being used and my suspicion is that this issue probably relates strictly to razor-pages, not to regular mvc controllers – Seafish Nov 06 '17 at 01:41
  • Part of the view you showed does not contain the input in which the `SchemaId` gets set the value. User will need to select that... right? Or is just posted first time from the controller on the get method and you need it back on the POST? – Cristian Szpisjak Nov 06 '17 at 04:32
  • @CristianSzpisjak Yeah, that's right - when the user visits the page (GET), it will typically have a query string with `schemaId` in it. The Razor page OnGet fills the SchemaId from the GET parameter and then binds it to the hidden form field, which is then POSTed back to the controller when the form is submitted. – Seafish Nov 06 '17 at 05:57

1 Answers1

1

I think I found out the reason.

[BindRequired] and [Required] attributes defined in PageModel are seemed to be ignored. Therefore you need to validate the model something like the following:

public class Schema
{
    [Required]
    [BindRequired]
    public string Id { get; set; }
}
public class TestModel : PageModel
{
    [BindProperty]
    public Schema Schema { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        // SchemaId is NULL right here!
        if (!ModelState.IsValid) // Yet IsValid = true!
        {
            return Page();
        }

        return Page();
    }

}

Then in cshtml

<div asp-validation-summary="All"></div>
<form enctype="multipart/form-data" method="POST" asp-page="Upload">
    <input class="inputfile" type="file" id="UploadedFiles" multiple="multiple" name="UploadedFiles">
    <label for="UploadedFiles">
        <span>Choose a file...</span>
    </label>
    <input type="hidden" asp-for="Schema.Id">
    <button type="submit" class="inputfilesubmit">Upload</button>
</form>

I still haven't found any documentation mentioning this, but in Microsoft documentation they also write validation attributes in a separate class, so I think this behavior is expected.

Hope this will solve the problem.

Noctis
  • 11,507
  • 3
  • 43
  • 82
Mike Mat
  • 632
  • 7
  • 15
  • 1
    This is correct and your workaround is great. The issue has been raised several times (e.g. [aspnet/Razor#1688](https://github.com/aspnet/Razor/issues/1668), [aspnet/Mvc#6312](https://github.com/aspnet/Mvc/issues/6312), [aspnet/Mvc#6790](https://github.com/aspnet/Mvc/issues/6790), [aspnet/Mvc#6891](https://github.com/aspnet/Mvc/issues/6891), [aspnet/Mvc#6927](https://github.com/aspnet/Mvc/issues/6927)). It was fixed in [aspnet/Mvc@236ef5d](https://github.com/aspnet/Mvc/commit/236ef5d) which should be included in ASP.NET Core 2.1. – Kevinoid Feb 01 '18 at 12:40
  • Still not fixed in Core 2.1.500 – Vilmir Nov 30 '18 at 19:36