ModelState.IsValid или Model.IsValid?
Я пишу контроллер и модульные тесты для него, когда я сталкивался с двумя способами (в равной степени действительными, я думаю), чтобы что-то сделать. Все мои модели имеют свойство IsValid, которое я могу проверить, чтобы спросить модель, действительно ли она действительна или нет.
При обратной передаче методу действия контроллера, если модель действительна, я хочу сохранить, в противном случае я хочу повторно отобразить форму, чтобы пользователь мог исправить свои ошибки.
Моя первоначальная мысль состояла в том, чтобы просто проверить, запрашивается ли модель, действительно ли она действительна, но я понял, что могу также проверить ModelState.IsValid.
Есть ли у кого-нибудь конкретная причина взглянуть на один против другого?
Ответы
Ответ 1
Я думаю, что настраиваемая проверка бизнеса, встроенная в вашу модель, - прекрасный подход. Способ, которым я бы справился с этим, заключался бы в добавлении каких-либо пользовательских ошибок проверки в ModelState:
if (ModelState.IsValid)
{
if (!model.IsValid)
{
ModelState.AddModelError("The model is not valid");
}
else
{
return RedirectToAction("Index");
}
}
return View(model);
Таким образом, ваше представление имеет доступ к ошибкам проверки независимо от того, являются ли они настраиваемыми или встроенными.
Ответ 2
ModelState
можно передать в TempData
, чтобы следовать Post-Redirect-Get. Пример:
[HttpPost]
[ExportModelStateToTempData]
public ActionResult Delete(int id)
{
if (_service.DeleteTask(id))
return RedirectToAction(ControllerActions.Index);
return RedirectToAction(ControllerActions.Edit, new { id });
}
[ImportModelStateFromTempData]
public ActionResult Edit(int id)
{
var task = _service.GetTask(id);
return View(ControllerActions.Edit, GetEditModel(task));
}
Пользователь может удалить задачу с помощью действия callig/Task/Delete, но если что-то пойдет не так, и появится сообщение об ошибке, нажатие F5 больше не будет вызывать удаление. когда ModelState
после Delete
переносится на Edit
, все ошибки отображаются на странице редактирования.
Это код для импорта/экспорта атрибутов ModelState
:
public abstract class ModelStateTempDataTransferAttribute : ActionFilterAttribute
{
protected static readonly string Key = typeof(ModelStateTempDataTransferAttribute).FullName;
}
public class ExportModelStateToTempDataAttribute : ModelStateTempDataTransferAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//Only export when ModelState is not valid
if (!filterContext.Controller.ViewData.ModelState.IsValid)
{
//Export if we are redirecting
if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult))
{
filterContext.Controller.TempData[Key] = filterContext.Controller.ViewData.ModelState;
}
}
base.OnActionExecuted(filterContext);
}
}
public class ImportModelStateFromTempDataAttribute : ModelStateTempDataTransferAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
ModelStateDictionary modelState = filterContext.Controller.TempData[Key] as ModelStateDictionary;
if (modelState != null)
{
//Only Import if we are viewing
if (filterContext.Result is ViewResult)
{
filterContext.Controller.ViewData.ModelState.Merge(modelState);
}
else
{
//Otherwise remove it.
filterContext.Controller.TempData.Remove(Key);
}
}
base.OnActionExecuted(filterContext);
}
}
То же самое с Model
было бы очень проблематичным.
Ответ 3
Вы также можете перечислить коллекцию ошибок и записать сообщение Exact error на выход.
else if (!ModelState.IsValid)
{
List<ModelError> modelErrors = new List<ModelError>();
ModelErrorCollection errors = new ModelErrorCollection();
;
// got to the model and look at the Array of Values
foreach (var item in ModelState)
{
if (item.Value.Errors.Count != 0)
{
// get the error
errors = item.Value.Errors;
modelErrors.Add(errors.Single());
}
}
if (modelErrors.Count != 0)
{
foreach (var error in modelErrors)
{
Debug.WriteLine(error.ErrorMessage);
}
}
...