Ответ 1
Вы можете использовать атрибут Bind для этого: http://ittecture.wordpress.com/2009/05/01/tip-of-the-day-199-asp-net-mvc-defining-model-binding-explicitly/
Лучшим вариантом будет использование ViewModels.
У меня есть мастер регистрации для регистрации нового пользователя. Когда я пытаюсь перейти на вторую страницу, я получаю ошибки проверки, потому что мой объект User
еще не был полностью заполнен. Можно ли каким-либо образом указать каждому ActionMethod
игнорировать некоторые свойства при проверке проверки ModelState.IsValid
?
например. (Упрощенный код pseduo)
public class User
{
[Required]
public string Name; // Asked on page 1.
[Required]
public int Age; // Asked on page 1.
[Required]
public string Avatar; // Asked on Page 2.
}
он жалуется на то, что Аватар требуется/не может быть нулевым. Но у меня нет возможности попросить пользователя заполнить эту информацию до следующей страницы.
Можно ли просить игнорировать эту проверку на стр. 1?
Вы можете использовать атрибут Bind для этого: http://ittecture.wordpress.com/2009/05/01/tip-of-the-day-199-asp-net-mvc-defining-model-binding-explicitly/
Лучшим вариантом будет использование ViewModels.
В действии просто удалите ошибки для элементов, еще не проверенных. Это делает вашу модель действительной для уже проверенных элементов.
foreach (var error in ModelState["Avatar"].Errors)
{
ModelState["Avatar"].Errors.Remove(error);
}
или
ModelState["Avatar"].Errors.Clear();
Это обсуждается в книге Стив Сандерсон asp.net mvc 2, стр. 486.
Создайте настраиваемый атрибут ValidateIncomingValuesOnlyAttribute, который наследует ActionFilterAttribute и применит его к вашему классу контроллера.
Отменить метод OnActionExecuting:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var modelState = filterContext.Controller.ViewData.ModelState;
var incomingValues = filterContext.Controller.ValueProvider;
var keys = modelState.Keys.Where(x => !incomingValues.ContainsPrefix(x));
foreach(var key in keys)
{
modelState[key].Errors.Clear();
}
}
Таким образом, вы проверяете данные, относящиеся только к каждому шагу мастера. Затем вам нужна страница подтверждения без ввода данных для отправки проверенных данных на сервер.
Но прежде всего, прочитайте книгу Стива Сандерсона, он дает рабочее решение этой и вашей другой проблеме.
Если вместо вышесказанного вы решите отобразить на viewmodel, будьте осторожны, потому что вам либо придется:
а. Не украшайте свойства viewmodel атрибутами атрибутов data validation, в этом случае вы проверяете только после того, как пользователь заполнил весь мастер и попытайтесь отправить в базу данных. Это будет очень неприятно с точки зрения пользователя...
б. Кроме того, вам все равно придется использовать технику, описанную S Sanderson, т.е. Очистить все ошибки проверки, которые не относятся к полям текущего шага.
Я не вижу принятого ответа, отвечая на вопрос, как его спрашивали.
Чтобы игнорировать свойства ModelState, вот простейший код.
if (ModelState["PropertyName"] != null) ModelState["PropertyName"].Errors.Clear();
Я просто возился с формами проверки и ModelState
и обнаружил очень простое решение вашей проблемы без написания нового метода, переопределения и т.д.
ModelState.Where(m => m.Key == "Avatar").FirstOrDefault().Value.Errors.Clear();
// At this point ModeState will have an error for that Key,
// by applying Clear it remove the error so modelstate becomes valid again
if (!ModelState.IsValid) {
return View("User", model);
} else {
try {
// do something
} catch {
TempData["errorMessage"] = "something went wrong";
}
}
Как насчет пользовательского класса IgnoreModelErrors?
http://mrbigglesworth79.blogspot.in/2011/12/partial-validation-with-data.html
Наследовать от класса ActionFilterAttribute и очищать ошибки [на основе совпадений имен или шаблонов регулярных выражений] в OnActionExecuting, как показано в приведенной выше ссылке. Это будет более чистым.
ViewModels, которые точно соответствуют отправляемым данным, обычно рекомендуются, потому что они очень предсказуемы, и вы получаете все преимущества сильной печати, строительных лесов и т.д. С другой стороны, использование BindAttribute может потребовать от вас свойства, которые не отправляются обратно в учетную запись, и могут привести к бесшумному сбою во время выполнения при изменении имени свойства, но строки BindAttribute Include или Exclude не являются. Избегание использования атрибутов проверки имеет множество недостатков в MVC и должно быть заменено некоторыми другими методами проверки, такими как IValidatableObject или FluentValidation.
Несмотря на все преимущества ViewModels и предостережения, которые сопровождают BindAttribute, иногда может быть предпочтительнее использовать BindAttribute и частично публиковать в модели/режиме просмотра. Этот ActionFilterAttribute охватывает этот точный случай. Он принимает код @awrigley, процитированный шаг дальше, но вместо устранения ошибок на основе ValueProvider он очищает ошибки на основе использования BindAttribute (например, Include и Exclude). Этот атрибут можно безопасно добавить в GlobalFilterCollection, потому что он не изменит поведение проверки MVC, если BindAttribute не был применен. Обратите внимание: я не очень использовал это, но он хорошо подходит для моих основных случаев.
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Mvc;
/// <summary>
/// When the BindAttribute is in use, validation errors only show for values that
/// are included or not excluded.
/// </summary>
public class ValidateBindableValuesOnlyAttributes : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var modelState = filterContext.Controller.ViewData.ModelState;
var includedProperties = filterContext.ActionDescriptor.GetParameters()
.SelectMany(o => o.BindingInfo.Include.Select(name => (string.IsNullOrWhiteSpace(o.BindingInfo.Prefix) ? "" : o.BindingInfo.Prefix + ".") + name));
var excludedProperties = filterContext.ActionDescriptor.GetParameters()
.SelectMany(o => o.BindingInfo.Exclude.Select(name => (string.IsNullOrWhiteSpace(o.BindingInfo.Prefix) ? "" : o.BindingInfo.Prefix + ".") + name));
var ignoreTheseProperties = new List<KeyValuePair<string, ModelState>>();
if (includedProperties.Any())
{
ignoreTheseProperties.AddRange(modelState.Where(k => !includedProperties.Any(name => Regex.IsMatch(k.Key, "^" + Regex.Escape(name) + @"(\.|\[|$)"))));
}
ignoreTheseProperties.AddRange(modelState.Where(k => excludedProperties.Any(name => Regex.IsMatch(k.Key, "^" + Regex.Escape(name) + @"(\.|\[|$)"))));
foreach (var item in ignoreTheseProperties)
{
item.Value.Errors.Clear();
}
}
}
У меня был ссылочный объект, который не должен был быть проверен.
Удалено из проверки в начале действия:
[HttpPost]
public async Task<IActionResult> Post([FromBody] Contact contact)
{
var skipped = ModelState.Keys.Where(key => key.StartsWith(nameof(Contact.Portfolios)));
foreach (var key in skipped)
ModelState.Remove(key);
//ModelState doesn't include anything about Portfolios which we're not concerned with
if (!ModelState.IsValid)
return BadRequest(ModelState);
//Rest of action
}