Reputation: 1337
I'm writing an ASP.NET Core web app. I'm using MVC's model validation framework, and I have a "form" model that contains a Product
object and a IsCreationMode
boolean.
I want to validate the Product
, but I also want to make sure that, if I'm creating the product, no product already exists with that SKU. So I've defined a custom validation attribute that returns a validation error if IsCreationMode
is true and the product SKU can be found in the database. I use this attribute on the model class itself, and it works.
[ProductIsUnique]
public class ProductForm
{
public Product Product { get; set; }
public bool IsCreationMode { get; set; }
}
However, if the product object is invalid, then the model object is not validated (by that I mean it is always considered "valid"). (I haven't tested if it is true that any object, such that not all of its members are valid, is also not validated.)
This means that the user may see an error about a certain property of the product (say, "Name too long"), then fix that, submit and get another error, because the whole model was not validated the first time, but would have been found invalid if it had been checked.
Is there a configuration setting for this? It doesn't seem to me that "validation anyway" is an undesirable or unrecommended option...
EDIT
Here comes a minimal example.
Create a new ASP.NET Core MVC project:
dotnet new mvc
Put this in the HomeController.cs file:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Stack.Models;
using System.ComponentModel.DataAnnotations;
namespace Stack.Controllers
{
public class HomeController : Controller
{
[HttpGet]
public IActionResult Index()
{
ProductForm model = new ProductForm()
{
IsCreationMode = true
};
return View(model);
}
[HttpPost]
public IActionResult Index(ProductForm model)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
return Ok();
}
}
public class Product
{
public int ID { get; set; }
[StringLength(50, MinimumLength = 3)]
public string Name { get; set; }
}
[ProductIsUnique]
public class ProductForm
{
public Product Product { get; set; }
public bool IsCreationMode { get; set; }
}
public class ProductIsUniqueAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ProductForm productForm = validationContext.ObjectInstance as ProductForm;
if (productForm.IsCreationMode && productForm.Product.ID == 10)
{
return new ValidationResult(string.Format("A Product with ID '{0}' already exists. You cannot create another one.", productForm.Product.ID), new string[]
{
"Product.ID"
});
}
return ValidationResult.Success;
}
}
}
Put this in Views/Home/Index.cshtml:
@model Stack.Controllers.ProductForm
<form method="post">
<p>ID: @Html.TextBoxFor(m => m.Product.ID)</p>
<p>Name: @Html.TextBoxFor(m => m.Product.Name)</p>
@Html.HiddenFor(m => m.IsCreationMode)
<input type="submit" value="Submit" />
</form>
Build and run. Now if you visit /
you will receive a form with two fields.
1) Type anything but "10" in the ID, and a string between 3 and 50 characters in the Name. The server responds 200 OK as expected.
2) Type "10" in the ID, and a string between 3 and 50 characters in the Name. The server responds 400 BadRequest, with a JSON describing one single error, i.e., the failure from ProductIsUnique
, as expected.
3) Type "10" and a string shorter than 3 characters. I expect two errors: the invalid Name AND the failure from ProductIsUnique
. Instead, I only get the invalid Name.
Upvotes: 0
Views: 491